@stigmer/runner 3.12.8 → 3.13.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/dist/.build-fingerprint +1 -1
- package/dist/activities/call-agent.d.ts +3 -2
- package/dist/activities/call-agent.js +13 -4
- package/dist/activities/call-agent.js.map +1 -1
- package/dist/activities/discover-mcp-server.js +1 -1
- package/dist/activities/execute-cursor/fetch-interceptor.js +29 -16
- package/dist/activities/execute-cursor/fetch-interceptor.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +14 -0
- package/dist/activities/execute-cursor/index.js +55 -5
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +15 -9
- package/dist/activities/execute-cursor/message-translator.js +15 -9
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +1 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +2 -2
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.js +9 -0
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +10 -1
- package/dist/activities/execute-deep-agent/setup.js +20 -3
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/client/stigmer-client.js +7 -2
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/payload-codecs.d.ts +1 -1
- package/dist/payload-codecs.js +6 -4
- package/dist/payload-codecs.js.map +1 -1
- package/dist/runner-manager.js +1 -1
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +1 -1
- package/dist/runner.js.map +1 -1
- package/dist/shared/memory-retrieval.d.ts +126 -0
- package/dist/shared/memory-retrieval.js +293 -0
- package/dist/shared/memory-retrieval.js.map +1 -0
- package/dist/shared/runner-credential-keys.js +2 -1
- package/dist/shared/runner-credential-keys.js.map +1 -1
- package/dist/shared/zip-extract.js +1 -1
- package/dist/shared/zip-extract.js.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/history-encryption-e2e.test.ts +1 -1
- package/src/activities/call-agent.ts +14 -4
- package/src/activities/discover-mcp-server.ts +1 -1
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +40 -1
- package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +49 -15
- package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +1 -1
- package/src/activities/execute-cursor/fetch-interceptor.ts +37 -16
- package/src/activities/execute-cursor/index.ts +58 -5
- package/src/activities/execute-cursor/message-translator.ts +15 -9
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +3 -2
- package/src/activities/execute-deep-agent/attachment-injector.ts +2 -2
- package/src/activities/execute-deep-agent/index.ts +10 -0
- package/src/activities/execute-deep-agent/setup.ts +34 -6
- package/src/client/stigmer-client.ts +7 -2
- package/src/payload-codecs.ts +12 -9
- package/src/runner-manager.ts +1 -1
- package/src/runner.ts +1 -1
- package/src/shared/__tests__/memory-retrieval.test.ts +310 -0
- package/src/shared/__tests__/skill-mount.test.ts +1 -1
- package/src/shared/__tests__/skill-writer.test.ts +1 -1
- package/src/shared/__tests__/zip-extract.test.ts +1 -1
- package/src/shared/memory-retrieval.ts +383 -0
- package/src/shared/runner-credential-keys.ts +2 -1
- package/src/shared/zip-extract.ts +1 -1
- package/dist/claimcheck/compressor.d.ts +0 -2
- package/dist/claimcheck/compressor.js +0 -8
- package/dist/claimcheck/compressor.js.map +0 -1
- package/dist/claimcheck/config.d.ts +0 -7
- package/dist/claimcheck/config.js +0 -10
- package/dist/claimcheck/config.js.map +0 -1
- package/dist/claimcheck/index.d.ts +0 -3
- package/dist/claimcheck/index.js +0 -4
- package/dist/claimcheck/index.js.map +0 -1
- package/dist/claimcheck/payload-codec.d.ts +0 -23
- package/dist/claimcheck/payload-codec.js +0 -105
- package/dist/claimcheck/payload-codec.js.map +0 -1
- package/dist/encryption/config.d.ts +0 -64
- package/dist/encryption/config.js +0 -113
- package/dist/encryption/config.js.map +0 -1
- package/dist/encryption/index.d.ts +0 -3
- package/dist/encryption/index.js +0 -3
- package/dist/encryption/index.js.map +0 -1
- package/dist/encryption/payload-codec.d.ts +0 -41
- package/dist/encryption/payload-codec.js +0 -130
- package/dist/encryption/payload-codec.js.map +0 -1
- package/dist/shared/zip-structure.d.ts +0 -61
- package/dist/shared/zip-structure.js +0 -128
- package/dist/shared/zip-structure.js.map +0 -1
- package/src/__test-utils__/zip-fixtures.ts +0 -206
- package/src/__tests__/claimcheck-codec.test.ts +0 -256
- package/src/__tests__/encryption-codec.test.ts +0 -287
- package/src/__tests__/fixtures/encrypted-payload-fixture.json +0 -15
- package/src/claimcheck/compressor.ts +0 -9
- package/src/claimcheck/config.ts +0 -20
- package/src/claimcheck/index.ts +0 -3
- package/src/claimcheck/payload-codec.ts +0 -139
- package/src/encryption/config.ts +0 -161
- package/src/encryption/index.ts +0 -3
- package/src/encryption/payload-codec.ts +0 -152
- package/src/shared/zip-structure.ts +0 -181
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Payload-encryption configuration (stigmer-cloud#227, stigmer#398).
|
|
3
|
-
*
|
|
4
|
-
* Two key sources, strict precedence:
|
|
5
|
-
*
|
|
6
|
-
* 1. Environment (STIGMER_PAYLOAD_ENCRYPTION_KEY(_ID)) — the operator's
|
|
7
|
-
* explicit choice: self-hosted deployments sharing one key with their
|
|
8
|
-
* server, and cloud sandboxes injected with the platform key. When the
|
|
9
|
-
* env key is set, bootstrap-delivered material is ignored entirely.
|
|
10
|
-
* 2. Bootstrap-delivered — server-managed per-identity keys handed to
|
|
11
|
-
* desktop-class runners by getRunnerBootstrapConfig. Held in memory
|
|
12
|
-
* only; persistence lives server-side, which is what makes Temporal
|
|
13
|
-
* replay work across runner restarts (every boot re-fetches the SAME
|
|
14
|
-
* key).
|
|
15
|
-
*
|
|
16
|
-
* Encryption is enabled iff a key is present from either source — the same
|
|
17
|
-
* enabled-iff-configured pattern as the claim-check codec. A malformed key
|
|
18
|
-
* fails the boot rather than silently running plaintext: an operator who set
|
|
19
|
-
* the key (or a server that minted one) intended history to be encrypted.
|
|
20
|
-
*
|
|
21
|
-
* Key rotation: payloads carry the id of the key that encrypted them.
|
|
22
|
-
* During a rotation window the previous key stays readable via the
|
|
23
|
-
* secondary pair while new payloads are written under the primary key.
|
|
24
|
-
* Workers capture keys at construction, so a rotated bootstrap key lands
|
|
25
|
-
* on the next runner boot — there is no live re-key.
|
|
26
|
-
*/
|
|
27
|
-
import { getRunnerSecret } from "../shared/runner-credential-store.js";
|
|
28
|
-
const KEY_ENV = "STIGMER_PAYLOAD_ENCRYPTION_KEY";
|
|
29
|
-
const KEY_ID_ENV = "STIGMER_PAYLOAD_ENCRYPTION_KEY_ID";
|
|
30
|
-
const SECONDARY_KEY_ENV = "STIGMER_PAYLOAD_ENCRYPTION_SECONDARY_KEY";
|
|
31
|
-
const SECONDARY_KEY_ID_ENV = "STIGMER_PAYLOAD_ENCRYPTION_SECONDARY_KEY_ID";
|
|
32
|
-
const AES_256_KEY_BYTES = 32;
|
|
33
|
-
/**
|
|
34
|
-
* Returns the encryption config, or undefined when encryption is not
|
|
35
|
-
* configured (the codec is then simply not installed).
|
|
36
|
-
*
|
|
37
|
-
* Source precedence: the env key wins outright; bootstrap-delivered
|
|
38
|
-
* material applies only when no env key is set (see the module doc).
|
|
39
|
-
*
|
|
40
|
-
* @throws when a key is present but malformed, or a key id is missing —
|
|
41
|
-
* key misconfiguration must stop the boot, not degrade to plaintext.
|
|
42
|
-
* This applies equally to bootstrap material: a server that hands out
|
|
43
|
-
* a bad key or omits its id has broken the protocol contract, and
|
|
44
|
-
* running plaintext against a server that manages keys would silently
|
|
45
|
-
* defeat the feature.
|
|
46
|
-
*/
|
|
47
|
-
export function loadPayloadEncryptionConfig(bootstrap) {
|
|
48
|
-
// Key VALUES resolve through the credential store (the #508 boot capture
|
|
49
|
-
// moves them out of process.env — agent shells must not read them); the
|
|
50
|
-
// *_KEY_ID companions are rotation bookkeeping, not secrets, and stay
|
|
51
|
-
// plain env reads.
|
|
52
|
-
const rawKey = getRunnerSecret(KEY_ENV);
|
|
53
|
-
if (rawKey) {
|
|
54
|
-
const primary = {
|
|
55
|
-
keyId: requireKeyId(KEY_ID_ENV),
|
|
56
|
-
key: parseKey(rawKey, KEY_ENV),
|
|
57
|
-
};
|
|
58
|
-
const rawSecondary = getRunnerSecret(SECONDARY_KEY_ENV);
|
|
59
|
-
const secondary = rawSecondary
|
|
60
|
-
? {
|
|
61
|
-
keyId: requireKeyId(SECONDARY_KEY_ID_ENV),
|
|
62
|
-
key: parseKey(rawSecondary, SECONDARY_KEY_ENV),
|
|
63
|
-
}
|
|
64
|
-
: undefined;
|
|
65
|
-
return { primary, secondary };
|
|
66
|
-
}
|
|
67
|
-
if (bootstrap?.key) {
|
|
68
|
-
const primary = {
|
|
69
|
-
keyId: requireBootstrapKeyId(bootstrap.keyId, "payload_encryption_key_id"),
|
|
70
|
-
key: parseKey(bootstrap.key, "bootstrap payload_encryption_key"),
|
|
71
|
-
};
|
|
72
|
-
const secondary = bootstrap.secondaryKey
|
|
73
|
-
? {
|
|
74
|
-
keyId: requireBootstrapKeyId(bootstrap.secondaryKeyId, "payload_encryption_secondary_key_id"),
|
|
75
|
-
key: parseKey(bootstrap.secondaryKey, "bootstrap payload_encryption_secondary_key"),
|
|
76
|
-
}
|
|
77
|
-
: undefined;
|
|
78
|
-
return { primary, secondary };
|
|
79
|
-
}
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
function requireKeyId(envName) {
|
|
83
|
-
const keyId = process.env[envName];
|
|
84
|
-
// An explicit id is required (no default): during rotation two keys
|
|
85
|
-
// coexist, and payloads must name which one encrypted them.
|
|
86
|
-
if (!keyId) {
|
|
87
|
-
throw new Error(`Payload encryption misconfigured: ${envName} is required when the ` +
|
|
88
|
-
`corresponding key is set`);
|
|
89
|
-
}
|
|
90
|
-
return keyId;
|
|
91
|
-
}
|
|
92
|
-
function requireBootstrapKeyId(keyId, fieldName) {
|
|
93
|
-
if (!keyId) {
|
|
94
|
-
throw new Error(`Runner bootstrap returned a payload encryption key without its ${fieldName} — ` +
|
|
95
|
-
`refusing to encrypt under an unidentified key (server contract violation)`);
|
|
96
|
-
}
|
|
97
|
-
return keyId;
|
|
98
|
-
}
|
|
99
|
-
function parseKey(rawBase64, envName) {
|
|
100
|
-
let key;
|
|
101
|
-
try {
|
|
102
|
-
key = Buffer.from(rawBase64, "base64");
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
throw new Error(`Payload encryption misconfigured: ${envName} is not valid base64`);
|
|
106
|
-
}
|
|
107
|
-
if (key.length !== AES_256_KEY_BYTES) {
|
|
108
|
-
throw new Error(`Payload encryption misconfigured: ${envName} must decode to ` +
|
|
109
|
-
`${AES_256_KEY_BYTES} bytes (AES-256), got ${key.length}`);
|
|
110
|
-
}
|
|
111
|
-
return key;
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=config.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/encryption/config.ts"],"names":[],"mappings":"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"}
|
package/dist/encryption/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/encryption/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Temporal PayloadCodec that encrypts payloads at rest in workflow
|
|
3
|
-
* history (stigmer-cloud#227).
|
|
4
|
-
*
|
|
5
|
-
* Why: the workflow engine runs inside the Temporal deterministic
|
|
6
|
-
* sandbox, so decrypted execution-context values cross the history
|
|
7
|
-
* boundary in many places — the hydrate activity result, the runtime
|
|
8
|
-
* env passed as an input to every per-task activity, and expression
|
|
9
|
-
* results recorded as local-activity markers. Encrypting at the payload
|
|
10
|
-
* codec layer closes the entire class with one mechanism instead of
|
|
11
|
-
* chasing each crossing.
|
|
12
|
-
*
|
|
13
|
-
* Envelope (cross-SDK contract — the Java decode-only codec in
|
|
14
|
-
* stigmer-cloud's temporal-starter must match it byte-for-byte, pinned
|
|
15
|
-
* by the conformance fixture in __tests__/fixtures/):
|
|
16
|
-
*
|
|
17
|
-
* metadata: encoding = "binary/encrypted"
|
|
18
|
-
* encryption-key-id = <key id that encrypted this payload>
|
|
19
|
-
* data: iv (12 bytes) ‖ AES-256-GCM(ciphertext ‖ tag (16 bytes))
|
|
20
|
-
*
|
|
21
|
-
* The plaintext is the serialized ORIGINAL Payload proto (metadata AND
|
|
22
|
-
* data), so decode restores the payload exactly — including its
|
|
23
|
-
* original encoding — with no side channel.
|
|
24
|
-
*
|
|
25
|
-
* Decode passes through payloads it did not encode. This is what keeps
|
|
26
|
-
* plaintext signals from the Java/Go orchestrators and pre-rollout
|
|
27
|
-
* in-flight histories working with zero migration. Everything else
|
|
28
|
-
* fails closed: unknown key id, missing key id, and ciphertext tampering
|
|
29
|
-
* all throw rather than surfacing bogus payloads.
|
|
30
|
-
*/
|
|
31
|
-
import type { Payload, PayloadCodec } from "@temporalio/common";
|
|
32
|
-
import type { PayloadEncryptionConfig } from "./config.js";
|
|
33
|
-
export declare class EncryptionPayloadCodec implements PayloadCodec {
|
|
34
|
-
private readonly config;
|
|
35
|
-
private readonly decryptKeysById;
|
|
36
|
-
constructor(config: PayloadEncryptionConfig);
|
|
37
|
-
encode(payloads: Payload[]): Promise<Payload[]>;
|
|
38
|
-
decode(payloads: Payload[]): Promise<Payload[]>;
|
|
39
|
-
private encodePayload;
|
|
40
|
-
private decodePayload;
|
|
41
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Temporal PayloadCodec that encrypts payloads at rest in workflow
|
|
3
|
-
* history (stigmer-cloud#227).
|
|
4
|
-
*
|
|
5
|
-
* Why: the workflow engine runs inside the Temporal deterministic
|
|
6
|
-
* sandbox, so decrypted execution-context values cross the history
|
|
7
|
-
* boundary in many places — the hydrate activity result, the runtime
|
|
8
|
-
* env passed as an input to every per-task activity, and expression
|
|
9
|
-
* results recorded as local-activity markers. Encrypting at the payload
|
|
10
|
-
* codec layer closes the entire class with one mechanism instead of
|
|
11
|
-
* chasing each crossing.
|
|
12
|
-
*
|
|
13
|
-
* Envelope (cross-SDK contract — the Java decode-only codec in
|
|
14
|
-
* stigmer-cloud's temporal-starter must match it byte-for-byte, pinned
|
|
15
|
-
* by the conformance fixture in __tests__/fixtures/):
|
|
16
|
-
*
|
|
17
|
-
* metadata: encoding = "binary/encrypted"
|
|
18
|
-
* encryption-key-id = <key id that encrypted this payload>
|
|
19
|
-
* data: iv (12 bytes) ‖ AES-256-GCM(ciphertext ‖ tag (16 bytes))
|
|
20
|
-
*
|
|
21
|
-
* The plaintext is the serialized ORIGINAL Payload proto (metadata AND
|
|
22
|
-
* data), so decode restores the payload exactly — including its
|
|
23
|
-
* original encoding — with no side channel.
|
|
24
|
-
*
|
|
25
|
-
* Decode passes through payloads it did not encode. This is what keeps
|
|
26
|
-
* plaintext signals from the Java/Go orchestrators and pre-rollout
|
|
27
|
-
* in-flight histories working with zero migration. Everything else
|
|
28
|
-
* fails closed: unknown key id, missing key id, and ciphertext tampering
|
|
29
|
-
* all throw rather than surfacing bogus payloads.
|
|
30
|
-
*/
|
|
31
|
-
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
32
|
-
// Default-import + destructure, NOT `import { temporal } from …`:
|
|
33
|
-
// @temporalio/proto is CommonJS and its `temporal` export defeats Node's
|
|
34
|
-
// cjs-module-lexer named-export detection, so the named form loads under
|
|
35
|
-
// tsx/vitest (their interop is looser) but crashes plain `node dist/main.js`
|
|
36
|
-
// at startup with "Named export 'temporal' not found" (stigmer/stigmer#399
|
|
37
|
-
// boot regression). Pinned by scripts/verify-dist-boot.mjs in CI.
|
|
38
|
-
import proto from "@temporalio/proto";
|
|
39
|
-
const { temporal } = proto;
|
|
40
|
-
const ENCODING_METADATA_KEY = "encoding";
|
|
41
|
-
const ENCRYPTED_ENCODING_VALUE = "binary/encrypted";
|
|
42
|
-
const KEY_ID_METADATA_KEY = "encryption-key-id";
|
|
43
|
-
/** AES-GCM parameters shared with the Java implementation. */
|
|
44
|
-
const IV_BYTES = 12;
|
|
45
|
-
const AUTH_TAG_BYTES = 16;
|
|
46
|
-
export class EncryptionPayloadCodec {
|
|
47
|
-
config;
|
|
48
|
-
decryptKeysById;
|
|
49
|
-
constructor(config) {
|
|
50
|
-
this.config = config;
|
|
51
|
-
this.decryptKeysById = new Map([[config.primary.keyId, config.primary.key]]);
|
|
52
|
-
if (config.secondary) {
|
|
53
|
-
this.decryptKeysById.set(config.secondary.keyId, config.secondary.key);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async encode(payloads) {
|
|
57
|
-
return payloads.map((p) => this.encodePayload(p));
|
|
58
|
-
}
|
|
59
|
-
async decode(payloads) {
|
|
60
|
-
return payloads.map((p) => this.decodePayload(p));
|
|
61
|
-
}
|
|
62
|
-
encodePayload(payload) {
|
|
63
|
-
// Data-less payloads (binary/null from void results) stay as-is:
|
|
64
|
-
// there is nothing to protect, and the cross-language parents that
|
|
65
|
-
// await our workflows as void must be able to read them without a key.
|
|
66
|
-
if (!payload.data || payload.data.length === 0) {
|
|
67
|
-
return payload;
|
|
68
|
-
}
|
|
69
|
-
const plaintext = temporal.api.common.v1.Payload.encode(payload).finish();
|
|
70
|
-
return {
|
|
71
|
-
metadata: {
|
|
72
|
-
[ENCODING_METADATA_KEY]: Buffer.from(ENCRYPTED_ENCODING_VALUE),
|
|
73
|
-
[KEY_ID_METADATA_KEY]: Buffer.from(this.config.primary.keyId),
|
|
74
|
-
},
|
|
75
|
-
data: encrypt(plaintext, this.config.primary),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
decodePayload(payload) {
|
|
79
|
-
if (!isEncryptedPayload(payload)) {
|
|
80
|
-
return payload;
|
|
81
|
-
}
|
|
82
|
-
const keyIdBytes = payload.metadata?.[KEY_ID_METADATA_KEY];
|
|
83
|
-
if (!keyIdBytes) {
|
|
84
|
-
throw new Error("Encrypted payload is missing its encryption-key-id metadata — refusing to decode");
|
|
85
|
-
}
|
|
86
|
-
const keyId = Buffer.from(keyIdBytes).toString("utf-8");
|
|
87
|
-
const key = this.decryptKeysById.get(keyId);
|
|
88
|
-
if (!key) {
|
|
89
|
-
throw new Error(`Encrypted payload uses unknown key id '${keyId}' — configure it as the ` +
|
|
90
|
-
`primary or secondary payload encryption key (rotation window?)`);
|
|
91
|
-
}
|
|
92
|
-
const plaintext = decrypt(payload.data, key, keyId);
|
|
93
|
-
return temporal.api.common.v1.Payload.decode(plaintext);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function isEncryptedPayload(payload) {
|
|
97
|
-
const encoding = payload.metadata?.[ENCODING_METADATA_KEY];
|
|
98
|
-
if (!encoding)
|
|
99
|
-
return false;
|
|
100
|
-
return Buffer.from(encoding).toString("utf-8") === ENCRYPTED_ENCODING_VALUE;
|
|
101
|
-
}
|
|
102
|
-
function encrypt(plaintext, key) {
|
|
103
|
-
const iv = randomBytes(IV_BYTES);
|
|
104
|
-
const cipher = createCipheriv("aes-256-gcm", key.key, iv);
|
|
105
|
-
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
106
|
-
// Layout iv ‖ ciphertext ‖ tag matches Java's AES/GCM/NoPadding, whose
|
|
107
|
-
// doFinal() output is ciphertext ‖ tag.
|
|
108
|
-
return Buffer.concat([iv, ciphertext, cipher.getAuthTag()]);
|
|
109
|
-
}
|
|
110
|
-
function decrypt(data, key, keyId) {
|
|
111
|
-
if (data.length < IV_BYTES + AUTH_TAG_BYTES) {
|
|
112
|
-
throw new Error(`Encrypted payload under key id '${keyId}' is truncated (${data.length} bytes)`);
|
|
113
|
-
}
|
|
114
|
-
const buf = Buffer.from(data);
|
|
115
|
-
const iv = buf.subarray(0, IV_BYTES);
|
|
116
|
-
const ciphertext = buf.subarray(IV_BYTES, buf.length - AUTH_TAG_BYTES);
|
|
117
|
-
const tag = buf.subarray(buf.length - AUTH_TAG_BYTES);
|
|
118
|
-
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
119
|
-
decipher.setAuthTag(tag);
|
|
120
|
-
try {
|
|
121
|
-
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
// GCM auth failure: tampered ciphertext or a key that does not match
|
|
125
|
-
// its advertised id. Never surface partially decrypted bytes.
|
|
126
|
-
throw new Error(`Failed to decrypt payload under key id '${keyId}' — ciphertext is ` +
|
|
127
|
-
`corrupt or the configured key does not match`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=payload-codec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"payload-codec.js","sourceRoot":"","sources":["../../src/encryption/payload-codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,61 +0,0 @@
|
|
|
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[];
|
|
@@ -1,128 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"zip-structure.js","sourceRoot":"","sources":["../../src/shared/zip-structure.ts"],"names":[],"mappings":"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"}
|