@stigmer/runner 3.12.0 → 3.12.2
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/dist/.build-fingerprint +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +5 -4
- package/dist/activities/execute-cursor/capture-flow.js +5 -4
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
- package/dist/activities/execute-cursor/cas-observations.d.ts +10 -4
- package/dist/activities/execute-cursor/cas-observations.js +10 -4
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
- package/dist/activities/execute-cursor/hook-script.js +39 -13
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/skill-resolver.d.ts +8 -0
- package/dist/activities/execute-cursor/skill-resolver.js +77 -26
- package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +23 -8
- package/dist/activities/execute-deep-agent/attachment-injector.js +104 -105
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +3 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +3 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.js +11 -1
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +15 -3
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.js +16 -9
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/generate-session-subject.d.ts +80 -0
- package/dist/activities/generate-session-subject.js +283 -0
- package/dist/activities/generate-session-subject.js.map +1 -0
- package/dist/client/stigmer-client.d.ts +8 -0
- package/dist/client/stigmer-client.js +10 -0
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +25 -10
- package/dist/middleware/approval-gate.js +33 -14
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/otel-spans.d.ts +2 -1
- package/dist/middleware/otel-spans.js +2 -1
- package/dist/middleware/otel-spans.js.map +1 -1
- package/dist/middleware/path-normalization.d.ts +15 -2
- package/dist/middleware/path-normalization.js +39 -5
- package/dist/middleware/path-normalization.js.map +1 -1
- package/dist/runner-manager.js +3 -1
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +3 -1
- package/dist/runner.js.map +1 -1
- package/dist/shared/attachment-vision.js +9 -0
- package/dist/shared/attachment-vision.js.map +1 -1
- package/dist/shared/mcp-enabled-tools.d.ts +6 -2
- package/dist/shared/mcp-enabled-tools.js +6 -2
- package/dist/shared/mcp-enabled-tools.js.map +1 -1
- package/dist/shared/mcp-manager.js +8 -0
- package/dist/shared/mcp-manager.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +46 -10
- package/dist/shared/plan-mode-permissions.js +56 -12
- package/dist/shared/plan-mode-permissions.js.map +1 -1
- package/dist/shared/zip-extract.d.ts +24 -6
- package/dist/shared/zip-extract.js +31 -90
- package/dist/shared/zip-extract.js.map +1 -1
- package/dist/shared/zip-structure.d.ts +61 -0
- package/dist/shared/zip-structure.js +128 -0
- package/dist/shared/zip-structure.js.map +1 -0
- package/package.json +2 -2
- package/src/__test-utils__/zip-fixtures.ts +206 -0
- package/src/activities/__tests__/generate-session-subject.test.ts +348 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +60 -9
- package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +166 -49
- package/src/activities/execute-cursor/capture-flow.ts +5 -4
- package/src/activities/execute-cursor/cas-observations.ts +10 -4
- package/src/activities/execute-cursor/hook-script.ts +39 -13
- package/src/activities/execute-cursor/skill-resolver.ts +98 -31
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +165 -126
- package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +246 -37
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +7 -2
- package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +35 -4
- package/src/activities/execute-deep-agent/attachment-injector.ts +146 -142
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +3 -1
- package/src/activities/execute-deep-agent/prompt-builder.ts +11 -1
- package/src/activities/execute-deep-agent/setup.ts +15 -3
- package/src/activities/execute-deep-agent/subagent-wiring.ts +16 -9
- package/src/activities/generate-session-subject.ts +370 -0
- package/src/client/stigmer-client.ts +11 -0
- package/src/middleware/__tests__/approval-gate.test.ts +130 -1
- package/src/middleware/__tests__/path-normalization.test.ts +29 -3
- package/src/middleware/approval-gate.ts +58 -24
- package/src/middleware/otel-spans.ts +2 -1
- package/src/middleware/path-normalization.ts +42 -5
- package/src/runner-manager.ts +3 -0
- package/src/runner.ts +3 -0
- package/src/shared/__tests__/attachment-vision.test.ts +4 -0
- package/src/shared/__tests__/mcp-manager.test.ts +7 -2
- package/src/shared/__tests__/plan-mode-permissions.test.ts +58 -0
- package/src/shared/__tests__/zip-extract.test.ts +106 -89
- package/src/shared/attachment-vision.ts +9 -0
- package/src/shared/filereview/__tests__/capture.test.ts +48 -0
- package/src/shared/mcp-enabled-tools.ts +6 -2
- package/src/shared/mcp-manager.ts +8 -0
- package/src/shared/plan-mode-permissions.ts +59 -12
- package/src/shared/zip-extract.ts +35 -117
- package/src/shared/zip-structure.ts +181 -0
|
@@ -2,12 +2,26 @@
|
|
|
2
2
|
* The Plan-mode filesystem permission rules — the enforcement twin of
|
|
3
3
|
* `plan-mode-prompt.ts` (which carries the instruction half of the contract).
|
|
4
4
|
*
|
|
5
|
-
* Plan mode is
|
|
6
|
-
* deny every filesystem write
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* Plan mode is contained BY CONSTRUCTION on the native harness: these rules
|
|
6
|
+
* deny every filesystem write everywhere AND scope reads to the session
|
|
7
|
+
* workspace (issue #528 — owner ruling: the workspace is plan mode's read
|
|
8
|
+
* boundary on cloud and desktop runners alike). Without the read boundary,
|
|
9
|
+
* model-provided absolute paths reached anywhere the process account could
|
|
10
|
+
* read — the pod filesystem (including /proc/self/environ) on cloud runners,
|
|
11
|
+
* the user's whole home directory on desktop — while plan mode still carries
|
|
12
|
+
* exfiltration-capable tools (web_fetch, MCP). Rules are first-match-wins
|
|
13
|
+
* with a permissive default (deepagents' decidePathAccess), so order is
|
|
14
|
+
* load-bearing: the workspace read-allow must precede the read-deny.
|
|
15
|
+
*
|
|
16
|
+
* The workspace-root pattern is matched as a STRING against the raw tool-call
|
|
17
|
+
* path (micromatch, dot:true), before the backend touches disk. That is
|
|
18
|
+
* exactly why the legitimate out-of-workspace reads keep working: skills,
|
|
19
|
+
* attachments, and the approved plan live in the platform dir but are
|
|
20
|
+
* addressed through the `{workspace}/.stigmer` symlink (see
|
|
21
|
+
* shared/workspace/stigmer-link.ts), so their path strings are in-root even
|
|
22
|
+
* though the bytes are not. The same holds for multi-workspace local entries
|
|
23
|
+
* (`{workspace}/{name}` symlinks). A realpath-based boundary would break
|
|
24
|
+
* both; do not "harden" this into one.
|
|
11
25
|
*
|
|
12
26
|
* Applied in execute-deep-agent/setup.ts to the parent graph AND threaded
|
|
13
27
|
* into every compiled sub-agent graph: deepagents' parent-permission
|
|
@@ -18,9 +32,9 @@
|
|
|
18
32
|
*
|
|
19
33
|
* Rules travel with a companion: every graph that carries them also installs
|
|
20
34
|
* the path-normalization middleware (middleware/path-normalization.ts,
|
|
21
|
-
*
|
|
22
|
-
* paths outright
|
|
23
|
-
*
|
|
35
|
+
* issues #429/#528), because deepagents' rule validation refuses
|
|
36
|
+
* workspace-relative paths outright and its `ls`/`glob`/`grep` schema default
|
|
37
|
+
* of "/" would deny the bare first listing. Both are derived from the same
|
|
24
38
|
* expression at each composition site so they cannot drift apart.
|
|
25
39
|
*
|
|
26
40
|
* Invariant: never combine these rules with a shell-capable (sandbox)
|
|
@@ -30,4 +44,26 @@
|
|
|
30
44
|
* for shell capability on both the parent and sub-agent backends.
|
|
31
45
|
*/
|
|
32
46
|
import type { FilesystemPermission } from "deepagents";
|
|
33
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Backslash-escape every character micromatch/picomatch treats as glob
|
|
49
|
+
* syntax, so the result matches the input literally. micromatch exports no
|
|
50
|
+
* escape API of its own, and this is correctness, not caution: an unescaped
|
|
51
|
+
* `(` in a desktop project path would make the workspace read-allow rule
|
|
52
|
+
* silently never match — bricking every plan-mode read for that workspace.
|
|
53
|
+
* Semantics are pinned end-to-end through deepagents' own matcher by the
|
|
54
|
+
* special-character workspace suite in plan-mode-path-normalization.test.ts.
|
|
55
|
+
*/
|
|
56
|
+
export declare function escapeGlobLiteral(literal: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Build the plan-mode rule set for a graph whose filesystem backend is
|
|
59
|
+
* rooted at `workspaceRootDir`. The three rules read as the policy:
|
|
60
|
+
* reads allowed in the workspace, reads denied everywhere else, writes
|
|
61
|
+
* denied everywhere.
|
|
62
|
+
*
|
|
63
|
+
* `{root}/**` matches the root itself as well as its subtree (verified
|
|
64
|
+
* against the installed micromatch), so one allow pattern suffices. The
|
|
65
|
+
* root is `path.resolve`d first because enforcement canonicalizes incoming
|
|
66
|
+
* paths (collapsed slashes, no trailing separator) before matching — a
|
|
67
|
+
* trailing slash in the pattern would silently match nothing.
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildPlanModePermissions(workspaceRootDir: string): FilesystemPermission[];
|
|
@@ -2,12 +2,26 @@
|
|
|
2
2
|
* The Plan-mode filesystem permission rules — the enforcement twin of
|
|
3
3
|
* `plan-mode-prompt.ts` (which carries the instruction half of the contract).
|
|
4
4
|
*
|
|
5
|
-
* Plan mode is
|
|
6
|
-
* deny every filesystem write
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* Plan mode is contained BY CONSTRUCTION on the native harness: these rules
|
|
6
|
+
* deny every filesystem write everywhere AND scope reads to the session
|
|
7
|
+
* workspace (issue #528 — owner ruling: the workspace is plan mode's read
|
|
8
|
+
* boundary on cloud and desktop runners alike). Without the read boundary,
|
|
9
|
+
* model-provided absolute paths reached anywhere the process account could
|
|
10
|
+
* read — the pod filesystem (including /proc/self/environ) on cloud runners,
|
|
11
|
+
* the user's whole home directory on desktop — while plan mode still carries
|
|
12
|
+
* exfiltration-capable tools (web_fetch, MCP). Rules are first-match-wins
|
|
13
|
+
* with a permissive default (deepagents' decidePathAccess), so order is
|
|
14
|
+
* load-bearing: the workspace read-allow must precede the read-deny.
|
|
15
|
+
*
|
|
16
|
+
* The workspace-root pattern is matched as a STRING against the raw tool-call
|
|
17
|
+
* path (micromatch, dot:true), before the backend touches disk. That is
|
|
18
|
+
* exactly why the legitimate out-of-workspace reads keep working: skills,
|
|
19
|
+
* attachments, and the approved plan live in the platform dir but are
|
|
20
|
+
* addressed through the `{workspace}/.stigmer` symlink (see
|
|
21
|
+
* shared/workspace/stigmer-link.ts), so their path strings are in-root even
|
|
22
|
+
* though the bytes are not. The same holds for multi-workspace local entries
|
|
23
|
+
* (`{workspace}/{name}` symlinks). A realpath-based boundary would break
|
|
24
|
+
* both; do not "harden" this into one.
|
|
11
25
|
*
|
|
12
26
|
* Applied in execute-deep-agent/setup.ts to the parent graph AND threaded
|
|
13
27
|
* into every compiled sub-agent graph: deepagents' parent-permission
|
|
@@ -18,9 +32,9 @@
|
|
|
18
32
|
*
|
|
19
33
|
* Rules travel with a companion: every graph that carries them also installs
|
|
20
34
|
* the path-normalization middleware (middleware/path-normalization.ts,
|
|
21
|
-
*
|
|
22
|
-
* paths outright
|
|
23
|
-
*
|
|
35
|
+
* issues #429/#528), because deepagents' rule validation refuses
|
|
36
|
+
* workspace-relative paths outright and its `ls`/`glob`/`grep` schema default
|
|
37
|
+
* of "/" would deny the bare first listing. Both are derived from the same
|
|
24
38
|
* expression at each composition site so they cannot drift apart.
|
|
25
39
|
*
|
|
26
40
|
* Invariant: never combine these rules with a shell-capable (sandbox)
|
|
@@ -29,7 +43,37 @@
|
|
|
29
43
|
* it is the mode that clears `shellEnv`, and `shellEnv` is the single switch
|
|
30
44
|
* for shell capability on both the parent and sub-agent backends.
|
|
31
45
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
import { resolve } from "node:path";
|
|
47
|
+
/**
|
|
48
|
+
* Backslash-escape every character micromatch/picomatch treats as glob
|
|
49
|
+
* syntax, so the result matches the input literally. micromatch exports no
|
|
50
|
+
* escape API of its own, and this is correctness, not caution: an unescaped
|
|
51
|
+
* `(` in a desktop project path would make the workspace read-allow rule
|
|
52
|
+
* silently never match — bricking every plan-mode read for that workspace.
|
|
53
|
+
* Semantics are pinned end-to-end through deepagents' own matcher by the
|
|
54
|
+
* special-character workspace suite in plan-mode-path-normalization.test.ts.
|
|
55
|
+
*/
|
|
56
|
+
export function escapeGlobLiteral(literal) {
|
|
57
|
+
return literal.replace(/[\\*?()[\]{}!+@]/g, "\\$&");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build the plan-mode rule set for a graph whose filesystem backend is
|
|
61
|
+
* rooted at `workspaceRootDir`. The three rules read as the policy:
|
|
62
|
+
* reads allowed in the workspace, reads denied everywhere else, writes
|
|
63
|
+
* denied everywhere.
|
|
64
|
+
*
|
|
65
|
+
* `{root}/**` matches the root itself as well as its subtree (verified
|
|
66
|
+
* against the installed micromatch), so one allow pattern suffices. The
|
|
67
|
+
* root is `path.resolve`d first because enforcement canonicalizes incoming
|
|
68
|
+
* paths (collapsed slashes, no trailing separator) before matching — a
|
|
69
|
+
* trailing slash in the pattern would silently match nothing.
|
|
70
|
+
*/
|
|
71
|
+
export function buildPlanModePermissions(workspaceRootDir) {
|
|
72
|
+
const canonicalRoot = resolve(workspaceRootDir);
|
|
73
|
+
return [
|
|
74
|
+
{ operations: ["read"], paths: [`${escapeGlobLiteral(canonicalRoot)}/**`] },
|
|
75
|
+
{ operations: ["read"], paths: ["/**"], mode: "deny" },
|
|
76
|
+
{ operations: ["write"], paths: ["/**"], mode: "deny" },
|
|
77
|
+
];
|
|
78
|
+
}
|
|
35
79
|
//# sourceMappingURL=plan-mode-permissions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-mode-permissions.js","sourceRoot":"","sources":["../../src/shared/plan-mode-permissions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"plan-mode-permissions.js","sourceRoot":"","sources":["../../src/shared/plan-mode-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CACtC,gBAAwB;IAExB,MAAM,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChD,OAAO;QACL,EAAE,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,iBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;QAC3E,EAAE,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;QACtD,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;KACxD,CAAC;AACJ,CAAC"}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Skill-artifact ZIP extraction: text-decoded entries with non-fatal
|
|
3
|
+
* structural failure.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Structural parsing lives in zip-structure.ts (central-directory-based —
|
|
6
|
+
* see that module's doc for why local-header walks are never acceptable,
|
|
7
|
+
* issue #450). This module owns the skill-artifact *policy* on top of it:
|
|
8
|
+
*
|
|
9
|
+
* Input without a readable central directory is not given a second
|
|
10
|
+
* chance on purpose: every artifact reaching the runner was validated at
|
|
11
|
+
* push by a central-directory-based reader on both editions (OSS:
|
|
12
|
+
* `safearchive/zip` in stigmer-server's skill storage; cloud:
|
|
13
|
+
* commons-compress `ZipFile` in SkillArtifactExtractor), so a missing
|
|
14
|
+
* EOCD here can only mean a truncated or corrupted download — and the
|
|
15
|
+
* honest result for that is the documented empty return, not a guess.
|
|
16
|
+
* (The attachment injector consumes the same structural layer under the
|
|
17
|
+
* opposite policy — fail-hard — because its input is untrusted.)
|
|
18
|
+
*
|
|
19
|
+
* Handles stored (method 0) and deflated (method 8) entries. Returns
|
|
20
|
+
* parsed entries as path + content pairs — consumers bring their own
|
|
21
|
+
* write mechanism (WorkspaceBackend, node:fs, etc.).
|
|
8
22
|
*
|
|
9
23
|
* Extracted from skill-writer.ts so both the deep-agent and Cursor
|
|
10
24
|
* harnesses can share the same ZIP parsing logic without coupling to
|
|
@@ -23,7 +37,11 @@ export interface ZipFileEntry {
|
|
|
23
37
|
* entries whose filename (basename or full path) matches any excluded name.
|
|
24
38
|
*
|
|
25
39
|
* Returns an empty array for empty, truncated, or non-ZIP input rather
|
|
26
|
-
* than throwing — callers treat missing artifacts as non-fatal.
|
|
40
|
+
* than throwing — callers treat missing artifacts as non-fatal. Errors
|
|
41
|
+
* while *decompressing* a structurally valid entry (corrupt deflate
|
|
42
|
+
* stream, unsupported compression method) do propagate: at that point
|
|
43
|
+
* the archive's structure vouched for the entry, and silently dropping
|
|
44
|
+
* it would be the exact corruption this module exists to prevent.
|
|
27
45
|
*/
|
|
28
46
|
export declare function extractZipFileEntries(zipBytes: Uint8Array, options?: {
|
|
29
47
|
exclude?: readonly string[];
|
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Skill-artifact ZIP extraction: text-decoded entries with non-fatal
|
|
3
|
+
* structural failure.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Structural parsing lives in zip-structure.ts (central-directory-based —
|
|
6
|
+
* see that module's doc for why local-header walks are never acceptable,
|
|
7
|
+
* issue #450). This module owns the skill-artifact *policy* on top of it:
|
|
8
|
+
*
|
|
9
|
+
* Input without a readable central directory is not given a second
|
|
10
|
+
* chance on purpose: every artifact reaching the runner was validated at
|
|
11
|
+
* push by a central-directory-based reader on both editions (OSS:
|
|
12
|
+
* `safearchive/zip` in stigmer-server's skill storage; cloud:
|
|
13
|
+
* commons-compress `ZipFile` in SkillArtifactExtractor), so a missing
|
|
14
|
+
* EOCD here can only mean a truncated or corrupted download — and the
|
|
15
|
+
* honest result for that is the documented empty return, not a guess.
|
|
16
|
+
* (The attachment injector consumes the same structural layer under the
|
|
17
|
+
* opposite policy — fail-hard — because its input is untrusted.)
|
|
18
|
+
*
|
|
19
|
+
* Handles stored (method 0) and deflated (method 8) entries. Returns
|
|
20
|
+
* parsed entries as path + content pairs — consumers bring their own
|
|
21
|
+
* write mechanism (WorkspaceBackend, node:fs, etc.).
|
|
8
22
|
*
|
|
9
23
|
* Extracted from skill-writer.ts so both the deep-agent and Cursor
|
|
10
24
|
* harnesses can share the same ZIP parsing logic without coupling to
|
|
11
25
|
* a specific workspace abstraction.
|
|
12
26
|
*/
|
|
13
27
|
import { createInflateRaw } from "node:zlib";
|
|
28
|
+
import { EOCD_MIN_SIZE, parseZipStructure } from "./zip-structure.js";
|
|
14
29
|
/**
|
|
15
30
|
* Parse and decompress all file entries from a ZIP archive.
|
|
16
31
|
*
|
|
@@ -18,16 +33,22 @@ import { createInflateRaw } from "node:zlib";
|
|
|
18
33
|
* entries whose filename (basename or full path) matches any excluded name.
|
|
19
34
|
*
|
|
20
35
|
* Returns an empty array for empty, truncated, or non-ZIP input rather
|
|
21
|
-
* than throwing — callers treat missing artifacts as non-fatal.
|
|
36
|
+
* than throwing — callers treat missing artifacts as non-fatal. Errors
|
|
37
|
+
* while *decompressing* a structurally valid entry (corrupt deflate
|
|
38
|
+
* stream, unsupported compression method) do propagate: at that point
|
|
39
|
+
* the archive's structure vouched for the entry, and silently dropping
|
|
40
|
+
* it would be the exact corruption this module exists to prevent.
|
|
22
41
|
*/
|
|
23
42
|
export async function extractZipFileEntries(zipBytes, options) {
|
|
24
|
-
if (zipBytes.length <
|
|
43
|
+
if (zipBytes.length < EOCD_MIN_SIZE)
|
|
25
44
|
return [];
|
|
26
45
|
let entries;
|
|
27
46
|
try {
|
|
28
|
-
entries =
|
|
47
|
+
entries = parseZipStructure(zipBytes);
|
|
29
48
|
}
|
|
30
|
-
catch {
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.warn("[zip-extract] archive has no readable central directory " +
|
|
51
|
+
`(truncated or corrupt download?): ${err instanceof Error ? err.message : String(err)}`);
|
|
31
52
|
return [];
|
|
32
53
|
}
|
|
33
54
|
const excludeSet = new Set(options?.exclude ?? []);
|
|
@@ -50,87 +71,7 @@ function isExcluded(name, excludeSet) {
|
|
|
50
71
|
const basename = name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
|
|
51
72
|
return excludeSet.has(basename);
|
|
52
73
|
}
|
|
53
|
-
|
|
54
|
-
const entries = [];
|
|
55
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
56
|
-
let offset = 0;
|
|
57
|
-
while (offset < data.length - 4) {
|
|
58
|
-
const signature = view.getUint32(offset, true);
|
|
59
|
-
if (signature !== 0x04034b50)
|
|
60
|
-
break; // Local file header signature
|
|
61
|
-
const generalFlags = view.getUint16(offset + 6, true);
|
|
62
|
-
const hasDataDescriptor = (generalFlags & 0x08) !== 0;
|
|
63
|
-
const compressionMethod = view.getUint16(offset + 8, true);
|
|
64
|
-
let compressedSize = view.getUint32(offset + 18, true);
|
|
65
|
-
let uncompressedSize = view.getUint32(offset + 22, true);
|
|
66
|
-
const fileNameLength = view.getUint16(offset + 26, true);
|
|
67
|
-
const extraFieldLength = view.getUint16(offset + 28, true);
|
|
68
|
-
const fileNameStart = offset + 30;
|
|
69
|
-
const fileName = new TextDecoder().decode(data.subarray(fileNameStart, fileNameStart + fileNameLength));
|
|
70
|
-
const dataStart = fileNameStart + fileNameLength + extraFieldLength;
|
|
71
|
-
if (hasDataDescriptor && compressedSize === 0) {
|
|
72
|
-
const sizes = findDataDescriptor(data, view, dataStart, compressionMethod);
|
|
73
|
-
compressedSize = sizes.compressedSize;
|
|
74
|
-
uncompressedSize = sizes.uncompressedSize;
|
|
75
|
-
}
|
|
76
|
-
const compressedData = data.subarray(dataStart, dataStart + compressedSize);
|
|
77
|
-
entries.push({
|
|
78
|
-
name: fileName,
|
|
79
|
-
isDirectory: fileName.endsWith("/"),
|
|
80
|
-
compressedData,
|
|
81
|
-
compressionMethod,
|
|
82
|
-
uncompressedSize,
|
|
83
|
-
});
|
|
84
|
-
let nextOffset = dataStart + compressedSize;
|
|
85
|
-
if (hasDataDescriptor) {
|
|
86
|
-
if (nextOffset + 4 <= data.length && view.getUint32(nextOffset, true) === 0x08074b50) {
|
|
87
|
-
nextOffset += 16; // signature(4) + crc(4) + compressedSize(4) + uncompressedSize(4)
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
nextOffset += 12; // crc(4) + compressedSize(4) + uncompressedSize(4)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
offset = nextOffset;
|
|
94
|
-
}
|
|
95
|
-
return entries;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Scan forward from dataStart to find the data descriptor that contains
|
|
99
|
-
* the actual compressed and uncompressed sizes. Looks for either the
|
|
100
|
-
* optional signature 0x08074b50 or falls back to scanning the central
|
|
101
|
-
* directory for the matching entry.
|
|
102
|
-
*/
|
|
103
|
-
function findDataDescriptor(data, view, dataStart, _compressionMethod) {
|
|
104
|
-
for (let pos = dataStart; pos < data.length - 16; pos++) {
|
|
105
|
-
const sig = view.getUint32(pos, true);
|
|
106
|
-
if (sig === 0x08074b50) {
|
|
107
|
-
return {
|
|
108
|
-
compressedSize: view.getUint32(pos + 8, true),
|
|
109
|
-
uncompressedSize: view.getUint32(pos + 12, true),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (sig === 0x04034b50 || sig === 0x02014b50) {
|
|
113
|
-
const descStart = pos - 12;
|
|
114
|
-
if (descStart >= dataStart) {
|
|
115
|
-
return {
|
|
116
|
-
compressedSize: view.getUint32(descStart + 4, true),
|
|
117
|
-
uncompressedSize: view.getUint32(descStart + 8, true),
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
for (let pos = dataStart; pos < data.length - 4; pos++) {
|
|
124
|
-
const sig = view.getUint32(pos, true);
|
|
125
|
-
if (sig === 0x04034b50 || sig === 0x02014b50 || sig === 0x08074b50) {
|
|
126
|
-
const compressedSize = sig === 0x08074b50
|
|
127
|
-
? view.getUint32(pos + 8, true)
|
|
128
|
-
: pos - dataStart;
|
|
129
|
-
return { compressedSize, uncompressedSize: 0 };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return { compressedSize: data.length - dataStart, uncompressedSize: 0 };
|
|
133
|
-
}
|
|
74
|
+
// ─── Decompression ───────────────────────────────────────────────────────
|
|
134
75
|
async function decompressEntry(entry) {
|
|
135
76
|
if (entry.compressionMethod === 0) {
|
|
136
77
|
return new TextDecoder().decode(entry.compressedData);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zip-extract.js","sourceRoot":"","sources":["../../src/shared/zip-extract.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"zip-extract.js","sourceRoot":"","sources":["../../src/shared/zip-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy-free structural ZIP parsing: end-of-central-directory (EOCD)
|
|
3
|
+
* location plus central-directory walk, producing one record per entry.
|
|
4
|
+
*
|
|
5
|
+
* Parsing is *central-directory-based* — never a front-to-back walk of
|
|
6
|
+
* local file headers. This is a correctness decision, not a style choice
|
|
7
|
+
* (issues #450 and #567): local headers of streaming entries
|
|
8
|
+
* (general-purpose flag bit 3) carry zeroed sizes, and the only
|
|
9
|
+
* local-only way to recover them is scanning the payload for the
|
|
10
|
+
* data-descriptor signature — which silently truncates any stored entry
|
|
11
|
+
* whose *content* happens to contain those four bytes, and
|
|
12
|
+
* desynchronizes every entry after it. The central directory always
|
|
13
|
+
* carries the real sizes, and every consumer here holds the complete
|
|
14
|
+
* archive bytes, so nothing a local-header walk could offer is needed.
|
|
15
|
+
*
|
|
16
|
+
* This layer maps bytes to entry records and nothing else. Policy — what
|
|
17
|
+
* a structural failure means, which entries are acceptable, how payloads
|
|
18
|
+
* are decoded — belongs to the consumers, and they differ on purpose:
|
|
19
|
+
*
|
|
20
|
+
* - shared/zip-extract.ts (skill artifacts): structural failure is
|
|
21
|
+
* NON-FATAL — both editions' push gates validated every artifact
|
|
22
|
+
* with a central-directory-based reader before storage, so a defect
|
|
23
|
+
* here can only be a truncated or corrupted download.
|
|
24
|
+
* - execute-deep-agent/attachment-injector.ts (user attachments):
|
|
25
|
+
* structural failure is FAIL-HARD — the input is an untrusted
|
|
26
|
+
* upload and nothing upstream vouched for it.
|
|
27
|
+
*
|
|
28
|
+
* ZIP64 is deliberately unsupported: both consumers cap input far below
|
|
29
|
+
* every ZIP64 threshold (skill push gates: 100MB / 10,000 files;
|
|
30
|
+
* attachment uploads: 10MB).
|
|
31
|
+
*
|
|
32
|
+
* Throws plain `Error`s on structural defects (no valid EOCD, a central
|
|
33
|
+
* directory or local header record that does not parse, a payload slice
|
|
34
|
+
* that runs past the end of the buffer); consumers translate those into
|
|
35
|
+
* their own error models.
|
|
36
|
+
*/
|
|
37
|
+
export interface ZipStructuralEntry {
|
|
38
|
+
/** Entry path exactly as recorded in the central directory. */
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly isDirectory: boolean;
|
|
41
|
+
readonly compressionMethod: number;
|
|
42
|
+
/**
|
|
43
|
+
* The central directory's declared uncompressed size — authoritative for
|
|
44
|
+
* pre-extraction accounting (e.g. ZIP-bomb budgeting), but still a
|
|
45
|
+
* *declaration*: a consumer that distrusts its input must enforce it
|
|
46
|
+
* against the actual decompressed output.
|
|
47
|
+
*/
|
|
48
|
+
readonly uncompressedSize: number;
|
|
49
|
+
/** The entry's raw payload slice (a view, not a copy) of the archive buffer. */
|
|
50
|
+
readonly compressedData: Uint8Array;
|
|
51
|
+
}
|
|
52
|
+
/** Fixed size of the EOCD record, excluding the variable-length comment. */
|
|
53
|
+
export declare const EOCD_MIN_SIZE = 22;
|
|
54
|
+
/**
|
|
55
|
+
* Enumerate the archive's entries from its central directory.
|
|
56
|
+
*
|
|
57
|
+
* Throws on any structural defect: no valid EOCD, a central directory or
|
|
58
|
+
* local header record that doesn't parse, or a payload slice that runs
|
|
59
|
+
* past the end of the buffer.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseZipStructure(data: Uint8Array): ZipStructuralEntry[];
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy-free structural ZIP parsing: end-of-central-directory (EOCD)
|
|
3
|
+
* location plus central-directory walk, producing one record per entry.
|
|
4
|
+
*
|
|
5
|
+
* Parsing is *central-directory-based* — never a front-to-back walk of
|
|
6
|
+
* local file headers. This is a correctness decision, not a style choice
|
|
7
|
+
* (issues #450 and #567): local headers of streaming entries
|
|
8
|
+
* (general-purpose flag bit 3) carry zeroed sizes, and the only
|
|
9
|
+
* local-only way to recover them is scanning the payload for the
|
|
10
|
+
* data-descriptor signature — which silently truncates any stored entry
|
|
11
|
+
* whose *content* happens to contain those four bytes, and
|
|
12
|
+
* desynchronizes every entry after it. The central directory always
|
|
13
|
+
* carries the real sizes, and every consumer here holds the complete
|
|
14
|
+
* archive bytes, so nothing a local-header walk could offer is needed.
|
|
15
|
+
*
|
|
16
|
+
* This layer maps bytes to entry records and nothing else. Policy — what
|
|
17
|
+
* a structural failure means, which entries are acceptable, how payloads
|
|
18
|
+
* are decoded — belongs to the consumers, and they differ on purpose:
|
|
19
|
+
*
|
|
20
|
+
* - shared/zip-extract.ts (skill artifacts): structural failure is
|
|
21
|
+
* NON-FATAL — both editions' push gates validated every artifact
|
|
22
|
+
* with a central-directory-based reader before storage, so a defect
|
|
23
|
+
* here can only be a truncated or corrupted download.
|
|
24
|
+
* - execute-deep-agent/attachment-injector.ts (user attachments):
|
|
25
|
+
* structural failure is FAIL-HARD — the input is an untrusted
|
|
26
|
+
* upload and nothing upstream vouched for it.
|
|
27
|
+
*
|
|
28
|
+
* ZIP64 is deliberately unsupported: both consumers cap input far below
|
|
29
|
+
* every ZIP64 threshold (skill push gates: 100MB / 10,000 files;
|
|
30
|
+
* attachment uploads: 10MB).
|
|
31
|
+
*
|
|
32
|
+
* Throws plain `Error`s on structural defects (no valid EOCD, a central
|
|
33
|
+
* directory or local header record that does not parse, a payload slice
|
|
34
|
+
* that runs past the end of the buffer); consumers translate those into
|
|
35
|
+
* their own error models.
|
|
36
|
+
*/
|
|
37
|
+
// ─── Format constants ────────────────────────────────────────────────────
|
|
38
|
+
const LOCAL_FILE_HEADER_SIG = 0x04034b50;
|
|
39
|
+
const CENTRAL_DIRECTORY_SIG = 0x02014b50;
|
|
40
|
+
const EOCD_SIG = 0x06054b50;
|
|
41
|
+
/** Fixed size of the EOCD record, excluding the variable-length comment. */
|
|
42
|
+
export const EOCD_MIN_SIZE = 22;
|
|
43
|
+
/** The archive comment length is a u16, so the EOCD sits within the last 64KB + 22 bytes. */
|
|
44
|
+
const EOCD_MAX_COMMENT = 0xffff;
|
|
45
|
+
const LOCAL_HEADER_SIZE = 30;
|
|
46
|
+
const CD_RECORD_SIZE = 46;
|
|
47
|
+
// ─── Parsing ─────────────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Enumerate the archive's entries from its central directory.
|
|
50
|
+
*
|
|
51
|
+
* Throws on any structural defect: no valid EOCD, a central directory or
|
|
52
|
+
* local header record that doesn't parse, or a payload slice that runs
|
|
53
|
+
* past the end of the buffer.
|
|
54
|
+
*/
|
|
55
|
+
export function parseZipStructure(data) {
|
|
56
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
57
|
+
const eocd = findEndOfCentralDirectory(data, view);
|
|
58
|
+
const entries = [];
|
|
59
|
+
let offset = eocd.centralDirectoryOffset;
|
|
60
|
+
for (let i = 0; i < eocd.entryCount; i++) {
|
|
61
|
+
if (offset + CD_RECORD_SIZE > data.length || view.getUint32(offset, true) !== CENTRAL_DIRECTORY_SIG) {
|
|
62
|
+
throw new Error(`invalid central directory record at offset ${offset}`);
|
|
63
|
+
}
|
|
64
|
+
const compressionMethod = view.getUint16(offset + 10, true);
|
|
65
|
+
const compressedSize = view.getUint32(offset + 20, true);
|
|
66
|
+
const uncompressedSize = view.getUint32(offset + 24, true);
|
|
67
|
+
const fileNameLength = view.getUint16(offset + 28, true);
|
|
68
|
+
const extraFieldLength = view.getUint16(offset + 30, true);
|
|
69
|
+
const commentLength = view.getUint16(offset + 32, true);
|
|
70
|
+
const localHeaderOffset = view.getUint32(offset + 42, true);
|
|
71
|
+
const fileName = new TextDecoder().decode(data.subarray(offset + CD_RECORD_SIZE, offset + CD_RECORD_SIZE + fileNameLength));
|
|
72
|
+
// The local header is consulted for one thing only: the payload start.
|
|
73
|
+
// Its name/extra field lengths can legitimately differ from the central
|
|
74
|
+
// directory's (streaming writers pad the extra field), so the offset
|
|
75
|
+
// cannot be derived from CD fields alone.
|
|
76
|
+
const dataStart = payloadStart(data, view, localHeaderOffset, fileName);
|
|
77
|
+
if (dataStart + compressedSize > data.length) {
|
|
78
|
+
throw new Error(`entry "${fileName}" payload runs past end of archive`);
|
|
79
|
+
}
|
|
80
|
+
entries.push({
|
|
81
|
+
name: fileName,
|
|
82
|
+
isDirectory: fileName.endsWith("/"),
|
|
83
|
+
compressionMethod,
|
|
84
|
+
uncompressedSize,
|
|
85
|
+
compressedData: data.subarray(dataStart, dataStart + compressedSize),
|
|
86
|
+
});
|
|
87
|
+
offset += CD_RECORD_SIZE + fileNameLength + extraFieldLength + commentLength;
|
|
88
|
+
}
|
|
89
|
+
return entries;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Locate and validate the end-of-central-directory record.
|
|
93
|
+
*
|
|
94
|
+
* Scans backward from the end of the archive across the maximum comment
|
|
95
|
+
* span. A signature match alone is not trusted (the four bytes can occur
|
|
96
|
+
* inside a trailing comment): the record must also point at an offset
|
|
97
|
+
* that actually holds a central directory record, or be a genuinely
|
|
98
|
+
* empty archive.
|
|
99
|
+
*/
|
|
100
|
+
function findEndOfCentralDirectory(data, view) {
|
|
101
|
+
const scanFloor = Math.max(0, data.length - EOCD_MIN_SIZE - EOCD_MAX_COMMENT);
|
|
102
|
+
for (let pos = data.length - EOCD_MIN_SIZE; pos >= scanFloor; pos--) {
|
|
103
|
+
if (view.getUint32(pos, true) !== EOCD_SIG)
|
|
104
|
+
continue;
|
|
105
|
+
const entryCount = view.getUint16(pos + 10, true);
|
|
106
|
+
const centralDirectorySize = view.getUint32(pos + 12, true);
|
|
107
|
+
const centralDirectoryOffset = view.getUint32(pos + 16, true);
|
|
108
|
+
const directoryEndsAtRecord = centralDirectoryOffset + centralDirectorySize <= pos;
|
|
109
|
+
const directoryLooksReal = entryCount === 0 ||
|
|
110
|
+
(centralDirectoryOffset + 4 <= data.length &&
|
|
111
|
+
view.getUint32(centralDirectoryOffset, true) === CENTRAL_DIRECTORY_SIG);
|
|
112
|
+
if (directoryEndsAtRecord && directoryLooksReal) {
|
|
113
|
+
return { centralDirectoryOffset, entryCount };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw new Error("no end-of-central-directory record found");
|
|
117
|
+
}
|
|
118
|
+
/** Resolve where an entry's payload begins, from its local file header. */
|
|
119
|
+
function payloadStart(data, view, localHeaderOffset, fileName) {
|
|
120
|
+
if (localHeaderOffset + LOCAL_HEADER_SIZE > data.length ||
|
|
121
|
+
view.getUint32(localHeaderOffset, true) !== LOCAL_FILE_HEADER_SIG) {
|
|
122
|
+
throw new Error(`entry "${fileName}" has no local file header at offset ${localHeaderOffset}`);
|
|
123
|
+
}
|
|
124
|
+
const localNameLength = view.getUint16(localHeaderOffset + 26, true);
|
|
125
|
+
const localExtraLength = view.getUint16(localHeaderOffset + 28, true);
|
|
126
|
+
return localHeaderOffset + LOCAL_HEADER_SIZE + localNameLength + localExtraLength;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=zip-structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip-structure.js","sourceRoot":"","sources":["../../src/shared/zip-structure.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/runner",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.2",
|
|
4
4
|
"description": "Embeddable Temporal worker for the Stigmer AI agent platform — handles agent execution, workflow orchestration, and MCP server management",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"@opentelemetry/sdk-metrics": "^2.0.0",
|
|
91
91
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
92
92
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
93
|
-
"@stigmer/protos": "3.12.
|
|
93
|
+
"@stigmer/protos": "3.12.2",
|
|
94
94
|
"@temporalio/activity": "^1.11.0",
|
|
95
95
|
"@temporalio/common": "^1.11.0",
|
|
96
96
|
"@temporalio/interceptors-opentelemetry": "~1.16.0",
|