fix(m5): F-3 — манифест floor-manifest-check проверяет весь защитный контур
DEFAULT_REQUIRED_HOOKS проверял только enforce-floor — owner мог зарегистрировать пол, забыть верховную стену / exfil-стражей и получить зелёный «protected». Расширено до security-load-bearing набора: enforce-floor + enforce-supreme-gate + normative-content + read-path-deny + mcp-classification. «Пол подтверждён» теперь = весь контур. WARN-only (Δ8 — сигнал, не блок); owner может передать иной requiredHooks. Аудит Машины 5 (объектив sharp-edges). TDD RED->GREEN. Регрессия tools-only 2789 + 2 skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,9 +21,18 @@ const HOOK_EVENTS = [
|
||||
'SessionEnd', 'UserPromptSubmit', 'PreCompact', 'Notification',
|
||||
];
|
||||
|
||||
// Минимально-обязательный набор для «пол подтверждён» — несущий пол-хук. Живая обёртка/
|
||||
// владелец может передать расширенный защитный набор; контракт checkManifest — по requiredHooks.
|
||||
const DEFAULT_REQUIRED_HOOKS = ['enforce-floor.mjs'];
|
||||
// Security-load-bearing набор для «пол подтверждён» (F-3, аудит 2026-06-07): не один пол, а
|
||||
// весь защитный контур — несущий пол (вето-до-плана) + верховная стена (вне-плана) + стражи
|
||||
// нормативки/read-exfil/egress-exfil. Раньше проверялся только enforce-floor → owner мог
|
||||
// забыть стену/стражей и получить зелёный «protected». Живая обёртка/владелец может передать
|
||||
// иной набор; контракт checkManifest — по requiredHooks (DEFAULT — фолбэк main()). WARN-only.
|
||||
const DEFAULT_REQUIRED_HOOKS = [
|
||||
'enforce-floor.mjs', // несущий пол: вето-до-плана на необратимое
|
||||
'enforce-supreme-gate.mjs', // верховная стена: default-deny вне плана (нужны ОБА)
|
||||
'enforce-normative-content-rules.mjs', // защита нормативки/памяти от подмены
|
||||
'enforce-read-path-deny.mjs', // read-exfil (transcript/runtime/secrets)
|
||||
'enforce-mcp-classification.mjs', // egress-exfil (исходящий MCP-payload)
|
||||
];
|
||||
|
||||
/** Собрать все command-строки хуков из settings (битые/пустые секции игнорируются, не бросает). */
|
||||
export function collectHookCommands(settings) {
|
||||
|
||||
@@ -57,3 +57,27 @@ describe('checkManifest (6.5, Δ8): WARN при отсутствии регис
|
||||
expect(r.block).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// F-3 (аудит 2026-06-07): DEFAULT_REQUIRED_HOOKS манифеста floor-manifest-check проверял только
|
||||
// enforce-floor — owner мог зарегистрировать пол, забыть стену/exfil-стражей и получить зелёный
|
||||
// «protected». Расширяем DEFAULT до security-load-bearing набора (пол + стена + normative/read/mcp
|
||||
// стражи), чтобы «пол подтверждён» означал весь защитный контур, а не один хук. WARN-only.
|
||||
describe('floor-manifest-check — F-3: DEFAULT защитный набор шире одного enforce-floor', () => {
|
||||
it('settings с одним enforce-floor (без стены) → cry (DEFAULT теперь требует защитный набор)', () => {
|
||||
const onlyFloor = { hooks: { PreToolUse: [{ matcher: '*', hooks: [{ type: 'command', command: 'node tools/enforce-floor.mjs' }] }] } };
|
||||
const r = checkManifest({ settings: onlyFloor }); // без requiredHooks → DEFAULT
|
||||
expect(r.cry).toBe(true);
|
||||
expect(r.missing).toContain('enforce-supreme-gate.mjs');
|
||||
});
|
||||
it('settings с полным защитным набором → ok (не кричит)', () => {
|
||||
const full = { hooks: { PreToolUse: [{ matcher: '*', hooks: [
|
||||
{ type: 'command', command: 'node tools/enforce-floor.mjs' },
|
||||
{ type: 'command', command: 'node tools/enforce-supreme-gate.mjs' },
|
||||
{ type: 'command', command: 'node tools/enforce-normative-content-rules.mjs' },
|
||||
{ type: 'command', command: 'node tools/enforce-read-path-deny.mjs' },
|
||||
{ type: 'command', command: 'node tools/enforce-mcp-classification.mjs' },
|
||||
] }] } };
|
||||
const r = checkManifest({ settings: full }); // без requiredHooks → DEFAULT
|
||||
expect(r.ok).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user