7faef3c93f
Defense-in-depth для escape-гранта: подпись пропуска floor_escape, чтобы форж без секретного ключа отвергался (поверх content-floor). Task 1 — фундамент: - receipt-sign.mjs: +домен RECEIPT_DOMAINS.FLOOR_ESCAPE='floor-escape' (R-31, изолирует подпись floor-escape от approval/frozen-plan). - askuser-answer-parser.mjs: +signFloorEscapeRecord/verifyFloorEscapeRecord — зеркало signApprovalRecord/verifyApprovalRecord, домен FLOOR_ESCAPE. Чистые, без ключа → sig:null. TDD: 5 новых тестов (доменная изоляция, подпись/верификация целой записи, подделка/без sig/без ключа/чужой ключ/чужой домен → false). Регрессия по затронутым файлам 82 GREEN, 0 регрессий. Спека: docs/superpowers/specs/2026-06-10-floor-escape-signing-design.md План: docs/superpowers/plans/2026-06-10-floor-escape-signing.md (Task 1) Прод-код инертен до провижининга ключа (Фаза 8). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
31 lines
1.6 KiB
JavaScript
31 lines
1.6 KiB
JavaScript
// tools/askuser-answer-parser-floor-escape.test.mjs
|
|
import { describe, it, expect } from 'vitest';
|
|
import { signFloorEscapeRecord, verifyFloorEscapeRecord } from './askuser-answer-parser.mjs';
|
|
import { signApprovalRecord } from './askuser-answer-parser.mjs';
|
|
|
|
const KEY = 'test-receipt-key';
|
|
const REC = { type: 'floor_escape', action: 'bash:git push --force', ts: 1000 };
|
|
|
|
describe('signFloorEscapeRecord / verifyFloorEscapeRecord', () => {
|
|
it('подписывает (+sig 64hex) и верифицирует целую запись', () => {
|
|
const signed = signFloorEscapeRecord(REC, KEY);
|
|
expect(signed.sig).toMatch(/^[0-9a-f]{64}$/);
|
|
expect(signed.action).toBe(REC.action);
|
|
expect(verifyFloorEscapeRecord(signed, KEY)).toBe(true);
|
|
});
|
|
it('false на подделке / без sig / без ключа / чужом ключе', () => {
|
|
const signed = signFloorEscapeRecord(REC, KEY);
|
|
expect(verifyFloorEscapeRecord({ ...signed, action: 'bash:rm -rf /' }, KEY)).toBe(false);
|
|
expect(verifyFloorEscapeRecord(REC, KEY)).toBe(false); // нет sig
|
|
expect(verifyFloorEscapeRecord(signed, null)).toBe(false);
|
|
expect(verifyFloorEscapeRecord(signed, 'other-key')).toBe(false);
|
|
});
|
|
it('доменная изоляция: approval-подпись НЕ проходит как floor-escape', () => {
|
|
const asApproval = signApprovalRecord(REC, KEY); // домен APPROVAL
|
|
expect(verifyFloorEscapeRecord(asApproval, KEY)).toBe(false);
|
|
});
|
|
it('без ключа → sig:null', () => {
|
|
expect(signFloorEscapeRecord(REC, null).sig).toBe(null);
|
|
});
|
|
});
|