skillscript-runtime v0.25.3
Upgrade impact: none (additive) for the documented usage — a {{secret.NAME}} marker written literally in a skill's shell/$ op still resolves exactly as before. Behavior change for an undocumented edge: a marker that is not in the skill source — one assembled at runtime from ${VAR} data or $set/$append — no longer resolves (it stays inert literal text). That path was never intended and was a leak; see below.
- Closes data-borne secret injection (adopter finding). Resolution used to run on the post-substitution sink string, so a
{{secret.NAME}}marker that arrived via runtime data (a${VAR}value containing the literal text) got resolved — e.g. an email body containing{{secret.AGENTMAIL_KEY}}would inject the real key into the outbound message. The runtime now resolves author-template-only: it masks markers written literally in the op template before${VAR}substitution, then splices the values in last. A marker arriving through data is never masked, so it can never resolve — it passes through as inert literal text. (This also refines the var-smuggle edge: a marker built via$set/$appendis data-borne and therefore inert, rather than resolving-if-declared.) - Sentinel forgery neutralized. The masking uses a NUL-delimited sentinel;
${VAR}substitution now strips NUL bytes from every substituted value, so untrusted data can't forge a sentinel to trigger a splice. (NUL is never valid in skill data —spawnrejects it — so the strip is loss-free.) - Unchanged guarantees: values never bind/emit/trace; the binary-allowlist gate runs before resolution; errors/traces render the marker, not the value; declare-before-spend (
# Requires) is enforced on author markers; the three tier-1 lint rules stand. Verified end-to-end acrossshell(argv=…),shell(command=…), and$ connector.toolsinks; the data-borne and forgery paths have regression tests.
Two lint improvements from the same dogfood (both adopter-found):
undeclared-varnow scansshell(argv=[...])elements. A${VAR}reference inside an argv element previously escaped the undeclared-var (and unknown-filter, legacy-$(...)-shape) checks — the lint's op-text collection skippedargv. Now an undeclared${VAR}in an argv element is caught at compile, like every other op position.- New tier-2
space-separated-vars.# Vars:is comma-separated; writing# Vars: A B Csilently collapsed into one malformed variable named"A B C"(the rest lost). The new warning flags any# Vars:entry whose name contains whitespace and suggests the comma form.