397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
2.6 KiB
JavaScript
44 lines
2.6 KiB
JavaScript
// tools/m1-foundation-invariants.test.mjs
|
|
// Машина 1 — сквозные инварианты фундамента (мастер-карта §«МАШИНА 1»).
|
|
import { describe, it, expect } from 'vitest';
|
|
import { journalAppend, loadJournal, verifyChain } from './action-journal.mjs';
|
|
import { signApprovalRecord, verifyApprovalRecord, buildApprovalRecord } from './askuser-answer-parser.mjs';
|
|
import { decideFromEvent } from './enforce-runtime-write-deny.mjs';
|
|
import { homedir } from 'node:os';
|
|
|
|
function memFs() {
|
|
const store = new Map();
|
|
return {
|
|
store,
|
|
readFileSync: (p) => { if (!store.has(String(p))) { const e = new Error('ENOENT'); e.code = 'ENOENT'; throw e; } return store.get(String(p)); },
|
|
writeFileSync: (p, d) => store.set(String(p), String(d)),
|
|
appendFileSync: (p, d) => store.set(String(p), (store.has(String(p)) ? store.get(String(p)) : '') + String(d)),
|
|
existsSync: (p) => store.has(String(p)),
|
|
};
|
|
}
|
|
const HOME_FWD = homedir().replace(/\\/g, '/');
|
|
const KEY = 'foundation-key';
|
|
|
|
describe('Машина 1 — инварианты фундамента', () => {
|
|
it('журнал: лог-до-действия пишется, и подменить запись задним числом нельзя', () => {
|
|
const fs = memFs();
|
|
journalAppend({ payload: { action: 'Edit', file: 'a.mjs' }, key: KEY, sessionId: 'S', runtimeDir: '/rt', nowMs: 1, fsImpl: fs });
|
|
const r2 = journalAppend({ payload: { action: 'Bash', cmd: 'git commit' }, key: KEY, sessionId: 'S', runtimeDir: '/rt', nowMs: 2, fsImpl: fs });
|
|
const loaded = loadJournal({ sessionId: 'S', runtimeDir: '/rt', fsImpl: fs });
|
|
expect(verifyChain(loaded.entries, r2.headSig, { key: KEY }).ok).toBe(true);
|
|
const tampered = loaded.entries.map((e, i) => i === 0 ? { ...e, payload: { action: 'NOTHING' } } : e);
|
|
expect(verifyChain(tampered, r2.headSig, { key: KEY }).ok).toBe(false);
|
|
});
|
|
|
|
it('расписка: подделать без ключа нельзя', () => {
|
|
const rec = buildApprovalRecord({ kind: 'approve_git_operation', pattern: 'git push --force', sessionId: 'S', nowMs: 1 });
|
|
expect(verifyApprovalRecord(rec, KEY)).toBe(false);
|
|
expect(verifyApprovalRecord(signApprovalRecord(rec, KEY), KEY)).toBe(true);
|
|
});
|
|
|
|
it('runtime защищён от записи любым инструментом', () => {
|
|
expect(decideFromEvent({ tool_name: 'mcp__fs__write', tool_input: { path: `${HOME_FWD}/.claude/runtime/x` } }).block).toBe(true);
|
|
expect(decideFromEvent({ tool_name: 'Write', tool_input: { file_path: `${HOME_FWD}/proj/x.mjs` } }).block).toBe(false);
|
|
});
|
|
});
|