@a5c-ai/comm-adapter 6.0.2 → 6.0.3-staging.00bdadd67cf1

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.
Files changed (43) hide show
  1. package/dist/__tests__/e2e-gate3-credential.e2e.test.d.ts +1 -0
  2. package/dist/__tests__/e2e-gate3-credential.e2e.test.d.ts.map +1 -0
  3. package/dist/__tests__/e2e-gate3-credential.e2e.test.js +191 -0
  4. package/dist/__tests__/e2e-gate3-credential.e2e.test.js.map +1 -0
  5. package/dist/__tests__/policy-credential-gate.test.d.ts +1 -0
  6. package/dist/__tests__/policy-credential-gate.test.d.ts.map +1 -0
  7. package/dist/__tests__/policy-credential-gate.test.js +327 -0
  8. package/dist/__tests__/policy-credential-gate.test.js.map +1 -0
  9. package/dist/__tests__/policy-pretooluse-gate.test.d.ts +1 -0
  10. package/dist/__tests__/policy-pretooluse-gate.test.d.ts.map +1 -0
  11. package/dist/__tests__/policy-pretooluse-gate.test.js +154 -0
  12. package/dist/__tests__/policy-pretooluse-gate.test.js.map +1 -0
  13. package/dist/__tests__/policy-spawn-gate.test.d.ts +1 -0
  14. package/dist/__tests__/policy-spawn-gate.test.d.ts.map +1 -0
  15. package/dist/__tests__/policy-spawn-gate.test.js +240 -0
  16. package/dist/__tests__/policy-spawn-gate.test.js.map +1 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/policy-credential-gate.d.ts +86 -0
  22. package/dist/policy-credential-gate.d.ts.map +1 -0
  23. package/dist/policy-credential-gate.js +141 -0
  24. package/dist/policy-credential-gate.js.map +1 -0
  25. package/dist/policy-pretooluse-hook.d.ts +55 -0
  26. package/dist/policy-pretooluse-hook.d.ts.map +1 -0
  27. package/dist/policy-pretooluse-hook.js +80 -0
  28. package/dist/policy-pretooluse-hook.js.map +1 -0
  29. package/dist/policy-spawn-gate.d.ts +82 -0
  30. package/dist/policy-spawn-gate.d.ts.map +1 -0
  31. package/dist/policy-spawn-gate.js +115 -0
  32. package/dist/policy-spawn-gate.js.map +1 -0
  33. package/dist/run-options.d.ts +40 -0
  34. package/dist/run-options.d.ts.map +1 -1
  35. package/dist/run-options.js.map +1 -1
  36. package/dist/spawn-invocation.d.ts +73 -1
  37. package/dist/spawn-invocation.d.ts.map +1 -1
  38. package/dist/spawn-invocation.js +190 -11
  39. package/dist/spawn-invocation.js.map +1 -1
  40. package/dist/spawn-runner.d.ts.map +1 -1
  41. package/dist/spawn-runner.js +41 -1
  42. package/dist/spawn-runner.js.map +1 -1
  43. package/package.json +6 -4
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-gate3-credential.e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/e2e-gate3-credential.e2e.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Milestone E — END-TO-END: GATE 3 credential backstop on a REAL spawn (§9.3 / AC-23a /
3
+ * AC-40 / AC-49 / AC-50). Carry-forward from Milestone D, exercised end-to-end here.
4
+ *
5
+ * Frozen tests authored STRICTLY from docs/design/proof-based-policy-enforcement.md. GATE 3
6
+ * is the LAST point before exec (§9.3): with a VALID CommandAuthorization whose
7
+ * credentialScope matches, the scoped credential IS delivered by its channel; with
8
+ * none/invalid, the scoped credential is NOT delivered (env dropped, `-v` mount omitted,
9
+ * secret/serviceaccount ref stripped), and if required the spawn is denied.
10
+ *
11
+ * This drives the REAL wired spawn path: it constructs a real signed authorization + a real
12
+ * trusted credential→scope source, builds a `Gate3Options`, and feeds it into the SHIPPED
13
+ * `buildInvocationCommand` (docker / k8s), asserting the credential is (or is not) present in
14
+ * the emitted invocation across env AND mount/serviceaccount channels — the policyGate3 /
15
+ * scopedEnvKeys surface. Uses real Ed25519 keys + the shipped envelope primitives; no mocks.
16
+ *
17
+ * MODULE PATHS: shipped primitives import from `@a5c-ai/policy-adapter` + `@a5c-ai/genty-core`;
18
+ * the GATE-3 spawn surface imports from `../spawn-invocation.js`. Resolution may fail until
19
+ * the wiring lands — expected. NO syntax errors.
20
+ */
21
+ import { describe, it, expect } from 'vitest';
22
+ import { createHash } from 'node:crypto';
23
+ import { createKeyPair, signPayload } from '@a5c-ai/trust-core';
24
+ import { argsHash as computeArgsHash, commandHash as computeCommandHash, canonicalizeCredentialIdentity, } from '@a5c-ai/policy-adapter';
25
+ import { buildInvocationCommand, CredentialGateDenied } from '../spawn-invocation.js';
26
+ const sha256 = (s) => createHash('sha256').update(s).digest('hex');
27
+ const CA_FIELDS = [
28
+ 'payloadType',
29
+ 'policyId',
30
+ 'policyDocHash',
31
+ 'configEpoch',
32
+ 'matchedChainId',
33
+ 'toolName',
34
+ 'toolCallId',
35
+ 'commandHash',
36
+ 'argsHash',
37
+ 'credentialScope',
38
+ 'evidenceFingerprints',
39
+ 'evidenceEnvelopeHashes',
40
+ 'evidenceStepBindings',
41
+ 'issuedAt',
42
+ 'expiresAt',
43
+ ];
44
+ const TOOL = 'Bash';
45
+ const TOOL_CALL_ID = 'call_aws_rm';
46
+ const COMMAND = 'aws s3 rm s3://prod-bucket/secret';
47
+ const ARGS = { command: COMMAND };
48
+ const CRED_SCOPE = 'aws:prod:s3-rw';
49
+ const POLICY_DOC_HASH = sha256('policy-doc-bytes');
50
+ const CONFIG_EPOCH = 5;
51
+ const MIN_EPOCH = 5;
52
+ // The trusted out-of-agent credential→scope source (AC-40 / AC-40a / AC-55). Keyed by the
53
+ // COLLISION-RESISTANT canonical identity (sha256 of the stable secret-store id). Two aliases
54
+ // (ARN + key-id) of ONE physical credential resolve to one identity → one scope (AC-55).
55
+ const STABLE_ID = 'kms:key/prod-s3-rw';
56
+ const IDENTITY = canonicalizeCredentialIdentity(STABLE_ID);
57
+ const CRED_ALIAS = 'arn:aws:kms:us-east-1:123:key/prod-s3-rw';
58
+ const CRED_SOURCE = {
59
+ aliasMap: {
60
+ [CRED_ALIAS]: STABLE_ID,
61
+ 'key-id:prod-s3-rw': STABLE_ID,
62
+ },
63
+ scopeByIdentity: { [IDENTITY]: CRED_SCOPE },
64
+ ambiguousIdentities: [],
65
+ };
66
+ const ISSUER = createKeyPair();
67
+ const ISSUER_ROOT = {
68
+ fingerprint: ISSUER.fingerprint,
69
+ kind: 'engine',
70
+ publicKey: ISSUER.publicKey,
71
+ label: 'policy-adapter-issuer',
72
+ };
73
+ /**
74
+ * A REAL, signed CommandAuthorization bound to the exact tool call + command + scope.
75
+ * `issuedAt` is back-dated a few seconds so the gate's `now >= issuedAt` bound holds even
76
+ * when the gate's `now` was captured slightly before this envelope was signed (avoids a
77
+ * millisecond-boundary flake); `expiresAt` stays well in the future.
78
+ */
79
+ function signAuthorization(over = {}) {
80
+ const now = Date.now();
81
+ const payload = {
82
+ payloadType: 'command-authorization',
83
+ policyId: 'aws-prod-write',
84
+ policyDocHash: POLICY_DOC_HASH,
85
+ configEpoch: CONFIG_EPOCH,
86
+ matchedChainId: 'human-plus-opus',
87
+ toolName: TOOL,
88
+ toolCallId: TOOL_CALL_ID,
89
+ commandHash: computeCommandHash(COMMAND),
90
+ argsHash: computeArgsHash(ARGS),
91
+ credentialScope: CRED_SCOPE,
92
+ evidenceFingerprints: ['fp-human', 'fp-proxy'],
93
+ evidenceEnvelopeHashes: [sha256('human-env'), sha256('model-env')],
94
+ evidenceStepBindings: [
95
+ { stepIndex: 0, requiredKind: 'human-approval', envelopeHash: sha256('human-env') },
96
+ { stepIndex: 1, requiredKind: 'model-decision', envelopeHash: sha256('model-env') },
97
+ ],
98
+ issuedAt: new Date(now - 5_000).toISOString(),
99
+ expiresAt: new Date(now + 120_000).toISOString(),
100
+ ...over,
101
+ };
102
+ return signPayload(ISSUER.privateKey, ISSUER.fingerprint, payload, CA_FIELDS);
103
+ }
104
+ /** Return a resolver that always yields the SAME pre-signed authorization (stable issuedAt). */
105
+ function constant(auth) {
106
+ return () => auth;
107
+ }
108
+ /** A Gate3Options that gates the scoped env var + a scoped docker `-v` mount. */
109
+ function makeGate3(resolveAuthorization, scoped = {}) {
110
+ return {
111
+ issuerRoots: [ISSUER_ROOT],
112
+ policyDocHash: POLICY_DOC_HASH,
113
+ currentConfigEpoch: CONFIG_EPOCH,
114
+ minEpochFloor: MIN_EPOCH,
115
+ now: Date.now(),
116
+ toolName: TOOL,
117
+ toolCallId: TOOL_CALL_ID,
118
+ command: COMMAND,
119
+ args: ARGS,
120
+ credentialSource: CRED_SOURCE,
121
+ resolveAuthorization,
122
+ scopedEnvKeys: scoped.scopedEnvKeys ?? { AWS_SESSION_TOKEN: { alias: CRED_ALIAS } },
123
+ ...(scoped.scopedMounts ? { scopedMounts: scoped.scopedMounts } : {}),
124
+ ...(scoped.scopedServiceAccount ? { scopedServiceAccount: scoped.scopedServiceAccount } : {}),
125
+ };
126
+ }
127
+ /** A fresh mutable SpawnArgs per call (avoids readonly-array assignability issues). */
128
+ function spawnArgs(env = { AWS_SESSION_TOKEN: 'the-secret', PATH: '/usr/bin' }) {
129
+ return {
130
+ command: 'claude',
131
+ args: ['--print', 'hi'],
132
+ env: { ...env },
133
+ cwd: '/repo',
134
+ usePty: false,
135
+ };
136
+ }
137
+ describe('Milestone E — GATE 3 credential backstop on a real spawn (AC-23a / AC-40 / AC-50)', () => {
138
+ it('ALLOW path: a VALID authorization → the scoped credential IS injected into the docker -e channel', () => {
139
+ const gate3 = makeGate3(constant(signAuthorization()));
140
+ const out = buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', gate3);
141
+ // The scoped credential is delivered.
142
+ expect(out.args).toContain('AWS_SESSION_TOKEN=the-secret');
143
+ // The ordinary (non-credential) env var still passes through.
144
+ expect(out.args).toContain('PATH=/usr/bin');
145
+ });
146
+ it('DENY (env): NO authorization → the scoped env credential is NOT injected (channel omitted)', () => {
147
+ const gate3 = makeGate3(() => undefined);
148
+ const out = buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', gate3);
149
+ expect(out.args).not.toContain('AWS_SESSION_TOKEN=the-secret');
150
+ // Non-credential env passes through — only the scoped credential is dropped.
151
+ expect(out.args).toContain('PATH=/usr/bin');
152
+ });
153
+ it('DENY (env): an authorization for a DIFFERENT scope → the scoped env credential is NOT injected', () => {
154
+ // The authorization binds a broader/other scope than the trusted source resolves for the
155
+ // credential (AC-40: the agent cannot relabel a broad credential as the narrow scope).
156
+ const gate3 = makeGate3(constant(signAuthorization({ credentialScope: 'aws:prod:other' })));
157
+ const out = buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', gate3);
158
+ expect(out.args).not.toContain('AWS_SESSION_TOKEN=the-secret');
159
+ });
160
+ it('DENY (env): a TAMPERED command after issuance → hash mismatch → scoped credential NOT injected (TOCTOU, AC-32)', () => {
161
+ // The authorization binds COMMAND, but the gate context now spawns a DIFFERENT command.
162
+ const gate3 = { ...makeGate3(constant(signAuthorization())), command: 'aws s3 rm s3://prod-bucket/OTHER' };
163
+ const out = buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', gate3);
164
+ expect(out.args).not.toContain('AWS_SESSION_TOKEN=the-secret');
165
+ });
166
+ it('ALLOW + DENY (mount, AC-50): a scoped docker -v mount is injected with a valid auth and OMITTED without', () => {
167
+ const MOUNT = '/host/creds:/creds:ro';
168
+ const scoped = { scopedMounts: { [MOUNT]: { alias: CRED_ALIAS } } };
169
+ const allowed = buildInvocationCommand({ mode: 'docker', image: 'img:1', volumes: [MOUNT] }, spawnArgs({}), 'claude', makeGate3(constant(signAuthorization()), scoped));
170
+ expect(allowed.args).toContain(MOUNT);
171
+ const denied = buildInvocationCommand({ mode: 'docker', image: 'img:1', volumes: [MOUNT] }, spawnArgs({}), 'claude', makeGate3(() => undefined, scoped));
172
+ expect(denied.args).not.toContain(MOUNT);
173
+ });
174
+ it('DENY (required): a REQUIRED scoped credential with no authorization → the spawn is DENIED (CredentialGateDenied)', () => {
175
+ const gate3 = makeGate3(() => undefined, { scopedEnvKeys: { AWS_SESSION_TOKEN: { alias: CRED_ALIAS, required: true } } });
176
+ expect(() => buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', gate3)).toThrow(CredentialGateDenied);
177
+ });
178
+ it('AC-40 (untrusted alias): a credential whose alias is NOT in the trusted source → NOT injected (fail closed)', () => {
179
+ const gate3 = makeGate3(constant(signAuthorization()), {
180
+ scopedEnvKeys: { AWS_SESSION_TOKEN: { alias: 'agent-supplied-unknown-alias' } },
181
+ });
182
+ const out = buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', gate3);
183
+ expect(out.args).not.toContain('AWS_SESSION_TOKEN=the-secret');
184
+ });
185
+ it('back-compat: with NO gate3 (anchor unpinned) every channel emits unchanged', () => {
186
+ const out = buildInvocationCommand({ mode: 'docker', image: 'img:1' }, spawnArgs(), 'claude', undefined);
187
+ // Without a gate, the scoped credential is NOT dropped (no enforcement, pre-Milestone-D posture).
188
+ expect(out.args).toContain('AWS_SESSION_TOKEN=the-secret');
189
+ });
190
+ });
191
+ //# sourceMappingURL=e2e-gate3-credential.e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-gate3-credential.e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/e2e-gate3-credential.e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-credential-gate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/policy-credential-gate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Milestone D — GATE 3 (credential injection backstop, spawn-invocation).
3
+ *
4
+ * Frozen tests authored strictly from docs/design/proof-based-policy-enforcement.md:
5
+ * §9.3 / AC-23a — a credential about to be delivered by ANY channel spawn-invocation.ts
6
+ * controls (docker -e env, docker -v mount, ssh K=V, k8s env / --env,
7
+ * k8s secret/serviceaccount) is tagged with a credentialScope from the
8
+ * trusted out-of-agent source; before delivery GATE 3 requires a valid
9
+ * CommandAuthorization whose credentialScope matches. NO valid
10
+ * authorization → the scoped credential is NOT delivered (env dropped,
11
+ * -v mount omitted, secret/serviceaccount ref stripped), and if the
12
+ * policy marks it required, the spawn is denied. GATE 3 RECOMPUTES
13
+ * argsHash/commandHash (the last point before exec) and re-checks them.
14
+ * §9.3 / AC-40 — the credential→scope mapping comes from a source OUTSIDE the agent
15
+ * process; the agent MUST NOT be able to assert a narrower scope for a
16
+ * broader credential. No scope in the trusted source for a scoped cred →
17
+ * deny (fail closed), never inject untagged.
18
+ * §9.3 / AC-40a — the credential IDENTITY is collision-resistant (sha256 of the stable
19
+ * secret-store id), never the value prefix / env-var name / agent alias;
20
+ * an ambiguous or absent identity → deny (never guess, never the narrower).
21
+ * §9.3 / AC-50 — GATE 3 mediates env + docker -v mount + k8s secret/serviceaccount;
22
+ * substrate-delivered credentials (IMDS/IRSA, pre-existing mounts, image
23
+ * creds) are a BOUNDED, WARNED non-goal — the evaluator refuses to claim
24
+ * a GATE-3 backstop for such an action.
25
+ * §14 / AC-55 — a KMS credential referenced by two aliases (ARN + key-id) canonicalizes
26
+ * to ONE identity → one scope (not falsely denied).
27
+ *
28
+ * MODULE PATHS: shipped primitives import from `@a5c-ai/policy-adapter`; the intended
29
+ * Milestone-D credential gate imports from `../policy-credential-gate.js`. Resolution may
30
+ * fail until Milestone D lands — expected. NO syntax errors.
31
+ */
32
+ import { describe, it, expect } from 'vitest';
33
+ import { createHash } from 'node:crypto';
34
+ import { createKeyPair, signPayload } from '@a5c-ai/trust-core';
35
+ import { argsHash as computeArgsHash, commandHash as computeCommandHash, canonicalizeCredentialIdentity, } from '@a5c-ai/policy-adapter';
36
+ // Intended Milestone-D GATE-3 credential-injection gate (§9.3, AC-23a/AC-40/AC-50).
37
+ import { gateCredentialInjection } from '../policy-credential-gate.js';
38
+ const sha256 = (s) => createHash('sha256').update(s).digest('hex');
39
+ const CA_FIELDS = [
40
+ 'payloadType',
41
+ 'policyId',
42
+ 'policyDocHash',
43
+ 'configEpoch',
44
+ 'matchedChainId',
45
+ 'toolName',
46
+ 'toolCallId',
47
+ 'commandHash',
48
+ 'argsHash',
49
+ 'credentialScope',
50
+ 'evidenceFingerprints',
51
+ 'evidenceEnvelopeHashes',
52
+ 'evidenceStepBindings',
53
+ 'issuedAt',
54
+ 'expiresAt',
55
+ ];
56
+ const ISSUER = createKeyPair();
57
+ const ISSUER_ROOT = {
58
+ fingerprint: ISSUER.fingerprint,
59
+ kind: 'engine',
60
+ publicKey: ISSUER.publicKey,
61
+ label: 'issuer',
62
+ };
63
+ const COMMAND = 'aws s3 cp a b';
64
+ const ARGS = { command: COMMAND };
65
+ const CRED_SCOPE = 'aws:prod:s3-ro';
66
+ const POLICY_DOC_HASH = sha256('policy-doc-bytes');
67
+ const CONFIG_EPOCH = 5;
68
+ // The trusted out-of-agent credential→scope source (AC-40/AC-40a/AC-55). Keyed by the
69
+ // COLLISION-RESISTANT canonical identity (sha256 of the stable secret-store id). Two
70
+ // aliases (ARN + key-id) of ONE credential resolve to one identity → one scope (AC-55).
71
+ const STABLE_ID = 'kms:key/prod-s3-ro';
72
+ const IDENTITY = canonicalizeCredentialIdentity(STABLE_ID);
73
+ const CRED_SOURCE = {
74
+ aliasMap: {
75
+ 'arn:aws:kms:us-east-1:123:key/prod-s3-ro': STABLE_ID,
76
+ 'key-id:prod-s3-ro': STABLE_ID,
77
+ },
78
+ scopeByIdentity: { [IDENTITY]: CRED_SCOPE },
79
+ ambiguousIdentities: [],
80
+ };
81
+ function makeCA(overrides = {}) {
82
+ const now = Date.now();
83
+ return {
84
+ payloadType: 'command-authorization',
85
+ policyId: 'aws-prod-write',
86
+ policyDocHash: POLICY_DOC_HASH,
87
+ configEpoch: CONFIG_EPOCH,
88
+ matchedChainId: 'human-plus-opus',
89
+ toolName: 'Bash',
90
+ toolCallId: 'call_A',
91
+ commandHash: computeCommandHash(COMMAND),
92
+ argsHash: computeArgsHash(ARGS),
93
+ credentialScope: CRED_SCOPE,
94
+ evidenceFingerprints: ['fp-human', 'fp-proxy'],
95
+ evidenceEnvelopeHashes: [sha256('human-env'), sha256('model-env')],
96
+ evidenceStepBindings: [
97
+ { stepIndex: 0, requiredKind: 'human-approval', envelopeHash: sha256('human-env') },
98
+ { stepIndex: 1, requiredKind: 'model-decision', envelopeHash: sha256('model-env') },
99
+ ],
100
+ issuedAt: new Date(now).toISOString(),
101
+ expiresAt: new Date(now + 120_000).toISOString(),
102
+ ...overrides,
103
+ };
104
+ }
105
+ function signCA(payload) {
106
+ return signPayload(ISSUER.privateKey, ISSUER.fingerprint, payload, CA_FIELDS);
107
+ }
108
+ function gateInput(opts) {
109
+ return {
110
+ issuerRoots: [ISSUER_ROOT],
111
+ policyDocHash: POLICY_DOC_HASH,
112
+ currentConfigEpoch: CONFIG_EPOCH,
113
+ minEpochFloor: CONFIG_EPOCH,
114
+ now: Date.now(),
115
+ toolName: 'Bash',
116
+ toolCallId: 'call_A',
117
+ // GATE 3 is the LAST point before exec — it recomputes these from the exact spawn.
118
+ command: opts.command ?? COMMAND,
119
+ args: opts.args ?? ARGS,
120
+ credentialSource: opts.source ?? CRED_SOURCE,
121
+ credentials: opts.credentials,
122
+ resolveAuthorization: () => opts.authorization,
123
+ };
124
+ }
125
+ describe('Milestone D — GATE 3 credential injection backstop (AC-23a, AC-40, AC-40a, AC-50, AC-55)', () => {
126
+ it('AC-23a: a scoped docker -e ENV credential WITH a matching authorization IS injected', () => {
127
+ const result = gateCredentialInjection(gateInput({
128
+ authorization: signCA(makeCA()),
129
+ credentials: [
130
+ { name: 'AWS_KEY', value: 'secret', alias: 'key-id:prod-s3-ro', channel: 'docker-env' },
131
+ ],
132
+ }));
133
+ expect(result.denied).toBe(false);
134
+ expect(result.injected.map((c) => c.name)).toContain('AWS_KEY');
135
+ expect(result.dropped).toHaveLength(0);
136
+ });
137
+ it('AC-23a: a scoped docker -e ENV credential WITHOUT authorization is NOT injected (dropped)', () => {
138
+ const result = gateCredentialInjection(gateInput({
139
+ authorization: undefined,
140
+ credentials: [
141
+ { name: 'AWS_KEY', value: 'secret', alias: 'key-id:prod-s3-ro', channel: 'docker-env' },
142
+ ],
143
+ }));
144
+ expect(result.injected.map((c) => c.name)).not.toContain('AWS_KEY');
145
+ expect(result.dropped.map((c) => c.name)).toContain('AWS_KEY');
146
+ });
147
+ it('AC-50: a scoped docker -v MOUNT credential WITHOUT authorization is OMITTED (mount dropped)', () => {
148
+ const result = gateCredentialInjection(gateInput({
149
+ authorization: undefined,
150
+ credentials: [
151
+ {
152
+ name: '/host/.aws:/root/.aws',
153
+ value: '/host/.aws',
154
+ alias: 'key-id:prod-s3-ro',
155
+ channel: 'docker-mount',
156
+ },
157
+ ],
158
+ }));
159
+ expect(result.dropped.map((c) => c.channel)).toContain('docker-mount');
160
+ expect(result.injected).toHaveLength(0);
161
+ });
162
+ it('AC-50: a scoped k8s SECRET reference WITHOUT authorization is STRIPPED', () => {
163
+ const result = gateCredentialInjection(gateInput({
164
+ authorization: undefined,
165
+ credentials: [
166
+ { name: 'prod-s3-secret', value: 'ref', alias: 'key-id:prod-s3-ro', channel: 'k8s-secret' },
167
+ ],
168
+ }));
169
+ expect(result.dropped.map((c) => c.channel)).toContain('k8s-secret');
170
+ expect(result.injected).toHaveLength(0);
171
+ });
172
+ it('AC-50: a scoped k8s SERVICEACCOUNT reference WITHOUT authorization is STRIPPED', () => {
173
+ const result = gateCredentialInjection(gateInput({
174
+ authorization: undefined,
175
+ credentials: [
176
+ {
177
+ name: '--serviceaccount=prod-s3',
178
+ value: 'prod-s3',
179
+ alias: 'key-id:prod-s3-ro',
180
+ channel: 'k8s-serviceaccount',
181
+ },
182
+ ],
183
+ }));
184
+ expect(result.dropped.map((c) => c.channel)).toContain('k8s-serviceaccount');
185
+ });
186
+ it('AC-23a: when a policy marks the scoped credential REQUIRED, no authorization → the spawn is DENIED', () => {
187
+ const result = gateCredentialInjection(gateInput({
188
+ authorization: undefined,
189
+ credentials: [
190
+ {
191
+ name: 'AWS_KEY',
192
+ value: 'secret',
193
+ alias: 'key-id:prod-s3-ro',
194
+ channel: 'docker-env',
195
+ required: true,
196
+ },
197
+ ],
198
+ }));
199
+ expect(result.denied).toBe(true);
200
+ expect(result.reason).toBeTruthy();
201
+ });
202
+ it('AC-23a/AC-32: args MUTATED before exec → GATE-3 recomputed argsHash mismatch → deny (TOCTOU)', () => {
203
+ // GATE 3 is the last recomputation before exec. The authorization binds argsHash over
204
+ // ARGS; the actual spawn args were mutated after issuance → the scoped cred is NOT
205
+ // injected and (required) the spawn is denied.
206
+ const result = gateCredentialInjection(gateInput({
207
+ authorization: signCA(makeCA()),
208
+ args: { command: 'aws s3 rm --recursive s3://prod' },
209
+ command: 'aws s3 rm --recursive s3://prod',
210
+ credentials: [
211
+ {
212
+ name: 'AWS_KEY',
213
+ value: 'secret',
214
+ alias: 'key-id:prod-s3-ro',
215
+ channel: 'docker-env',
216
+ required: true,
217
+ },
218
+ ],
219
+ }));
220
+ expect(result.denied).toBe(true);
221
+ });
222
+ it('AC-40: an AGENT-SUPPLIED alias absent from the trusted source → no scope → deny (fail closed)', () => {
223
+ // The agent supplies an alias that is NOT in the trusted alias map, trying to get a
224
+ // broad credential tagged as the narrow scope. GATE 3 must not inject an untagged cred.
225
+ const result = gateCredentialInjection(gateInput({
226
+ authorization: signCA(makeCA()),
227
+ credentials: [
228
+ {
229
+ name: 'AWS_KEY',
230
+ value: 'secret',
231
+ alias: 'agent-forged-alias',
232
+ channel: 'docker-env',
233
+ required: true,
234
+ },
235
+ ],
236
+ }));
237
+ expect(result.denied).toBe(true);
238
+ });
239
+ it('AC-40a: an AMBIGUOUS credential identity (backed by >1 credential) → deny (never the narrower)', () => {
240
+ const ambiguousSource = {
241
+ aliasMap: { 'key-id:prod-s3-ro': STABLE_ID },
242
+ scopeByIdentity: { [IDENTITY]: CRED_SCOPE },
243
+ ambiguousIdentities: [IDENTITY],
244
+ };
245
+ const result = gateCredentialInjection(gateInput({
246
+ authorization: signCA(makeCA()),
247
+ source: ambiguousSource,
248
+ credentials: [
249
+ {
250
+ name: 'AWS_KEY',
251
+ value: 'secret',
252
+ alias: 'key-id:prod-s3-ro',
253
+ channel: 'docker-env',
254
+ required: true,
255
+ },
256
+ ],
257
+ }));
258
+ expect(result.denied).toBe(true);
259
+ });
260
+ it('AC-40a: identity is collision-resistant — a credential VALUE prefix is never the scope key', () => {
261
+ // Two DIFFERENT stable ids must never collide to one identity key; the identity is
262
+ // sha256 of the stable id, not the value prefix.
263
+ const idA = canonicalizeCredentialIdentity('kms:key/A');
264
+ const idB = canonicalizeCredentialIdentity('kms:key/B');
265
+ expect(idA).not.toBe(idB);
266
+ expect(idA).toMatch(/^[0-9a-f]{64}$/);
267
+ });
268
+ it('AC-55: two aliases (ARN + key-id) of ONE credential resolve to ONE scope (not falsely denied)', () => {
269
+ const viaArn = gateCredentialInjection(gateInput({
270
+ authorization: signCA(makeCA()),
271
+ credentials: [
272
+ {
273
+ name: 'AWS_KEY',
274
+ value: 'secret',
275
+ alias: 'arn:aws:kms:us-east-1:123:key/prod-s3-ro',
276
+ channel: 'docker-env',
277
+ },
278
+ ],
279
+ }));
280
+ const viaKeyId = gateCredentialInjection(gateInput({
281
+ authorization: signCA(makeCA()),
282
+ credentials: [
283
+ { name: 'AWS_KEY', value: 'secret', alias: 'key-id:prod-s3-ro', channel: 'docker-env' },
284
+ ],
285
+ }));
286
+ // Both aliases resolve to the same scope and are injected under the same authorization.
287
+ expect(viaArn.denied).toBe(false);
288
+ expect(viaKeyId.denied).toBe(false);
289
+ expect(viaArn.injected.map((c) => c.name)).toContain('AWS_KEY');
290
+ expect(viaKeyId.injected.map((c) => c.name)).toContain('AWS_KEY');
291
+ });
292
+ it('AC-50: a scoped action whose credential would arrive via IMDS is a BOUNDED, WARNED non-goal → not claimed as backstopped', () => {
293
+ // A credential delivered by a substrate channel spawn-invocation does NOT construct
294
+ // (IMDS/IRSA) cannot be gated. When a policy action declares a scope that could only
295
+ // arrive via such a channel, the gate MUST emit a warning and MUST NOT claim a GATE-3
296
+ // backstop (it does not silently allow the scoped action as if backstopped).
297
+ const result = gateCredentialInjection(gateInput({
298
+ authorization: signCA(makeCA()),
299
+ credentials: [
300
+ {
301
+ name: 'AWS_WEB_IDENTITY_TOKEN_FILE',
302
+ value: 'imds',
303
+ alias: 'key-id:prod-s3-ro',
304
+ // A channel GATE 3 cannot mediate — surfaced as an out-of-scope warning.
305
+ channel: 'docker-env',
306
+ },
307
+ ],
308
+ source: {
309
+ aliasMap: { 'key-id:prod-s3-ro': STABLE_ID },
310
+ scopeByIdentity: {}, // no GATE-3-mediated scope → substrate-delivered
311
+ ambiguousIdentities: [],
312
+ },
313
+ }));
314
+ // With no mediated scope for a scoped credential, GATE 3 fails closed rather than
315
+ // injecting an untagged credential (AC-40) and surfaces the non-goal warning (AC-50).
316
+ expect(result.injected.map((c) => c.name)).not.toContain('AWS_WEB_IDENTITY_TOKEN_FILE');
317
+ expect(Array.isArray(result.warnings)).toBe(true);
318
+ });
319
+ it('any exception during GATE 3 evaluation is a denial / no-injection, never a pass', () => {
320
+ const result = gateCredentialInjection({
321
+ // Deliberately malformed input → must fail closed.
322
+ credentials: [{ name: 'X', value: 'y', alias: 'z', channel: 'docker-env' }],
323
+ });
324
+ expect(result.denied || result.injected.length === 0).toBe(true);
325
+ });
326
+ });
327
+ //# sourceMappingURL=policy-credential-gate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-credential-gate.test.js","sourceRoot":"","sources":["../../src/__tests__/policy-credential-gate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-pretooluse-gate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/policy-pretooluse-gate.test.ts"],"names":[],"mappings":""}