@longarc/mdash 3.1.2 → 3.1.3
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 +86 -23
- package/SECURITY.md +254 -0
- package/dist/accountability/engine.d.ts +27 -0
- package/dist/accountability/engine.d.ts.map +1 -0
- package/dist/accountability/engine.js +148 -0
- package/dist/accountability/engine.js.map +1 -0
- package/dist/accountability/types.d.ts +46 -0
- package/dist/accountability/types.d.ts.map +1 -0
- package/dist/accountability/types.js +8 -0
- package/dist/accountability/types.js.map +1 -0
- package/dist/checkpoint/engine.d.ts.map +1 -1
- package/dist/checkpoint/engine.js +4 -0
- package/dist/checkpoint/engine.js.map +1 -1
- package/dist/context/compose.d.ts +62 -0
- package/dist/context/compose.d.ts.map +1 -0
- package/dist/context/compose.js +286 -0
- package/dist/context/compose.js.map +1 -0
- package/dist/context/crypto/hash.d.ts +100 -0
- package/dist/context/crypto/hash.d.ts.map +1 -0
- package/dist/context/crypto/hash.js +248 -0
- package/dist/context/crypto/hash.js.map +1 -0
- package/dist/context/crypto/hmac.d.ts +80 -0
- package/dist/context/crypto/hmac.d.ts.map +1 -0
- package/dist/context/crypto/hmac.js +192 -0
- package/dist/context/crypto/hmac.js.map +1 -0
- package/dist/context/crypto/index.d.ts +7 -0
- package/dist/context/crypto/index.d.ts.map +1 -0
- package/dist/context/crypto/index.js +7 -0
- package/dist/context/crypto/index.js.map +1 -0
- package/dist/context/engine-v3.0-backup.d.ts +197 -0
- package/dist/context/engine-v3.0-backup.d.ts.map +1 -0
- package/dist/context/engine-v3.0-backup.js +392 -0
- package/dist/context/engine-v3.0-backup.js.map +1 -0
- package/dist/context/fragment.d.ts +99 -0
- package/dist/context/fragment.d.ts.map +1 -0
- package/dist/context/fragment.js +316 -0
- package/dist/context/fragment.js.map +1 -0
- package/dist/context/index.d.ts +99 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +180 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/provenance.d.ts +80 -0
- package/dist/context/provenance.d.ts.map +1 -0
- package/dist/context/provenance.js +294 -0
- package/dist/context/provenance.js.map +1 -0
- package/dist/context/resolve.d.ts +106 -0
- package/dist/context/resolve.d.ts.map +1 -0
- package/dist/context/resolve.js +440 -0
- package/dist/context/resolve.js.map +1 -0
- package/dist/context/store.d.ts +156 -0
- package/dist/context/store.d.ts.map +1 -0
- package/dist/context/store.js +396 -0
- package/dist/context/store.js.map +1 -0
- package/dist/context/types.d.ts +463 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +94 -0
- package/dist/context/types.js.map +1 -0
- package/dist/context/utils/atomic.d.ts +76 -0
- package/dist/context/utils/atomic.d.ts.map +1 -0
- package/dist/context/utils/atomic.js +159 -0
- package/dist/context/utils/atomic.js.map +1 -0
- package/dist/context/utils/credit.d.ts +65 -0
- package/dist/context/utils/credit.d.ts.map +1 -0
- package/dist/context/utils/credit.js +164 -0
- package/dist/context/utils/credit.js.map +1 -0
- package/dist/context/utils/index.d.ts +13 -0
- package/dist/context/utils/index.d.ts.map +1 -0
- package/dist/context/utils/index.js +13 -0
- package/dist/context/utils/index.js.map +1 -0
- package/dist/context/utils/utility.d.ts +63 -0
- package/dist/context/utils/utility.d.ts.map +1 -0
- package/dist/context/utils/utility.js +141 -0
- package/dist/context/utils/utility.js.map +1 -0
- package/dist/core/commitment.d.ts +25 -2
- package/dist/core/commitment.d.ts.map +1 -1
- package/dist/core/commitment.js +44 -6
- package/dist/core/commitment.js.map +1 -1
- package/dist/core/crypto.d.ts +2 -0
- package/dist/core/crypto.d.ts.map +1 -1
- package/dist/core/crypto.js +12 -0
- package/dist/core/crypto.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -10
- package/dist/index.js.map +1 -1
- package/dist/mcca/engine.d.ts.map +1 -1
- package/dist/mcca/engine.js +5 -4
- package/dist/mcca/engine.js.map +1 -1
- package/dist/physics/engine.d.ts +1 -0
- package/dist/physics/engine.d.ts.map +1 -1
- package/dist/physics/engine.js +36 -2
- package/dist/physics/engine.js.map +1 -1
- package/dist/provenance/api-handler.d.ts +45 -0
- package/dist/provenance/api-handler.d.ts.map +1 -0
- package/dist/provenance/api-handler.js +223 -0
- package/dist/provenance/api-handler.js.map +1 -0
- package/dist/provenance/api-types.d.ts +108 -0
- package/dist/provenance/api-types.d.ts.map +1 -0
- package/dist/provenance/api-types.js +9 -0
- package/dist/provenance/api-types.js.map +1 -0
- package/dist/provenance/index.d.ts +6 -0
- package/dist/provenance/index.d.ts.map +1 -0
- package/dist/provenance/index.js +3 -0
- package/dist/provenance/index.js.map +1 -0
- package/dist/provenance/provenance-engine.d.ts +63 -0
- package/dist/provenance/provenance-engine.d.ts.map +1 -0
- package/dist/provenance/provenance-engine.js +311 -0
- package/dist/provenance/provenance-engine.js.map +1 -0
- package/dist/provenance/types.d.ts +193 -0
- package/dist/provenance/types.d.ts.map +1 -0
- package/dist/provenance/types.js +9 -0
- package/dist/provenance/types.js.map +1 -0
- package/dist/tee/engine.d.ts.map +1 -1
- package/dist/tee/engine.js +14 -0
- package/dist/tee/engine.js.map +1 -1
- package/dist/warrant/engine.d.ts +24 -1
- package/dist/warrant/engine.d.ts.map +1 -1
- package/dist/warrant/engine.js +76 -1
- package/dist/warrant/engine.js.map +1 -1
- package/dist/zk/engine.d.ts.map +1 -1
- package/dist/zk/engine.js +7 -4
- package/dist/zk/engine.js.map +1 -1
- package/docs/SECURITY-PATCHES.md +170 -0
- package/package.json +17 -5
- package/src/__tests__/accountability.test.ts +308 -0
- package/src/__tests__/l1-verification-modes.test.ts +424 -0
- package/src/__tests__/phase1.benchmark.test.ts +94 -0
- package/src/__tests__/phase1.test.ts +0 -77
- package/src/__tests__/phase2-4.benchmark.test.ts +60 -0
- package/src/__tests__/phase2-4.test.ts +1 -52
- package/src/__tests__/provenance/api-handler.test.ts +356 -0
- package/src/__tests__/provenance/provenance-engine.test.ts +628 -0
- package/src/__tests__/sa-2026-008.test.ts +45 -0
- package/src/__tests__/sa-2026-009.test.ts +86 -0
- package/src/__tests__/sa-2026-010.test.ts +72 -0
- package/src/__tests__/sa-2026-012.test.ts +65 -0
- package/src/__tests__/sa-2026-nfc.test.ts +40 -0
- package/src/__tests__/security.test.ts +786 -0
- package/src/accountability/engine.ts +230 -0
- package/src/accountability/types.ts +58 -0
- package/src/checkpoint/engine.ts +4 -0
- package/src/context/__tests__/caret-v0.2.0.test.ts +860 -0
- package/src/context/__tests__/integration.test.ts +356 -0
- package/src/context/compose.ts +388 -0
- package/src/context/crypto/hash.ts +277 -0
- package/src/context/crypto/hmac.ts +253 -0
- package/src/context/crypto/index.ts +29 -0
- package/src/context/engine-v3.0-backup.ts +598 -0
- package/src/context/fragment.ts +454 -0
- package/src/context/index.ts +427 -0
- package/src/context/provenance.ts +380 -0
- package/src/context/resolve.ts +581 -0
- package/src/context/store.ts +503 -0
- package/src/context/types.ts +679 -0
- package/src/context/utils/atomic.ts +207 -0
- package/src/context/utils/credit.ts +224 -0
- package/src/context/utils/index.ts +13 -0
- package/src/context/utils/utility.ts +200 -0
- package/src/core/commitment.ts +129 -67
- package/src/core/crypto.ts +13 -0
- package/src/index.ts +62 -10
- package/src/mcca/engine.ts +5 -4
- package/src/physics/engine.ts +40 -3
- package/src/provenance/api-handler.ts +248 -0
- package/src/provenance/api-types.ts +112 -0
- package/src/provenance/index.ts +19 -0
- package/src/provenance/provenance-engine.ts +387 -0
- package/src/provenance/types.ts +211 -0
- package/src/tee/engine.ts +16 -0
- package/src/warrant/engine.ts +89 -1
- package/src/zk/engine.ts +8 -4
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA-2026-009: ZK seal verification gap
|
|
3
|
+
* Verifies that verifyProof rejects documents with tampered seals.
|
|
4
|
+
* Prior to fix: expectedSeal was computed but never compared.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
8
|
+
import { CommitmentEngine } from '../core/commitment.js';
|
|
9
|
+
import { ZKProofsEngine } from '../zk/engine.js';
|
|
10
|
+
import type { Seal } from '../core/crypto.js';
|
|
11
|
+
|
|
12
|
+
const TEST_KEY = 'test-seal-key-zk-verify-gap-minimum-32-chars';
|
|
13
|
+
|
|
14
|
+
describe('SA-2026-009: ZK seal verification gap', () => {
|
|
15
|
+
let commitmentEngine: CommitmentEngine;
|
|
16
|
+
let zkEngine: ZKProofsEngine;
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
commitmentEngine = new CommitmentEngine();
|
|
20
|
+
await commitmentEngine.initialize(TEST_KEY);
|
|
21
|
+
zkEngine = new ZKProofsEngine(commitmentEngine);
|
|
22
|
+
await zkEngine.initialize(TEST_KEY);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('verifyProof accepts a validly sealed document', async () => {
|
|
26
|
+
const doc = await zkEngine.requestProof({
|
|
27
|
+
type: 'commitment_inclusion',
|
|
28
|
+
statement: {
|
|
29
|
+
description: 'SA-2026-009 valid seal test',
|
|
30
|
+
claim: { test: 'valid-seal' },
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const completed = await zkEngine.waitForProof(doc.id, 5000);
|
|
35
|
+
const result = await zkEngine.verifyProof(completed);
|
|
36
|
+
|
|
37
|
+
expect(result.valid).toBe(true);
|
|
38
|
+
expect(result.errors).toHaveLength(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('verifyProof rejects a document with a tampered seal', async () => {
|
|
42
|
+
const doc = await zkEngine.requestProof({
|
|
43
|
+
type: 'commitment_inclusion',
|
|
44
|
+
statement: {
|
|
45
|
+
description: 'SA-2026-009 tampered seal test',
|
|
46
|
+
claim: { test: 'tampered-seal' },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const completed = await zkEngine.waitForProof(doc.id, 5000);
|
|
51
|
+
|
|
52
|
+
// Tamper with the seal
|
|
53
|
+
const tampered = { ...completed, seal: 'tampered-seal-value' as Seal };
|
|
54
|
+
|
|
55
|
+
const result = await zkEngine.verifyProof(tampered);
|
|
56
|
+
|
|
57
|
+
expect(result.valid).toBe(false);
|
|
58
|
+
expect(result.errors).toContain('Seal verification failed');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('verifyProof rejects a document with mutated content', async () => {
|
|
62
|
+
const doc = await zkEngine.requestProof({
|
|
63
|
+
type: 'commitment_inclusion',
|
|
64
|
+
statement: {
|
|
65
|
+
description: 'SA-2026-009 mutation test',
|
|
66
|
+
claim: { test: 'content-mutation' },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const completed = await zkEngine.waitForProof(doc.id, 5000);
|
|
71
|
+
|
|
72
|
+
// Mutate content without re-sealing
|
|
73
|
+
const mutated = {
|
|
74
|
+
...completed,
|
|
75
|
+
statement: {
|
|
76
|
+
...completed.statement,
|
|
77
|
+
description: 'ALTERED after sealing',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const result = await zkEngine.verifyProof(mutated);
|
|
82
|
+
|
|
83
|
+
expect(result.valid).toBe(false);
|
|
84
|
+
expect(result.errors).toContain('Seal verification failed');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA-2026-010: Checkpoint fire-and-forget error handling
|
|
3
|
+
* Verifies that L1 commitment failures are caught and surfaced
|
|
4
|
+
* via checkpoint status rather than silently swallowed.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeAll, vi } from 'vitest';
|
|
8
|
+
import { CommitmentEngine } from '../core/commitment.js';
|
|
9
|
+
import { CheckpointEngine } from '../checkpoint/engine.js';
|
|
10
|
+
|
|
11
|
+
const TEST_KEY = 'test-seal-key-checkpoint-catch-minimum-32-chars';
|
|
12
|
+
|
|
13
|
+
describe('SA-2026-010: Checkpoint fire-and-forget error handling', () => {
|
|
14
|
+
let commitmentEngine: CommitmentEngine;
|
|
15
|
+
let checkpointEngine: CheckpointEngine;
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
commitmentEngine = new CommitmentEngine();
|
|
19
|
+
await commitmentEngine.initialize(TEST_KEY);
|
|
20
|
+
checkpointEngine = new CheckpointEngine(commitmentEngine);
|
|
21
|
+
await checkpointEngine.initialize(TEST_KEY);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('checkpoint status becomes failed when L1 commit rejects', async () => {
|
|
25
|
+
// Spy on commit and make it reject
|
|
26
|
+
const commitSpy = vi.spyOn(commitmentEngine, 'commit')
|
|
27
|
+
.mockRejectedValueOnce(new Error('Simulated L1 failure'));
|
|
28
|
+
|
|
29
|
+
// Suppress expected console.error
|
|
30
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
31
|
+
|
|
32
|
+
const checkpoint = await checkpointEngine.createCheckpoint({
|
|
33
|
+
agent_id: 'test-agent-sa010',
|
|
34
|
+
trigger: 'action_start',
|
|
35
|
+
state: {
|
|
36
|
+
warrant_id: 'w-test-sa010' as any,
|
|
37
|
+
action_type: 'test',
|
|
38
|
+
context_hash: 'hash-test' as any,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// checkpoint is returned immediately (fire-and-forget)
|
|
43
|
+
expect(checkpoint.status).toBe('pending');
|
|
44
|
+
|
|
45
|
+
// Wait for the async .then/.catch to resolve
|
|
46
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
47
|
+
|
|
48
|
+
// After failure, status should be 'failed', not stuck on 'pending'
|
|
49
|
+
expect(checkpoint.status).toBe('failed');
|
|
50
|
+
|
|
51
|
+
commitSpy.mockRestore();
|
|
52
|
+
errorSpy.mockRestore();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('checkpoint status becomes sealed on successful L1 commit', async () => {
|
|
56
|
+
const checkpoint = await checkpointEngine.createCheckpoint({
|
|
57
|
+
agent_id: 'test-agent-sa010-ok',
|
|
58
|
+
trigger: 'action_start',
|
|
59
|
+
state: {
|
|
60
|
+
warrant_id: 'w-test-sa010-ok' as any,
|
|
61
|
+
action_type: 'test',
|
|
62
|
+
context_hash: 'hash-test-ok' as any,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Wait for async commit
|
|
67
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
68
|
+
|
|
69
|
+
expect(checkpoint.status).toBe('sealed');
|
|
70
|
+
expect(checkpoint.commitment_id).toBeTruthy();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA-2026-012: Influence budget underflow protection
|
|
3
|
+
* Verifies that influence and token counts never go negative
|
|
4
|
+
* after fragment removal.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
8
|
+
import { CommitmentEngine } from '../core/commitment.js';
|
|
9
|
+
import { MCCAEngine } from '../mcca/engine.js';
|
|
10
|
+
|
|
11
|
+
const TEST_KEY = 'test-seal-key-mcca-underflow-minimum-32-chars';
|
|
12
|
+
|
|
13
|
+
describe('SA-2026-012: Influence budget underflow protection', () => {
|
|
14
|
+
let commitmentEngine: CommitmentEngine;
|
|
15
|
+
let engine: MCCAEngine;
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
commitmentEngine = new CommitmentEngine();
|
|
19
|
+
await commitmentEngine.initialize(TEST_KEY);
|
|
20
|
+
engine = new MCCAEngine(commitmentEngine);
|
|
21
|
+
await engine.initialize(TEST_KEY);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('influence never goes negative after fragment removal', async () => {
|
|
25
|
+
// Add a fragment
|
|
26
|
+
const fragment = await engine.addFragment({
|
|
27
|
+
content: { test: 'underflow-influence' },
|
|
28
|
+
source_class: 'user',
|
|
29
|
+
region: 'task',
|
|
30
|
+
token_count: 50,
|
|
31
|
+
influence: 0.05,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Remove it once
|
|
35
|
+
await engine.removeFragment(fragment.id);
|
|
36
|
+
|
|
37
|
+
// Remove it again (simulating race or double-remove)
|
|
38
|
+
await engine.removeFragment(fragment.id);
|
|
39
|
+
|
|
40
|
+
// influenceBySource is private; access via cast to verify the floor guard
|
|
41
|
+
const influenceBySource = (engine as any).influenceBySource as Record<string, number>;
|
|
42
|
+
expect(influenceBySource.user).toBeGreaterThanOrEqual(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('token count never goes negative after double removal', async () => {
|
|
46
|
+
// Add a fragment
|
|
47
|
+
const fragment = await engine.addFragment({
|
|
48
|
+
content: { test: 'underflow-tokens' },
|
|
49
|
+
source_class: 'system',
|
|
50
|
+
region: 'task',
|
|
51
|
+
token_count: 100,
|
|
52
|
+
influence: 0.1,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Remove it once
|
|
56
|
+
await engine.removeFragment(fragment.id);
|
|
57
|
+
|
|
58
|
+
// Remove again
|
|
59
|
+
await engine.removeFragment(fragment.id);
|
|
60
|
+
|
|
61
|
+
// stats.tokens maps to the internal totalTokens field
|
|
62
|
+
const stats = engine.getStats();
|
|
63
|
+
expect(stats.tokens).toBeGreaterThanOrEqual(0);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA-2026-NFC: Unicode normalization in deterministicStringify
|
|
3
|
+
* Verifies that decomposed and precomposed Unicode codepoints
|
|
4
|
+
* produce identical hashes.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import { sha256Object, deterministicStringify } from '../core/crypto.js';
|
|
9
|
+
|
|
10
|
+
describe('SA-2026-NFC: Unicode normalization', () => {
|
|
11
|
+
it('NFD and NFC representations hash identically', async () => {
|
|
12
|
+
// U+00E9 (precomposed e-acute) vs U+0065 + U+0301 (e + combining acute)
|
|
13
|
+
const nfc = { name: '\u00E9' }; // e with acute (precomposed)
|
|
14
|
+
const nfd = { name: '\u0065\u0301' }; // e + combining acute (decomposed)
|
|
15
|
+
|
|
16
|
+
const hashNFC = await sha256Object(nfc);
|
|
17
|
+
const hashNFD = await sha256Object(nfd);
|
|
18
|
+
|
|
19
|
+
expect(hashNFC).toBe(hashNFD);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('deterministicStringify normalizes strings to NFC', () => {
|
|
23
|
+
const nfd = '\u0065\u0301'; // decomposed
|
|
24
|
+
const nfc = '\u00E9'; // precomposed
|
|
25
|
+
|
|
26
|
+
const resultNFD = deterministicStringify({ val: nfd });
|
|
27
|
+
const resultNFC = deterministicStringify({ val: nfc });
|
|
28
|
+
|
|
29
|
+
expect(resultNFD).toBe(resultNFC);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('non-string values are unaffected', () => {
|
|
33
|
+
const result = deterministicStringify({ num: 42, bool: true, nil: null });
|
|
34
|
+
const parsed = JSON.parse(result);
|
|
35
|
+
|
|
36
|
+
expect(parsed.num).toBe(42);
|
|
37
|
+
expect(parsed.bool).toBe(true);
|
|
38
|
+
expect(parsed.nil).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
});
|