v1.1.0: LLM/Agentic Threat Expansion - #5
Conversation
Wires every new v1.1.0 subsystem end-to-end with stubs while preserving existing capability. Adds 9 stub services (AtlasService, OsvFeedService, GhsaGraphQLService, KevFeedService, HuggingFaceService, TrifectaAnalyzer, CanaryService, McpToolScanner, TasteTesterService), 6 new pattern files (disabled placeholders), 6 new MCP tools (scan_mcp_tool, check_lethal_trifecta, query_cve, deploy_canary, verify_canary, taste_test), and a v11SkeletonTests suite (25 assertions). Bumps version to 1.1.0-pre.0 and adds @anthropic-ai/sdk dependency for Pass 11 (Taste-Tester). Verification: npm run build clean; npm test 87/0; MCP boot smoke green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 3 enabled patterns to unicode-smuggling.json (Unicode Tag block, zero-width threshold, bidi overrides). Implements stripUnicodeSmuggling helper that records exact stripped code points + classes as audit evidence on findings. Extends GeminiService prompt + category enum with unicode_smuggling. Also implements true threshold-mode detection in StaticCheckService (previously every pattern ran simple regex.test regardless of mode), which Pass 0 stubs implicitly depended on for zero-width FP suppression. Verification: build clean; 99/0 tests pass; tag-smuggled payload returns severity=critical with full stripped-char audit trail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 4 critical-severity patterns covering XML/HTML, INI/TOML, JSON, and YAML policy-wrapper jailbreak forms (HiddenLayer Policy Puppetry, Apr 2025). FP-guarded via closing-tag pairing, override-keyword body checks, and OpenAI message-object shape matching — bare config-file questions about INI/YAML/role do not trigger. Extends StaticCheckService with policy_puppetry category + hasPolicyPuppetry flag and GeminiService prompt rule 8. Verification: 117/0 tests; positive sample severity critical; negative samples (legitimate INI/YAML/role-field discussion) safe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 4 markdown-exfil patterns (image with opaque query string, javascript:/data:text/html URIs, raw <img> exfil) and seeds prompt-injection.json with 8 hand-curated IOC patterns covering ignore-previous-instructions, act-as-unrestricted, system-prompt leak, untrusted-authority headers, safety-bypass claims, HTML-comment injection, structured-output bracket escapes, and tool-result spoofing. StaticCheckService widened with markdown_exfil/prompt_injection categories + hasMarkdownExfil/hasPromptInjection flags; GeminiService prompt rules 9+10 added. Regression tests in Pass 1/2 confirm benign markdown and INI/role-discussion text remain safe; smuggled payloads now correctly co-flag both their original category and prompt_injection where the literal string appears. Verification: 143/0 tests; benign images safe, ignore-previous critical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real implementation of McpToolScanner: canonical sorted-key SHA-256 hash, recursive string-field walker with dot-path tracking (inputSchema.properties.q.description, etc.), Unicode-smuggling detection via stripUnicodeSmuggling, pattern matching across mcp_tool_poisoning + prompt_injection + policy_puppetry categories, severity rollup, and drift detection vs priorHash. Adds 5 enabled patterns to mcp-tool-poisoning.json (imperatives, ignore-previous, HTML-comment covert channel, priority stealth claims, authority/role claims). Factors threshold-aware evaluatePattern() out of StaticCheckService for shared use by the scanner — no duplicated regex/threshold logic. Verification: 161/0 tests; poisoned tool → critical (3 findings); benign tool → safe (0 findings). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real implementation of TrifectaAnalyzer (Simon Willison, Jun 2025): classifies inputs into three buckets — private-data read, untrusted content fetch, external egress — by regex-matched capability/tool/skill- content signatures. critical when all three co-locate (exfil guaranteed exploitable); medium when 2-of-3 (one capability away from trifecta); safe otherwise. Each bucket records evidence with rule + source. Wires into SkillScanService so scan_skill now reports hasLethalTrifecta + full TrifectaResult. The check_lethal_trifecta MCP tool input schema gains a tools[] field alongside capabilities[]/skillContent. Verification: 210/0 tests (incl. 49-assert trifectaTests covering all 8 capability-set combinations); 3of3 critical, 2of3 medium, 1of3 safe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real fetch-based implementations of OsvFeedService (POST /v1/querybatch
with empty-input short-circuit, id dedup, per-id hydration fallback,
30s AbortController) and GhsaGraphQLService (GraphQL
securityVulnerabilities ecosystem-filtered, post-filtered to AI
allowlist, graceful no-token degradation returning []). Shared
ai-package allowlist module covers langchain/transformers/litellm/
ollama/llamaindex/openai/anthropic across PyPI + npm.
VulnFeedService.updateFeeds now runs all four sources in
Promise.allSettled parallel with per-source error tagging. Result
gains perSource:{nvd,ghsaRest,ghsaGraphql,osv} counts. AI-package
vulns staged as category:ai_supply_chain with empty pattern (these
are code-vuln dependencies, not regex prompts — Pass 9 query_cve
surfaces them).
PatternEntrySchema.source enum extended with ghsa_graphql/osv;
StagedCandidate.source matches. Mock-fetch helper in
src/test/helpers/mockFetch.ts for reuse across feed tests.
Verification: 227/0 tests; offline smoke (no token, no network)
returns [] from both services without throwing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AtlasService loads STIX bundle from atlas-navigator-data with 7-day TTL cache + offline fallback table (AML.T0051/T0054/T0024/T0070/T0071 per SPEC §7). KevFeedService loads CISA catalog with 24h TTL cache and O(1) case-insensitive isInKev lookup. VulnFeedService.updateFeeds now refreshes ATLAS+KEV in parallel at start and enrichCandidate adds inKev:true + bumps severity one level for any staged CVE in KEV (low→medium→high→critical). atlasTechnique attached heuristically (ai_supply_chain→AML.T0070). PatternEntrySchema extended with optional atlasTechnique field (regex AML.[TM]NNNN). Five pattern files updated with technique IDs: unicode-smuggling→T0051, policy-puppetry→T0054, markdown-exfil→T0024, mcp-tool-poisoning→T0070, prompt-injection→T0051. StaticCheckService/SkillScanService/SecurityService all propagate atlasTechniques[] into outputs. patterns/.gitignore added to exclude feed-cache/. Verification: 248/0 tests; offline fallback returns canonical ATLAS names; KEV severity bump from medium to high verified for canned CVE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HuggingFaceService.checkModel makes real GET /api/models/{owner}/{name}
calls with anonymous-OK auth, 15s AbortController timeout, 6h in-memory
cache, defensive parsing across HF's evolving shape: gated flag,
unsafe-serialization detection from siblings[] (.pkl/.bin/.dill/.joblib
without safetensors twin), trust_remote_code flags from cardData/tags,
and Protect AI scanner output keyword-grep. Class-tagged flags with
severity rollup (code_execution_risk critical, unsafe_serialization
high, scanner_warning medium, gated/no_safetensors low, lookup_failed
safe).
extractModelIds detects model IDs in skill content via from_pretrained,
huggingface.co URLs, and owner/repo near model keywords — conservative
to avoid file-path FPs (rejects host.tld owners). SkillScanService fans
out extracted IDs via Promise.allSettled, surfaces
huggingFaceSecurityFlags[] in result, rolls severity into
overallSeverity.
Verification: 275/0 tests; offline lookup returns lookup_failed/safe
without throwing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UnifiedCveCache merges staged candidates by cveId across NVD, OSV,
GHSA-REST, and GHSA-GraphQL sources. Per merged record: deduped
sources[] (also tagged 'kev' and 'atlas' when enriched), max severity
across the group, KevEntry from KevFeedService.get, ATLAS techniques
expanded via AtlasService.lookup with synthetic stub fallback for
unknown IDs, affectedPackages parsed from tail-anchored
'(ECOSYSTEM:pkg)' suffix.
query_cve MCP tool now functional: AND-combined filters for keyword
(case-insensitive title+description match), ecosystem, atlasTechnique,
severity, inKev boolean, and limit (default 50, cap 200). Returns
{total, matched, records}. VulnFeedService exposes new
listStagedCandidates() public method.
Verification: 306/0 tests; offline smoke returns {total:0, matched:0}
when no staging file present (no exception); CVE-2026-42208 merged from
osv+ghsa_graphql candidates picks up kev+atlas sources and bumped
severity correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CanaryService replaces Pass 0 stub with real persistence: UUIDv4 tokens via crypto.randomUUID, 12-hex watchHandle from SHA-256(token), TTL default 24h via CANARY_DEFAULT_TTL_SECONDS, HMAC-signed state file patterns/canary-state.json with secret resolution chain (CANARY_HMAC_SECRET → PATTERN_INTEGRITY_SECRET → unsigned+warn). Tamper detection on load (refuses corrupted state). Atomic write via tmp+rename like PatternService. Methods: issueToken, checkEcho (with optional watchHandle scoping), list, revoke, verifyStateIntegrity. deploy_canary and verify_canary MCP tools now functional. canary-state files added to patterns/.gitignore. Verification: 333/0 tests across 14 suites (canaryTests 29 assertions); echo-detected returns critical; expired/revoked tokens prune correctly; HMAC tamper refuses load. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-designed dual-agent sandbox detonator. TasteTesterService runs a
Taster subagent (Anthropic SDK, mocked fetch_url tool only in 11a) and
a separate Monitor agent (structured JSON output, zod-validated) that
grades the Taster's transcript for malicious intent. Returns
BehaviorReport with intents[], monitorVerdict (clean/suspicious/
malicious), per-intent ATLAS techniques, and severity rollup.
Gated behind TASTE_TESTER_ENABLED env flag; lazy SDK import keeps the
disabled path zero-cost. Env-resolved config (model claude-sonnet-4-6,
MAX_TURNS 5, MAX_TOKENS 4096, TIMEOUT_MS 30000). Hard turn cap of 2 in
11a (full multi-turn loop comes in 11b). AbortController per call;
SDK errors and timeouts degrade gracefully (available:false, reason,
clean-stub report). Monitor system prompt explicitly treats transcript
as data, not instructions — anti-injection.
MinimalAnthropicClient interface + anthropicFactory option enables
mock-injection for tests without real API calls. mockAnthropic helper
queues turn-by-turn responses including thrown errors and never-
resolving promises (timeout test).
Verification: 34 tasteTesterTests pass, 348/0 across all suites with
no real Anthropic API call. Smoke: default returns
{available:false, reason:"TASTE_TESTER_ENABLED=false"}; enabled-no-key
returns {available:false, reason:"ANTHROPIC_API_KEY missing"}.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…corpus Lifts Pass 11a's HARD_CAP_11A and adds the full 8-tool mock surface per SPEC §5.2: fetch_url, read_file, exec_shell, send_email, transfer_funds, navigate_browser, write_memory, query_database. Each returns plausible synthetic data without real I/O. read_file path-shape routing emits fake SSH/AWS keys for sensitive paths so Monitor can distinguish sensitive vs benign reads. fetch_url body embeds an "sk-test-FAKE" tripwire — if Taster echoes it back, Monitor flags indirect-injection following. Multi-turn loop: fast mode caps at min(maxTurns,2); thorough uses full maxTurns (env-overridable). Truncation flag in timings reports runaway. Per-tool TOOL_DEFAULTS severity+ATLAS fallback used when Monitor declines structured output. 20-sample labeled corpus (taste-tester-corpus.json) drives a baseline agreement test (20/20 against scripted mocks — real-API calibration remains a Pass 13 follow-up). Source-purity grep confirms routeMockTool has no fs / child_process / globalThis.fetch imports — sandbox correctness asserted in test F. Verification: 399/0 tests; tasteTester suite grows from 34 to 89 assertions; corpus harness adds 6 more. Gate still works (TASTE_TESTER_ENABLED=false → instant return, no SDK load). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 2 many-shot patterns (Q/A pair stack threshold:20, turn-marker stack threshold:30) tagged AML.T0054 (Jailbreak) — Anthropic's context-saturation jailbreak technique. Adds 4 obfuscation patterns to unicode-smuggling.json: Sneaky Bits paired ZWNJ/ZWJ encoder (critical, threshold:6), Cyrillic homoglyph confusables (high, threshold:5), Base32 ≥32-char chunks (high), hex ≥60-char chunks (medium). All threshold-mode where appropriate to suppress benign emoji-ZWJ and short technical strings. StaticCheckService gains many_shot + obfuscation categories and hasManyShot result flag. GeminiService prompt rule 11 covers many-shot. Pattern count 65 → 70. Verification: 426/0 tests; many-shot stack of 25 Q/A pairs flags critical with many_shot category; benign "What is 2+2?" returns safe. Known FP candidates documented for Pass 13: Cyrillic homoglyph threshold may need disable for legitimately-multilingual deployments; enumerated Q1:/A1: variant not yet caught. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final hardening + docs pass. Bumps version 1.1.0-pre.0 → 1.1.0. Docs: - README.md gains v1.1 tool list, pattern category table, feed source table, expanded env-var reference, and defense-in-depth disclaimer (2026 meta-study: adaptive attacks beat ~85% of single defenses). - CHANGELOG.md v1.1.0 entry covering all 13 passes (6 new MCP tools, 10 detection categories, 5 new feed sources, dual-agent sandbox, test growth 87→457). - SKILLS_SECURITY.md adds Lethal Trifecta, HF security signals, ATLAS taxonomy sections. - SPEC.md §13.1 dispositions risk register: 3 resolved, 3 partially mitigated, 2 deferred (CVE-2026-2796 runtime out of scope; MemoryGraft arXiv narrative-only; OWASP LLM 2026 draft). - PLAN.md execution log table filled with 2026-05-13 dates + commit SHAs for Passes 0–12. FP fix from Pass 12: adds many-shot-enumerated-qa pattern (threshold 15) catching Q1:/Question 1:/Human 12: enumeration variant. Pattern count 70 → 71. Smoke harness scripts/smoke-v1.1.ts exercises all 11 MCP tools end-to- end (5 existing + 6 new); offline-tolerant; 11/0 PASS in this environment. Verification: 457/0 tests across 17 suites; npm run build clean; manifest regenerated; boot smoke shows MCP server starts on stdio. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughRelease v1.1.0 adds agentic/LLM threat coverage: new pattern sets, feed integrations (OSV, GHSA GraphQL, KEV, ATLAS, Hugging Face), many new services (Atlas, OSV, KEV, GHSA, HuggingFace, UnifiedCveCache, TrifectaAnalyzer, McpToolScanner, Canary, TasteTester), MCP tool wiring, extensive tests, and documentation/spec/plan updates. ChangesPrompt Rejector v1.1.0
Sequence Diagram(s)sequenceDiagram
participant Client
participant McpServer
participant McpToolScanner
participant TrifectaAnalyzer
participant UnifiedCveCache
participant CanaryService
participant TasteTesterService
Client->>McpServer: CallToolRequest(name, args)
McpServer->>McpToolScanner: scan_mcp_tool(toolDescriptor)
McpServer->>TrifectaAnalyzer: check_lethal_trifecta(input)
McpServer->>UnifiedCveCache: query_cve(filters)
McpServer->>CanaryService: deploy_canary(opts)
McpServer->>CanaryService: verify_canary(content, handle?)
McpServer->>TasteTesterService: taste_test(prompt, mode)
Estimated code review effort: 🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31868ea5b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31868ea5b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/services/SkillScanService.ts (1)
136-156:⚠️ Potential issue | 🟠 Major | ⚡ Quick winInclude trifecta in final risk decision.
trifectaResultis computed but excluded fromoverallSeverityandisDangerous, so a lethal-trifecta hit can still returnsafe: true.Suggested patch
const hfSevIdx = SEVERITIES.indexOf(hfSeverity); - const overallSeverity = SEVERITIES[Math.max(geminiSevIdx, staticSevIdx, skillSevIdx, hfSevIdx)]; + const trifectaSevIdx = trifectaResult.trifectaPresent ? SEVERITIES.indexOf("critical") : SEVERITIES.indexOf("low"); + const overallSeverity = SEVERITIES[Math.max(geminiSevIdx, staticSevIdx, skillSevIdx, hfSevIdx, trifectaSevIdx)]; // Decide "safe" status const isDangerous = overallSeverity === "critical" || overallSeverity === "high" || (geminiResult.isInjection && geminiResult.confidence > 0.6) || skillSpecificResult.hasDangerousToolUsage || - skillSpecificResult.hasNetworkExfiltration; + skillSpecificResult.hasNetworkExfiltration || + trifectaResult.trifectaPresent;Also applies to: 175-176
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/services/SkillScanService.ts` around lines 136 - 156, trifectaResult is computed but not used in the final risk aggregation; update the aggregation to include trifectaResult by adding trifectaResult.severity into the SEVERITIES Math.max calculation that computes overallSeverity, include ...trifectaResult.categories in the categories Set alongside geminiResult/staticResult/skillSpecificResult, and extend the isDangerous boolean to consider trifectaResult's relevant danger flags (e.g., trifectaResult.isTrifecta / trifectaResult.isLethal / trifectaResult.hasDangerousBehavior or similar property your code defines) so a lethal trifecta triggers safe: false; adjust the same logic referenced around overallSeverity and isDangerous (also applicable to the checks around lines 175-176).
🧹 Nitpick comments (4)
src/services/AtlasService.ts (1)
144-153: ⚡ Quick winAdd a timeout to the ATLAS bundle fetch.
The
fetch()call has no timeout, so a hung or slow upstream server could block therefresh()call indefinitely. Consider usingAbortControllerwith a reasonable timeout (e.g., 30s).🛠️ Proposed fix
async refresh(): Promise<AtlasRefreshResult> { // Cache-hit short-circuit: keeps us off the network in CI/loops. if (this.cacheIsFresh()) { this.tryLoadCache(); return { count: this.cache.size, fetchedAt: new Date().toISOString() }; } let bundleJson: any; try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 30_000); - const resp = await fetch(this.bundleUrl); + const resp = await fetch(this.bundleUrl, { signal: controller.signal }); + clearTimeout(timer); if (!resp.ok) { throw new Error(`ATLAS bundle fetch failed: HTTP ${resp.status}`); } bundleJson = await resp.json(); } catch (err: any) { throw new Error(`ATLAS refresh failed: ${err?.message || err}`); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/services/AtlasService.ts` around lines 144 - 153, The fetch to this.bundleUrl in the refresh() flow has no timeout; modify the refresh() method to use an AbortController with a ~30s timeout: create an AbortController, start a setTimeout that calls controller.abort() after 30_000ms, pass controller.signal into fetch(this.bundleUrl), and clear the timeout on success; catch the abort case (DOMException/AbortError) and throw a clear Error like "ATLAS refresh timed out" while preserving other error messages (the current catch that throws `ATLAS refresh failed: ...` should still wrap non-timeout errors). Ensure the timeout is cleaned up to avoid leaks.patterns/mcp-tool-poisoning.json (1)
7-7: Unverified ATLAS technique IDs.All five pattern descriptions note
[unverified ATLAS technique — pending verification]. Per the PR summary, this is a known limitation (ATLAS unverified IDs). Consider tracking verification as a post-merge task.Also applies to: 27-27, 47-47, 67-67, 87-87
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@patterns/mcp-tool-poisoning.json` at line 7, The pattern descriptions in patterns/mcp-tool-poisoning.json currently include the literal tag "[unverified ATLAS technique — pending verification]" in five entries; update each description to remove that bracketed tag (or replace it with a neutral, non-ambiguous note like "verification pending") so the description text is clean, and create a post-merge tracking issue (e.g., ISSUE-XXXX) to verify and, if verified, annotate or update the patterns later; search for the exact string "[unverified ATLAS technique — pending verification]" to locate and fix all occurrences.src/test/manyShotObfuscationTests.ts (1)
58-61: ⚡ Quick winConsolidate to a single
PatternServiceinstance.Lines 58-60 create two
PatternServiceinstances when one suffices. The first instancesvcis used only to callregenerateManifest()and then discarded.♻️ Simplify to single instance
const dir = createTestDir(); - const svc = new PatternService(dir); - svc.regenerateManifest(); - const patSvc = new PatternService(dir); - const checker = new StaticCheckService(patSvc); + const patSvc = new PatternService(dir); + patSvc.regenerateManifest(); + const checker = new StaticCheckService(patSvc);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/manyShotObfuscationTests.ts` around lines 58 - 61, The test unnecessarily constructs two PatternService instances (svc and patSvc); call regenerateManifest() on a single PatternService instance and reuse it when creating StaticCheckService. Replace the pattern where svc is only used to call regenerateManifest() and discarded by invoking PatternService dir once (e.g., create one PatternService, call its regenerateManifest(), then pass that same instance into the StaticCheckService constructor).SPEC.md (1)
42-42: 💤 Low valueSpecify language identifier for fenced code block.
The architecture diagram code block lacks a language identifier.
📝 Add language identifier
-``` +```text ┌─────────────────────────────────────────────────────────────────────────┐As per coding guidelines, markdownlint rule MD040 requires language identifiers on fenced code blocks.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SPEC.md` at line 42, The fenced code block containing the architecture diagram in SPEC.md is missing a language identifier; edit that diagram's opening fence (the architecture diagram code block) to include a language label such as "text" (e.g., change ``` to ```text) so the block complies with markdownlint MD040.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Around line 65-66: Update the stale test-count metrics in CHANGELOG.md: locate
the line containing the string "**426 assertions / 0 failures across 16 suites**
(up from 87 in v1.0.2)" and replace the assertion, failure and suite counts and
the "up from" baseline with the final numbers from the release test run; also
verify the adjacent "20-sample labeled Taste-Tester corpus..." line matches the
final validated sample/test pass counts and update if they changed. Ensure the
text formatting and parentheses remain unchanged.
In `@patterns/markdown-exfil.json`:
- Around line 6-8: The detector only matches Markdown image syntax; update the
"pattern" value for the rule named "Markdown image with suspicious query string"
so it also matches standard Markdown links by allowing either leading "!" or not
(i.e., support both "" and "[](...)"). Modify the regex in the "pattern"
field to accept an optional "!" before the link-start so both image and link
forms with long opaque query values are detected while preserving the existing
query-value length and character constraints.
In `@README.md`:
- Around line 449-454: The OSV.dev `/v1/querybatch` row in the README lists
example package names that don’t match the implemented AI-package allowlist
(e.g., "anthropic-sdk-python", "openai-python", "transformers-js"); update the
README entry so the listed packages exactly match the allowlist identifiers used
by the implementation (replace mismatched names in the bold OSV.dev
`/v1/querybatch` row with the actual package IDs used by the allowlist such as
the canonical registry names the code checks), double-check each token
(langchain, transformers, litellm, mlflow, ollama, llama-index, autogen, crewai,
langgraph, vllm, sglang, anthropic-sdk, openai, etc.) against the allowlist in
the code and make them identical to avoid verification mismatches.
In `@RESEARCH_FEEDS.md`:
- Around line 3-7: Remove the plan-mode/local-path artifact in
RESEARCH_FEEDS.md: locate the section labeled "Plan-mode output" that contains
the environment-specific local absolute path and replace that line with a
repo-relative or generic release-ready description (e.g., reference the doc by
repo-relative path or omit the machine-specific path entirely), ensuring the
file no longer contains any plan-mode instructions or local absolute paths
before merging.
In `@RESEARCH_THREATS.md`:
- Around line 75-86: The Markdown table anchored by the header row "| Item |
Date | Significance |" isn't terminated properly so the following "Sources:"
line is parsed as a table row; fix it by inserting a blank line (one empty
newline) immediately before the "Sources:" line so the table closes cleanly and
"Sources:" becomes a normal paragraph/header rather than part of the table.
In `@scripts/smoke-v1.1.ts`:
- Around line 56-59: The regex used to set looksNetworky (used with
partialOnNetworkErr) has a typo "ecconnreset" so ECONNRESET errors aren't
matched; update the network-error matcher regex (the
/(network|fetch|enotfound|timeout|ecconnreset|ssl|tls|getaddrinfo|429|503)/i
used to compute looksNetworky) to use the correct substring "econnreset" (or
include both "ec" and "econnreset" variants if desired) so ECONNRESET is
properly detected and downgraded to PARTIAL.
- Around line 167-176: The smoke step check_lethal_trifecta unconditionally
records "PASS" even when trifectaAnalyzer.analyze returns trifectaPresent=false
or severity has regressed; update the step to inspect the returned result (the
variable r from trifectaAnalyzer.analyze) and assert expected values (e.g.,
r.trifectaPresent === true and r.severity meets your threshold), then call
record("check_lethal_trifecta", "PASS", ...) only on success and record(...,
"FAIL", ...) (or throw) with diagnostic details when the assertion fails so
failures are surfaced; locate the call site around
tryStep("check_lethal_trifecta", ...) and update handling of r to conditionally
record PASS/FAIL based on r.trifectaPresent and r.severity.
In `@src/mcp/mcpServer.ts`:
- Around line 337-388: Branches for scan_mcp_tool, check_lethal_trifecta,
query_cve, deploy_canary, verify_canary and taste_test call handlers
(mcpToolScanner.scan, trifectaAnalyzer.analyze, unifiedCveCache.query,
canaryService.issueToken, canaryService.checkEcho, tasteTesterService.run) using
blindly-cast args and can throw or start unbounded work on malformed input. Add
explicit runtime validation for required fields and types (e.g., ensure tool is
object for scan_mcp_tool; capabilities/tools/skillContent are correct types for
check_lethal_trifecta; filters conform to QueryCveFilters for query_cve;
context/ttlSeconds types for deploy_canary; content string for verify_canary;
prompt string for taste_test), return a safe structured error response when
validation fails, and only call the corresponding methods after validation.
Ensure the same pattern as check_prompt/scan_skill (guard, validate,
early-return on bad input, then invoke mcpToolScanner.scan /
trifectaAnalyzer.analyze / unifiedCveCache.query / canaryService.issueToken /
canaryService.checkEcho / tasteTesterService.run).
In `@src/services/KevFeedService.ts`:
- Around line 54-59: The loop that climbs directories using
dirname(import.meta.url) can never terminate on non-POSIX roots because it only
checks for "/" — update the traversal in KevFeedService so it also breaks when
the directory stops changing (e.g., dir === dirname(dir)) or when dir equals the
filesystem root (use path.parse(dir).root) as a stop condition; modify the while
condition (and/or add an explicit break) around the existsSync(join(dir,
"package.json")) check so the loop exits on Windows roots, keeping use of
fileURLToPath, dirname, existsSync, join, and maintaining assignment to
this.cacheDir.
- Around line 127-136: The fetch in KevFeedService.refresh() is unbounded and
can hang; wrap the fetch(this.feedUrl) with an AbortController and a timeout
timer (e.g., setTimeout) so the request is aborted after a configurable
interval, pass controller.signal to fetch, clear the timer on success, and catch
AbortError specifically to throw a clear timeout/abort error; ensure body =
await resp.json() only runs when not aborted and that the controller is cleaned
up on all paths.
---
Outside diff comments:
In `@src/services/SkillScanService.ts`:
- Around line 136-156: trifectaResult is computed but not used in the final risk
aggregation; update the aggregation to include trifectaResult by adding
trifectaResult.severity into the SEVERITIES Math.max calculation that computes
overallSeverity, include ...trifectaResult.categories in the categories Set
alongside geminiResult/staticResult/skillSpecificResult, and extend the
isDangerous boolean to consider trifectaResult's relevant danger flags (e.g.,
trifectaResult.isTrifecta / trifectaResult.isLethal /
trifectaResult.hasDangerousBehavior or similar property your code defines) so a
lethal trifecta triggers safe: false; adjust the same logic referenced around
overallSeverity and isDangerous (also applicable to the checks around lines
175-176).
---
Nitpick comments:
In `@patterns/mcp-tool-poisoning.json`:
- Line 7: The pattern descriptions in patterns/mcp-tool-poisoning.json currently
include the literal tag "[unverified ATLAS technique — pending verification]" in
five entries; update each description to remove that bracketed tag (or replace
it with a neutral, non-ambiguous note like "verification pending") so the
description text is clean, and create a post-merge tracking issue (e.g.,
ISSUE-XXXX) to verify and, if verified, annotate or update the patterns later;
search for the exact string "[unverified ATLAS technique — pending
verification]" to locate and fix all occurrences.
In `@SPEC.md`:
- Line 42: The fenced code block containing the architecture diagram in SPEC.md
is missing a language identifier; edit that diagram's opening fence (the
architecture diagram code block) to include a language label such as "text"
(e.g., change ``` to ```text) so the block complies with markdownlint MD040.
In `@src/services/AtlasService.ts`:
- Around line 144-153: The fetch to this.bundleUrl in the refresh() flow has no
timeout; modify the refresh() method to use an AbortController with a ~30s
timeout: create an AbortController, start a setTimeout that calls
controller.abort() after 30_000ms, pass controller.signal into
fetch(this.bundleUrl), and clear the timeout on success; catch the abort case
(DOMException/AbortError) and throw a clear Error like "ATLAS refresh timed out"
while preserving other error messages (the current catch that throws `ATLAS
refresh failed: ...` should still wrap non-timeout errors). Ensure the timeout
is cleaned up to avoid leaks.
In `@src/test/manyShotObfuscationTests.ts`:
- Around line 58-61: The test unnecessarily constructs two PatternService
instances (svc and patSvc); call regenerateManifest() on a single PatternService
instance and reuse it when creating StaticCheckService. Replace the pattern
where svc is only used to call regenerateManifest() and discarded by invoking
PatternService dir once (e.g., create one PatternService, call its
regenerateManifest(), then pass that same instance into the StaticCheckService
constructor).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 661a4984-e2c8-4ce8-806a-18c1e13fe9c0
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (54)
CHANGELOG.mdPLAN.mdREADME.mdRESEARCH_FEEDS.mdRESEARCH_THREATS.mdSKILLS_SECURITY.mdSPEC.mdpackage.jsonpatterns/.gitignorepatterns/llm-threats.jsonpatterns/manifest.jsonpatterns/many-shot.jsonpatterns/markdown-exfil.jsonpatterns/mcp-tool-poisoning.jsonpatterns/policy-puppetry.jsonpatterns/prompt-injection.jsonpatterns/unicode-smuggling.jsonscripts/smoke-v1.1.tssrc/mcp/mcpServer.tssrc/schemas/PatternSchemas.tssrc/services/AtlasService.tssrc/services/CanaryService.tssrc/services/GeminiService.tssrc/services/GhsaGraphQLService.tssrc/services/HuggingFaceService.tssrc/services/KevFeedService.tssrc/services/McpToolScanner.tssrc/services/OsvFeedService.tssrc/services/SecurityService.tssrc/services/SkillScanService.tssrc/services/StaticCheckService.tssrc/services/TasteTesterService.tssrc/services/TrifectaAnalyzer.tssrc/services/UnifiedCveCache.tssrc/services/VulnFeedService.tssrc/services/aiPackageAllowlist.tssrc/test/atlasKevTests.tssrc/test/canaryTests.tssrc/test/fixtures/taste-tester-corpus.jsonsrc/test/helpers/mockAnthropic.tssrc/test/helpers/mockFetch.tssrc/test/huggingFaceTests.tssrc/test/manyShotObfuscationTests.tssrc/test/markdownExfilTests.tssrc/test/mcpToolScannerTests.tssrc/test/patternServiceTests.tssrc/test/policyPuppetryTests.tssrc/test/queryCveTests.tssrc/test/tasteTesterCorpusTests.tssrc/test/tasteTesterTests.tssrc/test/trifectaTests.tssrc/test/unicodeSmugglingTests.tssrc/test/v11SkeletonTests.tssrc/test/vulnFeed2Tests.ts
Generates a PATTERN_INTEGRITY_SECRET in local .env (gitignored) and re-runs regenerateManifest, producing an HMAC signature in patterns/manifest.json. Project still boots fine without the secret (HMAC verification skipped + warning); with the secret, manifest authenticity is cryptographically verifiable. .env.example gains documented entries for every env var introduced in v1.1.0 that was previously undocumented: - HF_TOKEN (Hugging Face Hub security-flag lookups) - ATLAS_REFRESH_INTERVAL_HOURS / KEV_REFRESH_INTERVAL_HOURS (feed TTLs) - TASTE_TESTER_ENABLED + ANTHROPIC_API_KEY + TASTE_TESTER_MODEL/ MAX_TURNS/MAX_TOKENS/TIMEOUT_MS (opt-in sandbox detonator) - CANARY_HMAC_SECRET / CANARY_DEFAULT_TTL_SECONDS (canary tokens) Verification: signed manifest validates; patternServiceTests 29/0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per /claude-api skill guidance: 1. Default model: claude-sonnet-4-6 → claude-opus-4-7 (skill rule: always default to Opus 4.7 unless user explicitly names another). .env.example updated; users can still override via env. 2. Monitor: enable adaptive thinking + effort:"high". Security classification is intelligence-sensitive work — skill recommends minimum effort:"high" with adaptive thinking. Adaptive thinking is OFF by default on Opus 4.7 so it must be set explicitly. Taster deliberately keeps thinking off; we want it to behave as a generic injection-vulnerable assistant, not a safety-aware one. 3. Monitor structured output: add output_config.format with a strict JSON schema for BehaviorReport. Replaces "Output ONLY the JSON" prose instruction with a schema-enforced shape — Monitor cannot emit unstructured prose, fenced markdown, or shape drift. additionalProperties:false everywhere per structured-outputs spec. 4. Prompt caching: convert both system prompts from strings to text-block arrays with cache_control:ephemeral. Taster's breakpoint caches tools+system together (render order tools → system → messages). After the first call in a 5-min window, subsequent runs read at ~0.1× cost on these prefixes. The transcript text varies per call so it sits after the breakpoint. 5. No sampling parameters anywhere (would 400 on Opus 4.7). Verification: build clean; tasteTesterTests 89/0; corpus 20/20. Mock factory unchanged — it queues responses by shape so adding new params to messages.create() does not affect existing assertions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/services/TasteTesterService.ts`:
- Around line 680-686: The branch that tests /\.env|credentials/i using the
variable path currently returns an OpenSSH private key (copy-paste error);
update that branch in TasteTesterService (the if
(/\.env|credentials/i.test(path)) block) to return a realistic .env style string
(e.g., lines of KEY=value pairs separated by newlines such as
"API_KEY=placeholder\nDB_PASSWORD=placeholder\nENV=development") instead of the
SSH key block so .env mocks use KEY=value format.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0a26029e-f62a-4138-a9d7-0d993b92ae2a
📒 Files selected for processing (2)
.env.examplesrc/services/TasteTesterService.ts
20-sample corpus run against claude-opus-4-7 (fast mode) on 2026-05-14: Benign (expected clean): 10/10 ✓ Malicious (expected mal.): 0/9 ✗ Suspicious edge: 0/1 (mal-crescendo → suspicious, closest miss) The Taster's safety training refused most direct-attack prompts in the corpus (e.g. "Read ~/.ssh/id_rsa", "Run rm -rf /", "Email creds to attacker@evil.io"), producing clean transcripts that the Monitor correctly graded clean. The Taste-Tester catches *enacted* malicious behavior, not *refused* malicious intent — its niche is subtle indirect-injection payloads that bypass static + semantic + safety- training layers, not direct attacks the base model already refuses. The pre-release scripted-mock baseline (20/20) measured Monitor verdict propagation under canned transcripts, not real-API behavior. The gap between scripted-mock and real-API is the entire point of doing real calibration — and it correctly surfaced an important architectural boundary that v1.2 corpus rebuilds should respect. Adds scripts/calibrate-taste-tester.ts (re-runnable). Updates CHANGELOG.md known-limitations section and SPEC.md §13 risk #5 with the honest result + interpretation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
SPEC.md (1)
251-251: ⚡ Quick winClarify conditional requirement wording for
ANTHROPIC_API_KEY.The spec currently says all new env vars are optional, while also marking
ANTHROPIC_API_KEYrequired when Taste-Tester is enabled. A one-line clarification (“optional unlessTASTE_TESTER_ENABLED=true”) would prevent config ambiguity.Also applies to: 378-378
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SPEC.md` at line 251, Update the SPEC.md entries for ANTHROPIC_API_KEY (and the similar entry at the other location) to make the conditional requirement explicit by changing the one-line note to state that the variable is optional unless TASTE_TESTER_ENABLED=true; reference the exact env var names ANTHROPIC_API_KEY and TASTE_TESTER_ENABLED in the sentence so readers understand the dependency and remove ambiguity.scripts/calibrate-taste-tester.ts (1)
34-37: ⚡ Quick winAdd documentation linking pricing constants to official Anthropic pricing source.
Current values ($5.00 input, $25.00 output, $6.25 cache-write, $0.50 cache-read per 1M tokens) are accurate per official Anthropic pricing, but these constants lack a reference to their source. Since the hard cap check at line 134 depends directly on these rates, adding a comment or documentation URL will help maintainers quickly verify pricing hasn't drifted when making updates in the future.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/calibrate-taste-tester.ts` around lines 34 - 37, Add a comment above the pricing constants (OPUS_4_7_INPUT_PER_MTOK, OPUS_4_7_OUTPUT_PER_MTOK, OPUS_4_7_CACHE_WRITE_PER_MTOK, OPUS_4_7_CACHE_READ_PER_MTOK) that cites the official Anthropic pricing URL and the date you verified it, so future maintainers can quickly validate these rates; ensure the comment also notes that the hard cap check relies on these values so reviewers know to re-check the source when updating them.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/calibrate-taste-tester.ts`:
- Around line 134-168: The exit logic and summary text are allowing partial-run
success and mislabeling the run as "mocks": update the success gate and
messaging so process.exit only returns 0 when both matches >= 16 and the full
expected corpus was processed (e.g., results.length === EXPECTED_SAMPLE_COUNT or
=== 20) instead of permitting early-abort partial runs (references:
process.exit(...) and results), and change the summary/oneLine string (variable
oneLine) to remove "mocks" (or otherwise reflect that this is a real-API run) so
the output correctly describes the run; keep existing ABORT_ON_FIRST_FAILURE and
COST_HARD_CAP_USD checks but ensure they cause non-zero exit when they produced
a partial run.
- Around line 110-121: The calibration script is missing monitor API token usage
because runMonitor()'s response.usage isn't stored; update the monitor flow to
capture and persist that usage (e.g., add a monitorUsage field on
TasteTesterResult or include it in BehaviorReport), modify
TasteTesterService.runMonitor (call site at TasteTesterService.ts:776) to return
or attach response.usage into the result object, and then alter
scripts/calibrate-taste-tester.ts to aggregate tokens from result.monitorUsage
in addition to result.tasterTranscript (summing input_tokens, output_tokens,
cache_creation_input_tokens, cache_read_input_tokens) so monitor spend is
included in cap accounting.
In `@SPEC.md`:
- Line 42: In SPEC.md update the fenced code block marker (the ``` fence shown)
to include a language tag (e.g., change ``` to ```text) so the block declares
its language and satisfies markdownlint rule MD040; locate the fence in SPEC.md
and add the language identifier to the opening backticks.
- Line 266: Pick a single canonical TTL and make SPEC.md and the config agree:
update the table row that currently reads “MITRE ATLAS (stix bundle) … cache
24h” and the configuration/default for ATLAS_REFRESH_INTERVAL_HOURS (currently
set to 168) so they match the chosen value; ensure any nearby docs/comments that
mention the ATLAS cache duration are updated too to remain consistent.
- Line 136: SPEC.md currently shows an invalid parameter shape for
check_lethal_trifecta; update the signature to match the actual implementation
used in mcpServer.ts and TrifectaAnalyzer.ts by changing the input type to an
object with optional fields (capabilities?: string[]; tools?: string[];
skillContent?: string;) so the SPEC accurately reflects the function parameter
shape for check_lethal_trifecta.
---
Nitpick comments:
In `@scripts/calibrate-taste-tester.ts`:
- Around line 34-37: Add a comment above the pricing constants
(OPUS_4_7_INPUT_PER_MTOK, OPUS_4_7_OUTPUT_PER_MTOK,
OPUS_4_7_CACHE_WRITE_PER_MTOK, OPUS_4_7_CACHE_READ_PER_MTOK) that cites the
official Anthropic pricing URL and the date you verified it, so future
maintainers can quickly validate these rates; ensure the comment also notes that
the hard cap check relies on these values so reviewers know to re-check the
source when updating them.
In `@SPEC.md`:
- Line 251: Update the SPEC.md entries for ANTHROPIC_API_KEY (and the similar
entry at the other location) to make the conditional requirement explicit by
changing the one-line note to state that the variable is optional unless
TASTE_TESTER_ENABLED=true; reference the exact env var names ANTHROPIC_API_KEY
and TASTE_TESTER_ENABLED in the sentence so readers understand the dependency
and remove ambiguity.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 460e8267-d459-435c-9ecd-a95f374464c3
📒 Files selected for processing (3)
CHANGELOG.mdSPEC.mdscripts/calibrate-taste-tester.ts
✅ Files skipped from review due to trivial changes (1)
- CHANGELOG.md
Six parallel-cluster agents addressed the bot-reviewer feedback from PR #5 across correctness, scripts hygiene, and doc consistency. CORRECTNESS - KevFeedService: replace `while (dir !== "/")` with the POSIX/Windows- safe `dirname(parent) === parent` terminator, fixing the non-terminating loop on non-POSIX roots. Add `timeoutMs` (default 30s) + AbortController to `refresh()` so the CISA fetch can no longer hang indefinitely. (#3, #4) - SkillScanService: fold `trifectaResult` into the final risk decision. Previously `hasLethalTrifecta` was reported but did not influence `overallSeverity` or `isDangerous`, so a 3-of-3 lethal trifecta could still return `safe: true`. Now 3-of-3 forces `overallSeverity = critical`, `safe: false`, adds synthetic `lethal_trifecta` category, and emits ATLAS T0024 + T0051 per SPEC §7. 2-of-3 contributes `medium` to overall severity but does not add the composite category. (Outside-diff finding) - StaticCheckService: per-flagGroup loop no longer `break`s on first match — scans all patterns in the group and takes max severity, so Sneaky Bits paired-ZWJ payloads now correctly report `critical` alongside the unicode_smuggling category instead of being stuck at `high`. (#2 — Codex) - mcpServer: add runtime argument validation to all 6 new tool handlers via a `validationError(msg)` helper, matching the pre-existing `check_prompt`/`scan_skill` error-envelope shape. Guards enforce SPEC §4.2 limits (prompt/content size, watchHandle hex shape, ttlSeconds 30-day cap, limit 1-200, etc.). (#5) - markdown-exfil.json: regex widened from `!\[…\]` to `!?\[…\]` so links with the same query-exfil shape are caught alongside images. Benign `[text](url)` links without long opaque queries still pass. (#7) - TasteTesterService: `.env|credentials` mock-router branch returns realistic `KEY=value` shape including the `sk-test-FAKE` tripwire, not the SSH-private-key body. Adds `TasteTesterUsage` accumulator on `TasteTesterResult` so callers (the calibration script) can read real token totals. (#6, #10) - smoke-v1.1.ts: ECONNRESET typo fixed (`ecconnreset` → `econnreset`) with broadened matcher for `etimedout`/`econnrefused`/ `eai_again` and `.toLowerCase()` normalization. `check_lethal_ trifecta` smoke step now actually asserts (positive 3-of-3 + negative 1-of-3) instead of always recording PASS. (#8, #9) - calibrate-taste-tester.ts: replace broken transcript-walking usage extraction with direct read of `result.usage`; gate `exit 0` on `matches >= 16 && !isPartial` and print a PARTIAL RUN warning when the loop broke early. (#10, #11) DOC CONSISTENCY - CHANGELOG.md: test totals corrected to `457 tests / 0 failures across 17 suites at v1.1.0 tag 31868ea` with a note that post-tag cluster fixes adjust further. Added Changed-section bullet documenting the trifecta-in-severity-rollup semantic fix. (#12) - README.md: OSV allowlist row now uses a representative subset of actual package IDs with reference to `src/services/ aiPackageAllowlist.ts`. (#13) - RESEARCH_FEEDS.md: removed committed plan-mode header + local absolute path. (#14) - RESEARCH_THREATS.md: blank line before `Sources:` so the table terminates cleanly (MD055/MD056). (#15) - SPEC.md: added `text` lang tag to the architecture-diagram fence (MD040); fixed `check_lethal_trifecta` input signature to match implementation (`{ capabilities?: string[]; tools?: string[]; skillContent?: string }`); ATLAS cache TTL aligned to 7 days matching `AtlasService.DEFAULT_TTL_MS`. (#16, #17, #18) VERIFICATION - `npm run build` clean - Per-cluster test suites individually green: - atlasKevTests: 24/24 (incl. 3 new timeout asserts) - manyShotObfuscationTests: 27/27 (Sneaky Bits now critical) - trifectaTests: 49/49 - markdownExfilTests: 31/31 (incl. new link positive + benign-link FP) - tasteTesterTests: 89/89 (incl. F's source-purity grep + Test C tripwire) - tasteTesterCorpusTests: 6/6 - v11SkeletonTests: 27/27 - Manifest regenerated with integrity check PASSED, 71 patterns - Pre-existing skillScanTests social-engineering failure (hardcoded SQLi regex matching "security UPDATE FROM Anthropic") confirmed unrelated via `git stash` baseline; flagged for separate follow-up Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review round 1 — addressed in dbc3454All 18 inline findings from CodeRabbit + Codex have been addressed in a single consolidation commit. Each thread above has a reply linking to the specific fix. Quick summary: Correctness fixes (11)
Doc consistency (7)
Outside-diff findingThe trifecta-in-rollup finding from the third CodeRabbit walkthrough is the same fix as the SkillScanService correctness item above. CI note for the maintainerThe Verification
Pre-existing failure flagged for separate triage
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/services/StaticCheckService.ts`:
- Around line 97-100: The simple-mode call to regex.test in StaticCheckService
(used by evaluatePattern / ActivePattern via checkWithPatternService) can mutate
RegExp.lastIndex for global regexes and cause flaky results; fix by ensuring the
regex's lastIndex is reset (or use a fresh RegExp instance) before calling
.test(text) in the simple-mode branch so repeated
evaluatePattern/checkWithPatternService calls do not observe a polluted
lastIndex and match behavior remains deterministic.
In `@src/services/TasteTesterService.ts`:
- Around line 547-552: The final severity calculation currently folds only
monitorReport.intents into intentMax and then into monitorReport.severity;
update the logic to also consider raw deterministic tool evidence from
monitorReport.toolCalls (e.g., entries like exec_shell or transfer_funds) so the
final severity is floored by any high-severity toolCalls. Specifically, when
computing the combined severity (using maxSeverity and monitorReport.severity),
include a reduction over monitorReport.toolCalls to compute a toolCallsMax
severity and then take maxSeverity(monitorReport.severity, intentMax,
toolCallsMax); apply the same fix in the other analogous block that calculates
severity (the block currently around lines 765-792).
- Around line 382-395: The withTimeout<T> helper currently only rejects the
outer promise but doesn't cancel the underlying Anthropic request, so wrap the
call to client.messages.create with an AbortController and pass its signal (or
accept a controller into withTimeout) and ensure controller.abort() is invoked
when the timeout fires; specifically, update withTimeout (and the call sites
that invoke messages.create) to create an AbortController, pass
controller.signal into messages.create options, store the controller in the
closure or parameters, and call controller.abort() inside the setTimeout
rejection branch so messages.create is actually cancelled on timeout.
In `@src/test/atlasKevTests.ts`:
- Around line 224-254: The test can hang if svc.refresh() never settles; wrap
the call inside a hard guard using Promise.race so the test always times out
fast: in the withMockedFetch callback, replace the direct await svc.refresh()
usage with awaiting Promise.race([svc.refresh(), new Promise((_, rej) =>
setTimeout(() => rej(new Error("test hard timeout")), <short-ms>))]) and assert
against that rejection; update references around KevFeedService, refresh, and
the local timing/assertion logic (start/elapsed/threw/msg) so the test fails
quickly when the hard guard fires.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b77dc863-9acc-4be8-be6b-9c6646f91b93
📒 Files selected for processing (17)
CHANGELOG.mdREADME.mdRESEARCH_FEEDS.mdRESEARCH_THREATS.mdSPEC.mdpatterns/manifest.jsonpatterns/markdown-exfil.jsonscripts/calibrate-taste-tester.tsscripts/smoke-v1.1.tssrc/mcp/mcpServer.tssrc/services/KevFeedService.tssrc/services/SkillScanService.tssrc/services/StaticCheckService.tssrc/services/TasteTesterService.tssrc/test/atlasKevTests.tssrc/test/manyShotObfuscationTests.tssrc/test/markdownExfilTests.ts
✅ Files skipped from review due to trivial changes (5)
- patterns/manifest.json
- SPEC.md
- RESEARCH_FEEDS.md
- RESEARCH_THREATS.md
- CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (9)
- patterns/markdown-exfil.json
- src/test/markdownExfilTests.ts
- scripts/smoke-v1.1.ts
- src/mcp/mcpServer.ts
- src/services/SkillScanService.ts
- README.md
- src/services/KevFeedService.ts
- src/test/manyShotObfuscationTests.ts
- scripts/calibrate-taste-tester.ts
CodeRabbit re-reviewed after dbc3454 and flagged 4 additional Major correctness/robustness issues. Three parallel cluster agents addressed them; rest of the round-2 comments were ack-only on the round-1 fixes. CORRECTNESS - StaticCheckService.evaluatePattern: reset `regex.lastIndex = 0` before both `.test()` (simple mode) and `text.matchAll(regex)` (threshold mode). Previously, cached `g`-flagged ActivePattern instances were mutated across calls — the next call started search from the previous match position and could silently miss subsequent matches. Live exposure was strongest in McpToolScanner.walkStrings, which iterates one cached pattern array across every string field of a tool descriptor: each field after the first match was at risk of being under-scanned. Added Test 14 in patternServiceTests with 6 assertions covering both modes + cross-text reuse. - TasteTesterService: propagate AbortSignal to messages.create() options so the underlying SDK call is actually cancelled when withTimeout() fires. Refactored withTimeout to accept a factory `(signal: AbortSignal) => Promise<T>`; both controller.abort() and reject() fire on timeout. MinimalAnthropicClient interface widened to mirror the real SDK's optional second RequestOptions arg. Prior behavior leaked in-flight requests after timeout and burned tokens. - TasteTesterService: floor final severity with raw deterministic tool-call evidence. Rollup now takes MAX across (monitorReport .severity, intentMax from Monitor's intents, rawIntentMax from raw toolCalls via intentsFromToolCalls + TOOL_DEFAULTS). A poisoned/ lazy Monitor returning `{intents:[],verdict:"clean",severity:"safe"}` while the Taster actually called exec_shell now correctly bumps to `critical`. New Test G asserts this scenario. monitorFallback path already derived everything from raw tool calls; no change needed. TEST ROBUSTNESS - atlasKevTests Test B3 (KEV timeout): wrap svc.refresh() in Promise.race with a 2000ms hard guard. If the AbortController/timer plumbing ever regresses, the test fails fast with a distinct diagnostic instead of blocking the whole runner indefinitely. VERIFICATION - npm run build clean - Per-cluster suites individually green: - patternServiceTests 35/35 (29 → 35 with Test 14) - tasteTesterTests 93/93 (89 → 93 with Test G) - tasteTesterCorpusTests 6/6 (corpus 20/20) - atlasKevTests 24/24 (B3 still passes with the new hard guard) - unicodeSmuggling 12/12, markdownExfil 31/31, manyShotObfuscation 27/27, policyPuppetry 18/18, mcpToolScanner 18/18, v11Skeleton 27/27 - Full `npm test` chain hangs on the network-bound atlasKev live-CISA fetch in this dev environment (pre-existing — CI will give the authoritative result on a clean runner) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Major coverage expansion (v1.0.2 → v1.1.0) addressing the late-2025/2026 shift toward LLM-native and agentic attacks. Implements 13 vertical-slice passes per PLAN.md, preserving every v1.0 capability.
6 new MCP tools:
scan_mcp_tool— Tool-poisoning scanner (SHA-256 drift, ignore-previous + imperative + HTML-comment lints)check_lethal_trifecta— Willison's private-read + untrusted-fetch + egress classifierquery_cve— Unified read across NVD + OSV + GHSA-REST + GHSA-GraphQL + KEV + ATLASdeploy_canary/verify_canary— UUIDv4 canary tokens (HMAC-signed state, TTL'd) for memory/RAG poisoning detectiontaste_test— User-designed dual-agent sandbox detonator (Taster + Monitor, gated behindTASTE_TESTER_ENABLED)5 new feed sources: OSV.dev
/v1/querybatch(AI-package allowlist), GHSA GraphQL ecosystem-filtered, MITRE ATLAS taxonomy (7-day cache + offline fallback), CISA KEV (24h cache + severity escalator), Hugging Face HubsecurityStatus.6 new detection categories:
unicode_smuggling,policy_puppetry,markdown_exfil,mcp_tool_poisoning,many_shot, plus 8 hand-curatedprompt_injectionIOCs and obfuscation expansion (Cyrillic homoglyphs, Base32, hex, Sneaky Bits encoder).Architecture:
PatternEntry.atlasTechnique?field (additive),evaluatePattern()extracted as shared helper,SkillScanResultgainshasLethalTrifecta/huggingFaceSecurityFlags/atlasTechniques[],VulnFeedResultgainsperSource: {nvd, ghsaRest, ghsaGraphql, osv}counts.Defense in depth, not silver bullet: A 2026 meta-study of 78 defense papers found adaptive attacks still beat ~85% of single defenses. v1.1.0 stacks five complementary layers — disclaimer added to README.
See SPEC.md for full architecture, RESEARCH_THREATS.md and RESEARCH_FEEDS.md for the threat-landscape work that drove this design.
Test plan
npm run build— TypeScript cleannpm test— 457/0 across 17 suites (up from 87 in v1.0.2)scripts/smoke-v1.1.ts— 11/0 PASS end-to-end, exercises every new toolnpx tsx scripts/regenerateManifest.ts— manifest integrity PASSED (71 patterns)Known limitations (carried into SPEC §13)
2512.16962— narrative citation only; no code path depends on the exact ID.AML.T0070,AML.T0071) — flagged[unverified]in fallback table; live STIX fetch attempts canonical resolution.MAX_TURNS=5— calibrated only against scripted-mock baseline (20/20). A real-API calibration run (~$0.50–$2 in Anthropic spend) against the 20-sample corpus insrc/test/fixtures/taste-tester-corpus.jsonwould surface the real accuracy number; deferred until a future cycle.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Detection / Patterns
Feeds & Taxonomy
Testing & Docs
Configuration