skillscript-runtime 0.25.2 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  Each release carries an **Upgrade impact:** line (first in its section) so a bump's requirements are visible at a glance. Tags (closed set): **BREAKING** (a manual change is needed to keep working) · **RE-APPROVE** (secured-mode signature invalidation — skills must be re-approved before they run) · **CONFIG** (`connectors.json` / config edit needed) · **none (additive)** (no action; backward-compatible). Standard from 0.20.0 forward; the pre-0.20 transitions that need action are flagged inline below (0.14.0, 0.18.8, 0.19.0). Full walkthrough: [UPGRADING.md](UPGRADING.md).
4
4
 
5
+ ## 0.26.0 — 2026-06-29 — file_read base64 encoding (inline binary attachments)
6
+
7
+ **Upgrade impact:** none (additive). New optional `encoding` kwarg on `file_read`; existing reads default to `utf8` exactly as before.
8
+
9
+ - **`file_read(path="…", encoding="base64")`** reads a file's **raw bytes** and base64-encodes them, so a binary file (image, PDF) can be inlined into an API payload — e.g. an `attachments:[{content: "<base64>", …}]` JSON body. This has to be a read-mode, not a pipe filter: the default `utf8` read decodes bytes to text (corrupting non-text bytes with U+FFFD) *before* any filter could run, so base64-ing a binary file after a text read would encode garbage. Reading the raw bytes at read time is the only correct path (adopter request 7130c3bd). Node's base64 emits a **single line** (no 76-column wrapping), which is what inline JSON wants — and needs no `base64` shell binary, so the shell allowlist stays untouched.
10
+ - `encoding` accepts `"utf8"` (default) or `"base64"`; anything else is refused at runtime and flagged tier-2 at compile by the new **`unknown-file-encoding`** lint rule (so a `"bas64"` typo is a lint line, not a failed run).
11
+ - **The filesystem path allowlist still gates every read**, base64 or not — a skill can only base64 a file under an allowlisted root, so this opens no new exfiltration path. (And base64'd file content is *data*, so a file containing literal `{{secret.X}}` text is inert under the v0.25.3 author-template-only secret model — no marker-injection through an attachment.)
12
+ - **Size note:** base64 inflates ~33%, and the encoded blob typically rides inline JSON into a downstream API. The practical wall isn't the runtime — it's the **target API's request-size limit** (e.g. a mail provider's max message size); an oversized attachment fails at that boundary, not in `file_read`.
13
+
14
+ ## 0.25.3 — 2026-06-28 — security: author-template-only secret resolution (closes data-borne injection)
15
+
16
+ **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.
17
+
18
+ - **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`/`$append` is data-borne and therefore inert, rather than resolving-if-declared.)
19
+ - **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 — `spawn` rejects it — so the strip is loss-free.)
20
+ - **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 across `shell(argv=…)`, `shell(command=…)`, and `$ connector.tool` sinks; the data-borne and forgery paths have regression tests.
21
+
22
+ Two lint improvements from the same dogfood (both adopter-found):
23
+
24
+ - **`undeclared-var` now scans `shell(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 skipped `argv`. Now an undeclared `${VAR}` in an argv element is caught at compile, like every other op position.
25
+ - **New tier-2 `space-separated-vars`.** `# Vars:` is comma-separated; writing `# Vars: A B C` silently 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.
26
+
5
27
  ## 0.25.2 — 2026-06-28 — fix: secret-use-only no longer false-positives on a documented marker
6
28
 
7
29
  **Upgrade impact:** none (additive). Removes a false positive introduced in 0.25.1; no change to valid skills beyond unblocking them.
@@ -1 +1 @@
1
- {"version":3,"file":"help-content.d.ts","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,k1CAYkF,CAAC;AA84B9H,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB"}
1
+ {"version":3,"file":"help-content.d.ts","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,k1CAYkF,CAAC;AAw5B9H,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB"}
@@ -63,7 +63,7 @@ The four delivery channels are all first-class:
63
63
  | Class | Shape | Examples |
64
64
  |---|---|---|
65
65
  | **Mutation statements** | \`$verb VAR = value\` / \`$verb VAR <value>\` | \`$set NAME = "Scott"\`, \`$append LIST <item>\` |
66
- | **Runtime-intrinsic function-calls** | \`verb(kwarg=value, ...) [-> BINDING]\` | \`emit(text="...")\`, \`inline(skill="...")\`, \`execute_skill(name="...") -> R\`, \`shell(command="...") -> R\` / \`shell(argv=[...]) -> R\`, \`file_read(path="...") -> R\`, \`file_write(path="...", content="...")\`, \`notify(agent="...")\` |
66
+ | **Runtime-intrinsic function-calls** | \`verb(kwarg=value, ...) [-> BINDING]\` | \`emit(text="...")\`, \`inline(skill="...")\`, \`execute_skill(name="...") -> R\`, \`shell(command="...") -> R\` / \`shell(argv=[...]) -> R\`, \`file_read(path="..." [, encoding="base64"]) -> R\`, \`file_write(path="...", content="...")\`, \`notify(agent="...")\` |
67
67
  | **External MCP dispatch** | \`$ <connector> kwarg=value, ... [-> BINDING]\` | \`$ youtrack_search query="..." -> R\`, \`$ llm prompt="..." -> R\`, \`$ data_read mode=fts query="..." -> R\` |
68
68
 
69
69
  The \`$\` prefix marks **state-affecting ops** (mutation OR external dispatch). Function-call shape marks **language-intrinsic ops the runtime knows directly**.
@@ -298,12 +298,20 @@ shell(command="echo hi && date +%Y", unsafe=true) -> OUT #
298
298
 
299
299
  **Fallback trailer:** all three forms (\`command=\`, \`argv=\`, \`unsafe=true\`) accept \`(fallback: "value")\` after the \`-> R\` binding. The fallback fires when (a) the shell op throws (binary not on allowlist, spawn failure, timeout), or (b) the binary runs cleanly but produces empty stdout after trim. Matches the \`$\`-dispatch op-trailer semantics. Example: \`shell(argv=["gh","pr","list"]) -> PRS (fallback: "No current PRs.")\` — when the repo has no open PRs and \`gh\` writes nothing to stdout, PRS binds to the fallback cleanly instead of leaving downstream \`\${PRS}\` references as UnresolvedVariableError. The \`|fallback:\` template filter has the same empty-aware semantic — both fire on empty-string-after-trim / empty-array / null/undefined.
300
300
 
301
- ### \`file_read(path="...") -> R\` — read file contents
301
+ ### \`file_read(path="...", encoding="...") -> R\` — read file contents
302
302
 
303
- Reads via Node \`fs.readFile\`. Substitutes \`\${VAR}\` in the path. Optional \`(fallback: "...")\` trailer binds when read fails. **Container note:** when the runtime is sandboxed (Docker, container deployment), the runtime's filesystem is namespace-isolated from the author's host — \`/tmp/x\` in the skill maps to the runtime's \`/tmp/x\`, not the host's. Use absolute paths under a known shared volume for cross-namespace work.
303
+ Reads via Node \`fs.readFile\`. Substitutes \`\${VAR}\` in the path. Optional \`(fallback: "...")\` trailer binds when read fails.
304
+
305
+ Optional \`encoding\` kwarg (default \`"utf8"\`):
306
+ - \`encoding="utf8"\` (default) — reads text; binds the file's contents as a string.
307
+ - \`encoding="base64"\` — reads the **raw bytes** and binds them base64-encoded (single line, no wrapping). Use this to inline a **binary** file (image, PDF) into an API payload — e.g. \`attachments:[{content: "\${B64}", ...}]\`. A plain utf8 read would corrupt non-text bytes before any \`|filter\` could run, so base64 must happen at read time, not via a pipe filter.
308
+ - Any other encoding value is refused at runtime and flagged \`unknown-file-encoding\` (tier-2) at compile.
309
+
310
+ The filesystem path allowlist gates every read regardless of encoding. **Container note:** when the runtime is sandboxed (Docker, container deployment), the runtime's filesystem is namespace-isolated from the author's host — \`/tmp/x\` in the skill maps to the runtime's \`/tmp/x\`, not the host's. Use absolute paths under a known shared volume for cross-namespace work.
304
311
 
305
312
  \`\`\`
306
313
  file_read(path="/var/reports/today.md") -> REPORT (fallback: "no report")
314
+ file_read(path="\${DOC}", encoding="base64") -> B64
307
315
  \`\`\`
308
316
 
309
317
  ### \`file_write(path="...", content="...", approved="...")\` — write file contents
@@ -909,6 +917,8 @@ Three tiers per ERD §3:
909
917
  ## Tier-2 (warning)
910
918
 
911
919
  - \`deprecated-question\` — bare \`?\` op (deprecated; compile-error path)
920
+ - \`space-separated-vars\` — a \`# Vars:\` entry name contains whitespace; \`# Vars:\` is comma-separated, so a space-separated list collapses into one malformed name
921
+ - \`unknown-file-encoding\` — \`file_read(..., encoding=...)\` names an encoding other than \`utf8\` / \`base64\`
912
922
  - \`deprecated-substitution-shape\` — \`$(VAR)\` substitution form compiles but warns; rewrite to \`\${VAR}\`.
913
923
  - \`unsafe-shell-ambiguous-subst\` — \`$(NAME)\` inside \`shell(command=..., unsafe=true)\` body that isn't a declared variable; collides with bash command-sub syntax
914
924
  - \`unsafe-shell-op\` — \`shell(command=..., unsafe=true)\` present; requires human review every time
@@ -1 +1 @@
1
- {"version":3,"file":"help-content.js","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"help-content.js","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
package/dist/lint.js CHANGED
@@ -1634,6 +1634,51 @@ const SECRET_DYNAMIC_NAME = {
1634
1634
  return findings;
1635
1635
  },
1636
1636
  };
1637
+ const UNKNOWN_FILE_ENCODING = {
1638
+ id: "unknown-file-encoding",
1639
+ severity: "warning",
1640
+ description: "A `file_read(..., encoding=...)` op names an encoding other than `utf8` or `base64`. Caught at compile so a typo (e.g. `bas64`) is a lint line, not a failed run.",
1641
+ remediation: 'Use `encoding="utf8"` (default, text) or `encoding="base64"` (raw bytes → base64, for binary files inlined into an API payload).',
1642
+ check: (ctx) => {
1643
+ const findings = [];
1644
+ for (const [targetName, target] of ctx.parsed.targets) {
1645
+ walkOps(target.ops, (op) => {
1646
+ if (op.kind !== "file_read")
1647
+ return;
1648
+ const enc = op.fileParams?.encoding;
1649
+ // Only flag a STATIC literal — a `${VAR}` encoding is resolved at runtime.
1650
+ if (enc === undefined || enc === "utf8" || enc === "base64" || /\$[({]/.test(enc))
1651
+ return;
1652
+ findings.push({
1653
+ rule: "unknown-file-encoding",
1654
+ severity: "warning",
1655
+ message: `file_read encoding '${enc}' is not recognized — use "utf8" or "base64".`,
1656
+ block: targetName,
1657
+ });
1658
+ });
1659
+ }
1660
+ return findings;
1661
+ },
1662
+ };
1663
+ const SPACE_SEPARATED_VARS = {
1664
+ id: "space-separated-vars",
1665
+ severity: "warning",
1666
+ description: "A `# Vars:` entry name contains whitespace — `# Vars:` is comma-separated, so a space-separated list collapses into a single malformed variable name (the rest are silently lost).",
1667
+ remediation: "Separate variables with commas: `# Vars: A, B, C` (not `# Vars: A B C`). Defaults still use `=`: `# Vars: A=\"x\", B`.",
1668
+ check: (ctx) => {
1669
+ const findings = [];
1670
+ for (const v of ctx.parsed.vars) {
1671
+ if (/\s/.test(v.name)) {
1672
+ findings.push({
1673
+ rule: "space-separated-vars",
1674
+ severity: "warning",
1675
+ message: `\`# Vars:\` entry '${v.name}' contains whitespace — looks space-separated. \`# Vars:\` is comma-separated; only commas split declarations. Did you mean \`${v.name.trim().split(/\s+/).join(", ")}\`?`,
1676
+ });
1677
+ }
1678
+ }
1679
+ return findings;
1680
+ },
1681
+ };
1637
1682
  const STATUS_DISABLED = {
1638
1683
  id: "status-disabled",
1639
1684
  severity: "error",
@@ -3595,6 +3640,8 @@ const RULES = [
3595
3640
  MISSING_SKILLSTORE_FOR_DATA_REF,
3596
3641
  // Tier-2 (warning)
3597
3642
  DEPRECATED_QUESTION,
3643
+ SPACE_SEPARATED_VARS,
3644
+ UNKNOWN_FILE_ENCODING,
3598
3645
  DEPRECATED_SUBSTITUTION_SHAPE,
3599
3646
  UNSAFE_SHELL_AMBIGUOUS_SUBST,
3600
3647
  UNSAFE_SHELL_UNESCAPED_SUBST,
@@ -3730,6 +3777,12 @@ function collectOpText(op) {
3730
3777
  text += " " + op.setValue;
3731
3778
  if (op.foreachList !== undefined)
3732
3779
  text += " " + op.foreachList;
3780
+ // v0.25.3 (adopter finding 6655eac4) — include `shell(argv=[...])` elements so
3781
+ // `${VAR}` refs inside an argv element are seen by the var-ref scans
3782
+ // (undeclared-var, unknown-filter, legacy-substitution-shape). Previously argv
3783
+ // refs escaped every check that routes through collectOpText.
3784
+ if (op.argv !== undefined)
3785
+ text += " " + op.argv.join(" ");
3733
3786
  return text;
3734
3787
  }
3735
3788
  /** Walk surrounding `foreach` scopes to see if `varName` is an iterator currently in scope at `op`. Conservative: walks the parent ops tree. */