@xshieldai/aegis-guard 0.4.0 → 0.5.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/package.json +1 -1
- package/src/approval-token.ts +42 -0
- package/src/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xshieldai/aegis-guard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "AEGIS Guard SDK — Five Locks primitives (approval-token, nonce, idempotency, SENSE, quality-evidence) + opt-in Agentic Control Center event bus for AEGIS-governed services",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
package/src/approval-token.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface ApprovalTokenPayload {
|
|
|
23
23
|
issued_by?: string;
|
|
24
24
|
nonce?: string;
|
|
25
25
|
status?: 'approved' | 'revoked' | 'denied';
|
|
26
|
+
action_digest?: string; // @rule:AEG-E-016 — binds the token to the CONCRETE act, not just its label
|
|
26
27
|
[key: string]: unknown;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -253,3 +254,44 @@ export function verifyScopedApprovalToken(
|
|
|
253
254
|
|
|
254
255
|
return payload;
|
|
255
256
|
}
|
|
257
|
+
|
|
258
|
+
// @rule:AEG-E-016 — bind an approval to the CONCRETE act, not just its label.
|
|
259
|
+
// An independent review (Oct 2026) showed a token scoped only to service/capability/operation
|
|
260
|
+
// approves ANY instance of that operation: one 'drop_table' approval drops any table. The fix is a
|
|
261
|
+
// digest of the concrete action (which table, which amount, which recipient) carried in the signed
|
|
262
|
+
// payload as action_digest; the gate recomputes it from the act in hand and refuses a mismatch.
|
|
263
|
+
// This rides the existing, hardened verifyScopedApprovalToken (a field with no token value refuses),
|
|
264
|
+
// so a token minted without an action_digest cannot pass the action-bound check at all.
|
|
265
|
+
|
|
266
|
+
// Deterministic JSON: keys sorted, so {a,b} and {b,a} digest the same.
|
|
267
|
+
function stableStringify(v: unknown): string {
|
|
268
|
+
if (v === null || typeof v !== 'object') return JSON.stringify(v) ?? 'null';
|
|
269
|
+
if (Array.isArray(v)) return '[' + v.map(stableStringify).join(',') + ']';
|
|
270
|
+
const o = v as Record<string, unknown>;
|
|
271
|
+
return '{' + Object.keys(o).sort().map((k) => JSON.stringify(k) + ':' + stableStringify(o[k])).join(',') + '}';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// SHA-256 of the canonical action. The caller owns what 'the action' is; the SDK never names domain concepts.
|
|
275
|
+
export function actionDigest(action: unknown): string {
|
|
276
|
+
return createHash('sha256').update(stableStringify(action)).digest('hex');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Mint an approval already bound to a concrete action (adds action_digest to the signed payload).
|
|
280
|
+
export function mintActionApprovalToken(payload: ApprovalTokenPayload, action: unknown): string {
|
|
281
|
+
return mintApprovalToken({ ...payload, action_digest: actionDigest(action) });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Verify an approval against the concrete action in hand. The token MUST carry a matching action_digest;
|
|
285
|
+
// a label-only token (no action_digest) is refused here, closing "one approval fits every instance".
|
|
286
|
+
export function verifyActionApprovalToken(
|
|
287
|
+
token: string,
|
|
288
|
+
expectedServiceId: string,
|
|
289
|
+
expectedCapability: string,
|
|
290
|
+
expectedOperation: string,
|
|
291
|
+
action: unknown,
|
|
292
|
+
): ApprovalTokenPayload {
|
|
293
|
+
return verifyScopedApprovalToken(
|
|
294
|
+
token, expectedServiceId, expectedCapability, expectedOperation,
|
|
295
|
+
{ action_digest: actionDigest(action) },
|
|
296
|
+
);
|
|
297
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,9 @@ export {
|
|
|
46
46
|
verifyApprovalToken,
|
|
47
47
|
verifyAndConsumeNonce,
|
|
48
48
|
verifyScopedApprovalToken,
|
|
49
|
+
actionDigest,
|
|
50
|
+
mintActionApprovalToken,
|
|
51
|
+
verifyActionApprovalToken,
|
|
49
52
|
} from './approval-token.js';
|
|
50
53
|
|
|
51
54
|
// @rule:KGT-002 — KGT-T1.1 (v0.3.0): approval tokens are AEGIS-signed EdDSA JWTs.
|