@coderifts/agent-guard 12.0.0 → 14.0.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/README.md +1 -1
- package/dist/cjs/atomic-profile.d.ts +61 -0
- package/dist/cjs/atomic-profile.d.ts.map +1 -0
- package/dist/cjs/atomic-profile.js +119 -0
- package/dist/cjs/atomic-profile.js.map +1 -0
- package/dist/cjs/cas-adapters/fs-default-wire.d.ts +40 -10
- package/dist/cjs/cas-adapters/fs-default-wire.d.ts.map +1 -1
- package/dist/cjs/cas-adapters/fs-default-wire.js +46 -18
- package/dist/cjs/cas-adapters/fs-default-wire.js.map +1 -1
- package/dist/cjs/cas-adapters/fs.d.ts +49 -0
- package/dist/cjs/cas-adapters/fs.d.ts.map +1 -1
- package/dist/cjs/cas-adapters/fs.js +167 -13
- package/dist/cjs/cas-adapters/fs.js.map +1 -1
- package/dist/cjs/cas-attestation.d.ts +23 -3
- package/dist/cjs/cas-attestation.d.ts.map +1 -1
- package/dist/cjs/cas-attestation.js +28 -0
- package/dist/cjs/cas-attestation.js.map +1 -1
- package/dist/cjs/conditional-write.d.ts +55 -0
- package/dist/cjs/conditional-write.d.ts.map +1 -1
- package/dist/cjs/conditional-write.js +6 -0
- package/dist/cjs/conditional-write.js.map +1 -1
- package/dist/cjs/deploy-gate.d.ts +15 -3
- package/dist/cjs/deploy-gate.d.ts.map +1 -1
- package/dist/cjs/deploy-gate.js +15 -3
- package/dist/cjs/deploy-gate.js.map +1 -1
- package/dist/cjs/execution-grant.d.ts +2 -0
- package/dist/cjs/execution-grant.d.ts.map +1 -1
- package/dist/cjs/execution-grant.js.map +1 -1
- package/dist/cjs/execution-proof.d.ts.map +1 -1
- package/dist/cjs/execution-proof.js +3 -1
- package/dist/cjs/execution-proof.js.map +1 -1
- package/dist/cjs/guard.d.ts.map +1 -1
- package/dist/cjs/guard.js +35 -7
- package/dist/cjs/guard.js.map +1 -1
- package/dist/cjs/index.d.ts +9 -5
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +18 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/monitoring-delivery.d.ts +1 -1
- package/dist/cjs/monitoring-delivery.d.ts.map +1 -1
- package/dist/cjs/tool-registry.d.ts.map +1 -1
- package/dist/cjs/tool-registry.js +15 -2
- package/dist/cjs/tool-registry.js.map +1 -1
- package/dist/cjs/types.d.ts +1 -1
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/with-coderifts.d.ts +50 -5
- package/dist/cjs/with-coderifts.d.ts.map +1 -1
- package/dist/cjs/with-coderifts.js +49 -6
- package/dist/cjs/with-coderifts.js.map +1 -1
- package/dist/esm/atomic-profile.d.ts +61 -0
- package/dist/esm/atomic-profile.d.ts.map +1 -0
- package/dist/esm/atomic-profile.js +111 -0
- package/dist/esm/atomic-profile.js.map +1 -0
- package/dist/esm/cas-adapters/fs-default-wire.d.ts +40 -10
- package/dist/esm/cas-adapters/fs-default-wire.d.ts.map +1 -1
- package/dist/esm/cas-adapters/fs-default-wire.js +45 -19
- package/dist/esm/cas-adapters/fs-default-wire.js.map +1 -1
- package/dist/esm/cas-adapters/fs.d.ts +49 -0
- package/dist/esm/cas-adapters/fs.d.ts.map +1 -1
- package/dist/esm/cas-adapters/fs.js +162 -12
- package/dist/esm/cas-adapters/fs.js.map +1 -1
- package/dist/esm/cas-attestation.d.ts +23 -3
- package/dist/esm/cas-attestation.d.ts.map +1 -1
- package/dist/esm/cas-attestation.js +28 -0
- package/dist/esm/cas-attestation.js.map +1 -1
- package/dist/esm/conditional-write.d.ts +55 -0
- package/dist/esm/conditional-write.d.ts.map +1 -1
- package/dist/esm/conditional-write.js +6 -0
- package/dist/esm/conditional-write.js.map +1 -1
- package/dist/esm/deploy-gate.d.ts +15 -3
- package/dist/esm/deploy-gate.d.ts.map +1 -1
- package/dist/esm/deploy-gate.js +15 -3
- package/dist/esm/deploy-gate.js.map +1 -1
- package/dist/esm/execution-grant.d.ts +2 -0
- package/dist/esm/execution-grant.d.ts.map +1 -1
- package/dist/esm/execution-grant.js.map +1 -1
- package/dist/esm/execution-proof.d.ts.map +1 -1
- package/dist/esm/execution-proof.js +3 -1
- package/dist/esm/execution-proof.js.map +1 -1
- package/dist/esm/guard.d.ts.map +1 -1
- package/dist/esm/guard.js +36 -8
- package/dist/esm/guard.js.map +1 -1
- package/dist/esm/index.d.ts +9 -5
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/monitoring-delivery.d.ts +1 -1
- package/dist/esm/monitoring-delivery.d.ts.map +1 -1
- package/dist/esm/tool-registry.d.ts.map +1 -1
- package/dist/esm/tool-registry.js +16 -3
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/with-coderifts.d.ts +50 -5
- package/dist/esm/with-coderifts.d.ts.map +1 -1
- package/dist/esm/with-coderifts.js +47 -6
- package/dist/esm/with-coderifts.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -236,7 +236,7 @@ outside the returned table — residual `calls_outside_guarded_path_invisible`.
|
|
|
236
236
|
|
|
237
237
|
The `withCodeRifts` example above is the **entry point** (absent profile: freshness and
|
|
238
238
|
conditional-write stay opt-in). For production, lock the fail-closed conjunction with
|
|
239
|
-
`profile: 'ENFORCING_STRICT'` (shipped guard@8.1.0; current package
|
|
239
|
+
`profile: 'ENFORCING_STRICT'` (shipped guard@8.1.0; current package 13.0.0). Construction
|
|
240
240
|
**aborts** if you opt down any locked flag, omit `resolvePriorContent`, or omit the execution
|
|
241
241
|
chain (`executionGrant: { enabled: true }`, required since 9.8.0).
|
|
242
242
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ENFORCING_ATOMIC_V1 — versioned profile (roadmap 1090).
|
|
3
|
+
*
|
|
4
|
+
* STRICT_V1 is frozen and is not edited. ATOMIC is a NEW profile: construction
|
|
5
|
+
* aborts with ATOMIC_PROFILE_UNSATISFIED when the invariant conjunction is not
|
|
6
|
+
* wired. Executor behaviour (nonce consume-once, CAS, read-back, attestation)
|
|
7
|
+
* is still observed per call; construction only demands the wiring.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PROFILE_ENFORCING_ATOMIC_V1: "ENFORCING_ATOMIC_V1";
|
|
10
|
+
export declare const GUARD_ATOMIC_WIRE_VALUE: "ENFORCING_ATOMIC";
|
|
11
|
+
export type AtomicProfileSpelling = 'ENFORCING_ATOMIC' | 'ENFORCING_ATOMIC_V1';
|
|
12
|
+
export declare const ATOMIC_PROFILE_UNSATISFIED: "ATOMIC_PROFILE_UNSATISFIED";
|
|
13
|
+
export type AtomicOutcome = 'AUTHORIZED_COMMITTED' | 'REFUSED' | 'INDETERMINATE';
|
|
14
|
+
/** Frozen invariant names — adding one is ATOMIC_V2, not an edit here. */
|
|
15
|
+
export declare const ATOMIC_INVARIANTS: readonly ["verified_receipt", "verified_grant_v2", "exact_executor", "exact_target", "after_payload_hash", "fresh_nonce", "nonce_consumed_once", "target_CAS", "read_back", "executor_attestation", "credential_boundary"];
|
|
16
|
+
export type MutatorRegister = {
|
|
17
|
+
registerMutator: (name: string) => MutatorRegister;
|
|
18
|
+
has: (name: string) => boolean;
|
|
19
|
+
list: () => readonly string[];
|
|
20
|
+
};
|
|
21
|
+
export declare function createMutatorRegister(): MutatorRegister;
|
|
22
|
+
export declare function isEnforcingAtomic(profile: unknown): boolean;
|
|
23
|
+
export type AtomicConstructionInput = {
|
|
24
|
+
executionGrant?: {
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
resolveStateNonce?: unknown;
|
|
27
|
+
grantVersion?: string;
|
|
28
|
+
} | null;
|
|
29
|
+
executorId?: string;
|
|
30
|
+
adapterId?: string;
|
|
31
|
+
targetUri?: string;
|
|
32
|
+
executorAttestation?: {
|
|
33
|
+
registry?: unknown;
|
|
34
|
+
} | null;
|
|
35
|
+
mutatorRegister?: MutatorRegister | null;
|
|
36
|
+
casAdapter?: unknown;
|
|
37
|
+
readBack?: unknown;
|
|
38
|
+
credentialBoundary?: unknown;
|
|
39
|
+
};
|
|
40
|
+
export declare function atomicConstructionProblems(input: AtomicConstructionInput): string[];
|
|
41
|
+
export declare function throwAtomicUnsatisfied(problems: string[]): never;
|
|
42
|
+
/**
|
|
43
|
+
* Per-call outcome union. Host-claimed commit never AUTHORIZED_COMMITTED.
|
|
44
|
+
*/
|
|
45
|
+
export declare function atomicOutcome(input: {
|
|
46
|
+
receiptVerified: boolean;
|
|
47
|
+
grantV2Valid: boolean;
|
|
48
|
+
executorMatch: boolean;
|
|
49
|
+
targetMatch: boolean;
|
|
50
|
+
nonceFresh: boolean;
|
|
51
|
+
nonceConsumedOnce: boolean;
|
|
52
|
+
casCommitted: boolean;
|
|
53
|
+
readBackOk: boolean;
|
|
54
|
+
executorAttested: boolean;
|
|
55
|
+
mutatorRegistered: boolean;
|
|
56
|
+
authUnavailable?: boolean;
|
|
57
|
+
nonceStoreUnavailable?: boolean;
|
|
58
|
+
readBackUnavailable?: boolean;
|
|
59
|
+
signerUnavailableAfterCommit?: boolean;
|
|
60
|
+
}): AtomicOutcome;
|
|
61
|
+
//# sourceMappingURL=atomic-profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-profile.d.ts","sourceRoot":"","sources":["../../src/atomic-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,EAAG,qBAA8B,CAAC;AAC1E,eAAO,MAAM,uBAAuB,EAAG,kBAA2B,CAAC;AACnE,MAAM,MAAM,qBAAqB,GAAG,kBAAkB,GAAG,qBAAqB,CAAC;AAE/E,eAAO,MAAM,0BAA0B,EAAG,4BAAqC,CAAC;AAEhF,MAAM,MAAM,aAAa,GAAG,sBAAsB,GAAG,SAAS,GAAG,eAAe,CAAC;AAEjF,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,4NAYnB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IACnD,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/B,IAAI,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEF,wBAAgB,qBAAqB,IAAI,eAAe,CAkBvD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,cAAc,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,EAAE,CAsCnF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAOhE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACxC,GAAG,aAAa,CAahB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ATOMIC_INVARIANTS = exports.ATOMIC_PROFILE_UNSATISFIED = exports.GUARD_ATOMIC_WIRE_VALUE = exports.PROFILE_ENFORCING_ATOMIC_V1 = void 0;
|
|
4
|
+
exports.createMutatorRegister = createMutatorRegister;
|
|
5
|
+
exports.isEnforcingAtomic = isEnforcingAtomic;
|
|
6
|
+
exports.atomicConstructionProblems = atomicConstructionProblems;
|
|
7
|
+
exports.throwAtomicUnsatisfied = throwAtomicUnsatisfied;
|
|
8
|
+
exports.atomicOutcome = atomicOutcome;
|
|
9
|
+
/**
|
|
10
|
+
* ENFORCING_ATOMIC_V1 — versioned profile (roadmap 1090).
|
|
11
|
+
*
|
|
12
|
+
* STRICT_V1 is frozen and is not edited. ATOMIC is a NEW profile: construction
|
|
13
|
+
* aborts with ATOMIC_PROFILE_UNSATISFIED when the invariant conjunction is not
|
|
14
|
+
* wired. Executor behaviour (nonce consume-once, CAS, read-back, attestation)
|
|
15
|
+
* is still observed per call; construction only demands the wiring.
|
|
16
|
+
*/
|
|
17
|
+
exports.PROFILE_ENFORCING_ATOMIC_V1 = 'ENFORCING_ATOMIC_V1';
|
|
18
|
+
exports.GUARD_ATOMIC_WIRE_VALUE = 'ENFORCING_ATOMIC';
|
|
19
|
+
exports.ATOMIC_PROFILE_UNSATISFIED = 'ATOMIC_PROFILE_UNSATISFIED';
|
|
20
|
+
/** Frozen invariant names — adding one is ATOMIC_V2, not an edit here. */
|
|
21
|
+
exports.ATOMIC_INVARIANTS = Object.freeze([
|
|
22
|
+
'verified_receipt',
|
|
23
|
+
'verified_grant_v2',
|
|
24
|
+
'exact_executor',
|
|
25
|
+
'exact_target',
|
|
26
|
+
'after_payload_hash',
|
|
27
|
+
'fresh_nonce',
|
|
28
|
+
'nonce_consumed_once',
|
|
29
|
+
'target_CAS',
|
|
30
|
+
'read_back',
|
|
31
|
+
'executor_attestation',
|
|
32
|
+
'credential_boundary',
|
|
33
|
+
]);
|
|
34
|
+
function createMutatorRegister() {
|
|
35
|
+
const names = new Set();
|
|
36
|
+
const api = {
|
|
37
|
+
registerMutator(name) {
|
|
38
|
+
if (typeof name !== 'string' || name.trim() === '') {
|
|
39
|
+
throw new Error('registerMutator: non-empty name required');
|
|
40
|
+
}
|
|
41
|
+
names.add(name);
|
|
42
|
+
return api;
|
|
43
|
+
},
|
|
44
|
+
has(name) {
|
|
45
|
+
return names.has(name);
|
|
46
|
+
},
|
|
47
|
+
list() {
|
|
48
|
+
return Object.freeze([...names]);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
return api;
|
|
52
|
+
}
|
|
53
|
+
function isEnforcingAtomic(profile) {
|
|
54
|
+
return profile === 'ENFORCING_ATOMIC' || profile === exports.PROFILE_ENFORCING_ATOMIC_V1;
|
|
55
|
+
}
|
|
56
|
+
function atomicConstructionProblems(input) {
|
|
57
|
+
const problems = [];
|
|
58
|
+
const grant = input.executionGrant;
|
|
59
|
+
if (!(grant && grant.enabled === true)) {
|
|
60
|
+
problems.push('verified_grant_v2: executionGrant.enabled must be true');
|
|
61
|
+
}
|
|
62
|
+
if (!(grant && grant.grantVersion === 'v2')) {
|
|
63
|
+
problems.push('verified_grant_v2: executionGrant.grantVersion must be \'v2\'');
|
|
64
|
+
}
|
|
65
|
+
if (typeof grant?.resolveStateNonce !== 'function') {
|
|
66
|
+
problems.push('fresh_nonce: executionGrant.resolveStateNonce required');
|
|
67
|
+
}
|
|
68
|
+
if (typeof input.executorId !== 'string' || input.executorId.length === 0) {
|
|
69
|
+
problems.push('exact_executor: executorId required');
|
|
70
|
+
}
|
|
71
|
+
if (typeof input.targetUri !== 'string' || input.targetUri.length === 0) {
|
|
72
|
+
problems.push('exact_target: targetUri required');
|
|
73
|
+
}
|
|
74
|
+
if (typeof input.adapterId !== 'string' || input.adapterId.length === 0) {
|
|
75
|
+
problems.push('after_payload_hash: adapterId required (payload bind surface)');
|
|
76
|
+
}
|
|
77
|
+
if (!(input.executorAttestation && input.executorAttestation.registry)) {
|
|
78
|
+
problems.push('executor_attestation: executorAttestation.registry required');
|
|
79
|
+
}
|
|
80
|
+
if (!input.mutatorRegister || typeof input.mutatorRegister.registerMutator !== 'function'
|
|
81
|
+
|| input.mutatorRegister.list().length === 0) {
|
|
82
|
+
problems.push('credential_boundary: mutatorRegister with ≥1 registerMutator required');
|
|
83
|
+
}
|
|
84
|
+
if (input.casAdapter == null) {
|
|
85
|
+
problems.push('target_CAS: casAdapter required');
|
|
86
|
+
}
|
|
87
|
+
if (typeof input.readBack !== 'function') {
|
|
88
|
+
problems.push('read_back: readBack function required');
|
|
89
|
+
}
|
|
90
|
+
if (input.credentialBoundary !== true && typeof input.credentialBoundary !== 'object') {
|
|
91
|
+
problems.push('credential_boundary: credentialBoundary assertion required');
|
|
92
|
+
}
|
|
93
|
+
return problems;
|
|
94
|
+
}
|
|
95
|
+
function throwAtomicUnsatisfied(problems) {
|
|
96
|
+
const err = new Error(`withCodeRifts: ${exports.ATOMIC_PROFILE_UNSATISFIED} — ${problems.length} condition(s):\n`
|
|
97
|
+
+ problems.map((p) => ` - ${p}`).join('\n'));
|
|
98
|
+
err.code = exports.ATOMIC_PROFILE_UNSATISFIED;
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Per-call outcome union. Host-claimed commit never AUTHORIZED_COMMITTED.
|
|
103
|
+
*/
|
|
104
|
+
function atomicOutcome(input) {
|
|
105
|
+
if (input.authUnavailable || input.nonceStoreUnavailable || input.readBackUnavailable) {
|
|
106
|
+
return 'INDETERMINATE';
|
|
107
|
+
}
|
|
108
|
+
if (input.signerUnavailableAfterCommit && input.casCommitted) {
|
|
109
|
+
return 'INDETERMINATE';
|
|
110
|
+
}
|
|
111
|
+
const ok = input.receiptVerified && input.grantV2Valid && input.executorMatch
|
|
112
|
+
&& input.targetMatch && input.nonceFresh && input.nonceConsumedOnce
|
|
113
|
+
&& input.casCommitted && input.readBackOk && input.executorAttested
|
|
114
|
+
&& input.mutatorRegistered;
|
|
115
|
+
if (ok && input.executorAttested)
|
|
116
|
+
return 'AUTHORIZED_COMMITTED';
|
|
117
|
+
return 'REFUSED';
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=atomic-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-profile.js","sourceRoot":"","sources":["../../src/atomic-profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ExecuteIfUnchangedOutcome } from '../conditional-write.js';
|
|
2
|
+
import type { VersionToken } from '../conditional-write.js';
|
|
3
|
+
import type { FsObjectIdentity } from './fs.js';
|
|
2
4
|
/** Unambiguous fs target: a non-empty `path` string on tool arguments (Write convention). */
|
|
3
5
|
export declare function inferFsPathFromArgs(args: unknown): string | null;
|
|
4
6
|
/**
|
|
@@ -6,20 +8,48 @@ export declare function inferFsPathFromArgs(args: unknown): string | null;
|
|
|
6
8
|
* is not an Edit (no old_string/new_string). Fragments are not a whole-file CAS.
|
|
7
9
|
*/
|
|
8
10
|
export declare function inferFullFileWriteContent(args: unknown): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Options carrying the AUTHORIZATION-TIME measurement into the write.
|
|
13
|
+
*/
|
|
14
|
+
export type FsCasWireOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* Token of the state the AUTHORIZATION examined, measured at T1 by the runner — before preflight,
|
|
17
|
+
* not at write time. This is the whole fix: see the note on wrapWriteWithFsCas.
|
|
18
|
+
*/
|
|
19
|
+
expected_token?: VersionToken | null;
|
|
20
|
+
/** Identity (dev+ino) of that same T1 object. Catches an inode swap the token cannot see. */
|
|
21
|
+
expected_identity?: FsObjectIdentity | null;
|
|
22
|
+
};
|
|
23
|
+
/** The pair a runner measures at T1. Both halves describe the SAME observation. */
|
|
24
|
+
export type FsAuthorizationMeasurement = {
|
|
25
|
+
expected_token: VersionToken;
|
|
26
|
+
expected_identity: FsObjectIdentity | null;
|
|
27
|
+
};
|
|
9
28
|
/**
|
|
10
29
|
* Wire the FS adapter when path + full-file contents are both present.
|
|
11
30
|
* Otherwise run the host write unchanged. If the host already returned an
|
|
12
31
|
* ExecuteIfUnchangedOutcome, pass it through (do not wrap again).
|
|
13
32
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
33
|
+
* THE TOKEN MUST COME FROM T1. This function used to call createFsVersionToken HERE, inside
|
|
34
|
+
* executeFactory — after preflight (T1) and after the T2 recheck — and then condition the write on
|
|
35
|
+
* that self-fetched value. Measured on 12.0.0: with a writer landing between T2 and that read, the
|
|
36
|
+
* CAS adopted the interfering state as its legitimate starting point and still reported
|
|
37
|
+
* status:'committed' with enforced:true. It was a real check of a vacuous window — it asked whether
|
|
38
|
+
* the state had changed since a read a microsecond earlier, which is a question whose answer is
|
|
39
|
+
* always no and which says nothing about the state the authorization actually examined.
|
|
40
|
+
*
|
|
41
|
+
* Now the expected token is supplied by the caller from the T1 measurement. When it is ABSENT we do
|
|
42
|
+
* NOT fall back to self-fetching, because that is precisely the vacuous claim: the write runs
|
|
43
|
+
* unwrapped and the guarantee is NON_ATOMIC, which is the honest thing to report and which
|
|
44
|
+
* requireConditionalWrite will refuse. A weaker guarantee is reportable; a false one is not.
|
|
45
|
+
*/
|
|
46
|
+
export declare function wrapWriteWithFsCas<T>(args: unknown, write: () => Promise<T> | T, opts?: FsCasWireOptions): Promise<T | ExecuteIfUnchangedOutcome<unknown>>;
|
|
47
|
+
/**
|
|
48
|
+
* The T1 measurement itself — call this in the RUNNER, before preflight, and pass the result to
|
|
49
|
+
* wrapWriteWithFsCas. Returns null when this call is not an inferable full-file FS write, or when
|
|
50
|
+
* the path is unsafe / unreadable (a token we could not measure is not a token we may claim).
|
|
23
51
|
*/
|
|
24
|
-
export declare function
|
|
52
|
+
export declare function measureFsAuthorization(args: unknown): Promise<FsAuthorizationMeasurement | null>;
|
|
53
|
+
/** Convenience: the token half only. */
|
|
54
|
+
export declare function measureFsAuthorizationToken(args: unknown): Promise<VersionToken | null>;
|
|
25
55
|
//# sourceMappingURL=fs-default-wire.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs-default-wire.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"fs-default-wire.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGhD,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,cAAc,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC7C,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,0BAA0B,GAAG;IACvC,cAAc,EAAE,YAAY,CAAC;IAC7B,iBAAiB,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC3B,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,CAAC,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAgBjD;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAetG;AAED,wCAAwC;AACxC,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAG7F"}
|
|
@@ -15,6 +15,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.inferFsPathFromArgs = inferFsPathFromArgs;
|
|
16
16
|
exports.inferFullFileWriteContent = inferFullFileWriteContent;
|
|
17
17
|
exports.wrapWriteWithFsCas = wrapWriteWithFsCas;
|
|
18
|
+
exports.measureFsAuthorization = measureFsAuthorization;
|
|
19
|
+
exports.measureFsAuthorizationToken = measureFsAuthorizationToken;
|
|
18
20
|
const cas_attestation_js_1 = require("../cas-attestation.js");
|
|
19
21
|
const fs_js_1 = require("./fs.js");
|
|
20
22
|
/** Unambiguous fs target: a non-empty `path` string on tool arguments (Write convention). */
|
|
@@ -51,31 +53,29 @@ function inferFullFileWriteContent(args) {
|
|
|
51
53
|
* Otherwise run the host write unchanged. If the host already returned an
|
|
52
54
|
* ExecuteIfUnchangedOutcome, pass it through (do not wrap again).
|
|
53
55
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
56
|
+
* THE TOKEN MUST COME FROM T1. This function used to call createFsVersionToken HERE, inside
|
|
57
|
+
* executeFactory — after preflight (T1) and after the T2 recheck — and then condition the write on
|
|
58
|
+
* that self-fetched value. Measured on 12.0.0: with a writer landing between T2 and that read, the
|
|
59
|
+
* CAS adopted the interfering state as its legitimate starting point and still reported
|
|
60
|
+
* status:'committed' with enforced:true. It was a real check of a vacuous window — it asked whether
|
|
61
|
+
* the state had changed since a read a microsecond earlier, which is a question whose answer is
|
|
62
|
+
* always no and which says nothing about the state the authorization actually examined.
|
|
63
|
+
*
|
|
64
|
+
* Now the expected token is supplied by the caller from the T1 measurement. When it is ABSENT we do
|
|
65
|
+
* NOT fall back to self-fetching, because that is precisely the vacuous claim: the write runs
|
|
66
|
+
* unwrapped and the guarantee is NON_ATOMIC, which is the honest thing to report and which
|
|
67
|
+
* requireConditionalWrite will refuse. A weaker guarantee is reportable; a false one is not.
|
|
63
68
|
*/
|
|
64
|
-
async function wrapWriteWithFsCas(args, write) {
|
|
69
|
+
async function wrapWriteWithFsCas(args, write, opts) {
|
|
65
70
|
const filePath = inferFsPathFromArgs(args);
|
|
66
71
|
const content = inferFullFileWriteContent(args);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
expected = await (0, fs_js_1.createFsVersionToken)(filePath);
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return write();
|
|
74
|
-
}
|
|
72
|
+
const expected = opts && typeof opts.expected_token === 'string' ? opts.expected_token : null;
|
|
73
|
+
if (filePath && content != null && expected !== null) {
|
|
75
74
|
return (0, fs_js_1.writeFileIfUnchanged)({
|
|
76
75
|
path: filePath,
|
|
77
76
|
expected_token: expected,
|
|
78
77
|
content,
|
|
78
|
+
...(opts && 'expected_identity' in opts ? { expected_identity: opts.expected_identity ?? null } : {}),
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
const raw = await write();
|
|
@@ -83,4 +83,32 @@ async function wrapWriteWithFsCas(args, write) {
|
|
|
83
83
|
return raw;
|
|
84
84
|
return raw;
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The T1 measurement itself — call this in the RUNNER, before preflight, and pass the result to
|
|
88
|
+
* wrapWriteWithFsCas. Returns null when this call is not an inferable full-file FS write, or when
|
|
89
|
+
* the path is unsafe / unreadable (a token we could not measure is not a token we may claim).
|
|
90
|
+
*/
|
|
91
|
+
async function measureFsAuthorization(args) {
|
|
92
|
+
const filePath = inferFsPathFromArgs(args);
|
|
93
|
+
const content = inferFullFileWriteContent(args);
|
|
94
|
+
if (!filePath || content == null)
|
|
95
|
+
return null;
|
|
96
|
+
try {
|
|
97
|
+
const target = await (0, fs_js_1.assertSafeCasPath)(filePath);
|
|
98
|
+
// Identity FIRST, then the token: both must describe the same observation, and taking identity
|
|
99
|
+
// first means a swap between them makes the token the newer of the two, which the pre-rename
|
|
100
|
+
// re-check then catches rather than silently accepting.
|
|
101
|
+
const expected_identity = await (0, fs_js_1.fsObjectIdentity)(target);
|
|
102
|
+
const expected_token = await (0, fs_js_1.createFsVersionToken)(target);
|
|
103
|
+
return { expected_token, expected_identity };
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Convenience: the token half only. */
|
|
110
|
+
async function measureFsAuthorizationToken(args) {
|
|
111
|
+
const m = await measureFsAuthorization(args);
|
|
112
|
+
return m ? m.expected_token : null;
|
|
113
|
+
}
|
|
86
114
|
//# sourceMappingURL=fs-default-wire.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs-default-wire.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,YAAY,CAAC;;
|
|
1
|
+
{"version":3,"file":"fs-default-wire.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs-default-wire.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,46 @@ export declare const FS_VERSION_TOKEN_PREFIX = "fs:v1:";
|
|
|
21
21
|
export declare const FS_ABSENT_TOKEN: VersionToken;
|
|
22
22
|
/** Extract content-hash segment from an fs:v1 token (null if absent/malformed). */
|
|
23
23
|
export declare function fsTokenContentHash(token: VersionToken | null | undefined): string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Refuse a path this adapter must not treat as a CAS target.
|
|
26
|
+
*
|
|
27
|
+
* MEASURED ABSENT before this existed (2026-08-27): `..` was silently resolved and the write landed
|
|
28
|
+
* outside the intended directory, and a symlinked target was followed by stat/readFile while the
|
|
29
|
+
* rename replaced the LINK — so the check and the write referred to DIFFERENT objects. That object
|
|
30
|
+
* mismatch is the other half of the TOCTOU: a path interpreted twice can have a symlink swapped in
|
|
31
|
+
* between the two interpretations.
|
|
32
|
+
*
|
|
33
|
+
* Three refusals, each for a different escape:
|
|
34
|
+
* - a literal `..` segment in the caller's own path (traversal, judged BEFORE resolution),
|
|
35
|
+
* - a symlinked final component (lstat — the object we would check is not the one we would write).
|
|
36
|
+
*
|
|
37
|
+
* The parent directory is CANONICALISED rather than rejected. Requiring realpath(dir) === dir was
|
|
38
|
+
* written first and measured wrong within the hour: on macOS /var is itself a symlink, so every
|
|
39
|
+
* path under a normal temp directory was refused. A symlinked ancestor is not the danger — an
|
|
40
|
+
* ancestor that CHANGES between the check and the write is, and that is closed by the inode/device
|
|
41
|
+
* identity re-check in writeFileIfUnchanged, not by refusing ordinary filesystems. Resolving the
|
|
42
|
+
* parent once and building the target from it means both the check and the write name one object.
|
|
43
|
+
*/
|
|
44
|
+
export declare class UnsafeCasPathError extends Error {
|
|
45
|
+
readonly reason: 'path_traversal' | 'symlink_target';
|
|
46
|
+
readonly casPath: string;
|
|
47
|
+
constructor(reason: 'path_traversal' | 'symlink_target', casPath: string);
|
|
48
|
+
}
|
|
49
|
+
export declare function assertSafeCasPath(filePath: string): Promise<string>;
|
|
50
|
+
/** Filesystem identity of the object a token was measured on. Evidence, not a comparison key. */
|
|
51
|
+
export type FsObjectIdentity = {
|
|
52
|
+
dev: number;
|
|
53
|
+
ino: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Identity of the object CURRENTLY at a path, read through a FILE DESCRIPTOR.
|
|
57
|
+
*
|
|
58
|
+
* Opening once and fstat-ing the handle is what makes this an identity rather than another path
|
|
59
|
+
* interpretation: the fd names the object, so a symlink swapped in afterwards cannot redirect it.
|
|
60
|
+
* null when the path does not exist.
|
|
61
|
+
*/
|
|
62
|
+
export declare function fsObjectIdentity(filePath: string): Promise<FsObjectIdentity | null>;
|
|
63
|
+
export declare function identitiesEqual(a: FsObjectIdentity | null | undefined, b: FsObjectIdentity | null | undefined): boolean;
|
|
24
64
|
/**
|
|
25
65
|
* Build a VersionToken from path: mtime (ms) + sha256 of file bytes.
|
|
26
66
|
* Throws on I/O errors other than callers may catch. ENOENT → returns FS_ABSENT_TOKEN
|
|
@@ -36,6 +76,13 @@ export type WriteFileIfUnchangedArgs = {
|
|
|
36
76
|
path: string;
|
|
37
77
|
/** Token measured before the host decided to write (from createFsVersionToken). */
|
|
38
78
|
expected_token: VersionToken;
|
|
79
|
+
/**
|
|
80
|
+
* Identity (dev+ino) of the object the AUTHORIZATION examined, from fsObjectIdentity at T1.
|
|
81
|
+
* Optional, and when omitted no identity claim is made. Supplying it is strictly stronger than
|
|
82
|
+
* the token alone: an inode swapped in carrying identical bytes and mtime produces an IDENTICAL
|
|
83
|
+
* token, so content equality cannot see it. null means "absent at T1" (create-if-absent).
|
|
84
|
+
*/
|
|
85
|
+
expected_identity?: FsObjectIdentity | null;
|
|
39
86
|
/** Full new file contents (utf8 string or Buffer). */
|
|
40
87
|
content: string | Buffer;
|
|
41
88
|
};
|
|
@@ -59,6 +106,8 @@ export declare function writeFileIfUnchanged(args: WriteFileIfUnchangedArgs): Pr
|
|
|
59
106
|
path: string;
|
|
60
107
|
bytes: number;
|
|
61
108
|
written_content_hash: string;
|
|
109
|
+
checked_identity: FsObjectIdentity | null;
|
|
110
|
+
committed_identity: FsObjectIdentity | null;
|
|
62
111
|
}>>;
|
|
63
112
|
/**
|
|
64
113
|
* Opt-in PriorContentResolver that reads a file path from tool arguments.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAA6B,CAAC;AAM5D,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQxF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAA6B,CAAC;AAM5D,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQxF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,OAAO,EAAE,MAAM;CAMzE;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCzE;AAED,iGAAiG;AACjG,MAAM,MAAM,gBAAgB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAazF;AAED,wBAAgB,eAAe,CAC7B,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EACtC,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GACrC,OAAO,CAGT;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA2BlF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAOnF;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,cAAc,EAAE,YAAY,CAAC;IAC7B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,kBAAkB,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC7C,CAAC,CAAC,CAgGF;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACrE,GAAG,oBAAoB,CAoBvB;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}
|