Files
portal/tools/enforce-prompt-injection.test.mjs
T
Дмитрий b0cd18d797 fix(router-gate): quote-aware redirect detector + drop dead override-phrase ads
Квирк 2: новый stripQuotedSpans делает детектор stdout/stderr-редиректа
кавычко-осознанным — `>` / `2>` ВНУТРИ кавыченного аргумента (текст коммита
с <email>, "2>1") больше не ложно-блокируется; настоящие редиректы (оператор
вне кавычек) блокируются как прежде. RED→GREEN, существующие redirect/cd-app
кейсы целы.

1A: убрана реклама мёртвых override-фраз (findOverride — заглушка v4, фразы
не работают): баннер enforce-prompt-injection (каждый UserPromptSubmit) +
block-сообщения enforce-verify-before-push / coverage-verify / memory-coverage
/ tdd-gate (×3). Каждый фикс залочен негативным тестом.

Сознательно НЕ делали: калибровку 6 судьи (читать чат-контекст) и ослабление
exact-match approve (квирк 3) — это рубежи защиты, их трогать нельзя.

Регрессия vitest tools-only: 1989 passed | 2 skipped (verify через
npx vitest run --root app --config vitest.config.tools.mjs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 14:05:52 +03:00

78 lines
3.0 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { buildReminder } from './enforce-prompt-injection.mjs';
describe('enforce-prompt-injection / buildReminder', () => {
it('always includes the coverage-first-line rule', () => {
const txt = buildReminder({ classification: null, recentFlags: [] });
expect(txt).toMatch(/First line of your response MUST be/);
expect(txt).toMatch(/coverage:\s*<channel>:<id>/);
});
it('includes classifier output when present', () => {
const txt = buildReminder({
classification: { task_type: 'feature', confidence: 0.85, recommended_node: '#19', recommended_chain: 'L13' },
recentFlags: [],
});
expect(txt).toMatch(/task_type=feature/);
expect(txt).toMatch(/confidence=0\.85/);
expect(txt).toMatch(/#19/);
expect(txt).toMatch(/L13/);
});
it('mentions plan requirement for feature/bugfix/refactor/cleanup', () => {
for (const t of ['feature', 'bugfix', 'refactor', 'cleanup']) {
const txt = buildReminder({
classification: { task_type: t, confidence: 0.7 },
recentFlags: [],
});
expect(txt).toMatch(/Plan required/);
}
});
it('omits plan requirement for conversation/question', () => {
const txt = buildReminder({
classification: { task_type: 'question', confidence: 0.9 },
recentFlags: [],
});
expect(txt).not.toMatch(/Plan required/);
});
it('surfaces recent rationalization flags (up to 3)', () => {
const txt = buildReminder({
classification: null,
recentFlags: [
{ kind: 'skipped-plan', evidence: 'too simple' },
{ kind: 'single-coverage-drift', evidence: 'TDD coverage used for memory sync' },
{ kind: 'weak-test', evidence: '1 expect' },
{ kind: 'commit-without-tests', evidence: 'production edit without test' },
],
});
expect(txt).toMatch(/Previous turn flagged/);
// Last 3 should appear, first one should NOT
expect(txt).toMatch(/single-coverage-drift/);
expect(txt).toMatch(/weak-test/);
expect(txt).toMatch(/commit-without-tests/);
expect(txt).not.toMatch(/skipped-plan/);
});
it('notes detected override phrase + suppressed rule keys', () => {
const txt = buildReminder({
classification: null,
recentFlags: [],
override: { phrase: 'срочно', suppresses: ['verify-before-push', 'tdd-gate'] },
});
expect(txt).toMatch(/Override phrase detected/);
expect(txt).toMatch(/срочно/);
expect(txt).toMatch(/verify-before-push/);
});
it('does NOT advertise dead override-vocabulary phrases (v4 stub — 1A 2026-05-31)', () => {
const txt = buildReminder({ classification: null, recentFlags: [] });
// findOverride/loadOverrideVocab — заглушки (vocab removed in v4); реклама фраз
// вводила в заблуждение (фразы не работают). Баннер убран.
expect(txt).not.toMatch(/Override vocabulary/);
expect(txt).not.toMatch(/без скилов/);
expect(txt).not.toMatch(/ремонт инфраструктуры/);
});
});