Skip to content

feat(context): add @atr provider for AI agent threat scanning#12194

Open
eeee2345 wants to merge 4 commits into
continuedev:mainfrom
eeee2345:feat/context-atr-provider
Open

feat(context): add @atr provider for AI agent threat scanning#12194
eeee2345 wants to merge 4 commits into
continuedev:mainfrom
eeee2345:feat/context-atr-provider

Conversation

@eeee2345
Copy link
Copy Markdown

@eeee2345 eeee2345 commented Apr 21, 2026

Summary

Adds an @atr context provider that scans the currently open file against the Agent Threat Rules (ATR) ruleset — 314 MIT-licensed YAML rules for AI agent threats (prompt injection, MCP tool poisoning, context exfiltration, skill-package compromise).

Invoking @atr in chat attaches each HIGH/CRITICAL rule match as a context item so the model sees the finding alongside the code.

Why this fits Continue

  • Mirrors the shape of ProblemsContextProvider — reads current-file state, returns one ContextItem per finding.
  • Optional dependency: agent-threat-rules is lazily imported. Users who don't install it see a one-line install hint; no bundle-size impact for users who don't use @atr.
  • Zero network calls, zero telemetry — rules are loaded locally from the npm package.
  • Complementary to @problems (TS/lint diagnostics); @atr targets AI-specific attack patterns that static analyzers don't cover.

Context on ATR

ATR is an open detection standard for AI agent threats. The ruleset is already integrated into two upstream ecosystems:

Benchmarks: 97.1% recall on NVIDIA garak's 666 in-the-wild jailbreak corpus; 100% recall on a 498-sample labeled SKILL.md benchmark; 0 false positives on a 432-sample benign skill corpus.

Source: https://github.com/Agent-Threat-Rule/agent-threat-rules
Paper: https://doi.org/10.5281/zenodo.19178002

Files

File Change
core/context/providers/ATRSecurityContextProvider.ts new, ~150 lines
core/context/providers/ATRSecurityContextProvider.test.ts new, 4 jest cases
core/context/providers/index.ts +2 lines (import + register)

Tests

cd core && npx jest context/providers/ATRSecurityContextProvider

Covers: HIGH/CRITICAL match surfacing, benign file reports "clean", missing dependency returns friendly install hint, missing open file handled gracefully.

Alternatives considered

If maintainers prefer external packaging over an in-tree provider, I can ship this as @continuedev/context-atr (or in the ATR org) using Continue's CustomContextProvider path. Opening here first to gauge preference — happy to pivot to an external package + a small docs PR linking to it.

Also happy to split into two PRs (provider + tests) or narrow scope if that fits the review cadence better.


Summary by cubic

Adds a new @atr context provider that scans the current file with the Agent Threat Rules and surfaces HIGH/CRITICAL findings as chat context items. Uses local rules from agent-threat-rules, no network calls.

  • New Features

    • Add ATRSecurityContextProvider and register as @atr.
    • Scans the open file and returns one context item per HIGH/CRITICAL rule match.
    • Graceful states: clean report, missing dependency hint, and no-open-file message.
  • Bug Fixes

    • Retry engine load after failures by clearing the cached rejection, so transient errors and mid-session installs recover.
    • Treat empty open files as valid scan targets; only the no-file case shows the no-file message.
    • Fix TypeScript build errors: use a variable for the dynamic agent-threat-rules import (avoids TS2307) and move Node imports to top-of-file to resolve TS2339.
    • Defer import.meta.url access to support non-ES2020/CommonJS subprojects (avoids TS1343), with a safe __filename fallback.

Written for commit 97ed375. Summary will update on new commits. Review in cubic

Introduces ATRSecurityContextProvider that loads the Agent Threat Rules
ruleset (optional 'agent-threat-rules' npm dependency) and surfaces
HIGH/CRITICAL matches for the current file as chat context items.

Mirrors ProblemsContextProvider structure; lazy-imports the dependency so
users who don't install it see a friendly install hint instead. Zero
network calls, zero telemetry.
@eeee2345 eeee2345 requested a review from a team as a code owner April 21, 2026 23:05
@eeee2345 eeee2345 requested review from sestinj and removed request for a team April 21, 2026 23:05
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Apr 21, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="core/context/providers/ATRSecurityContextProvider.ts">

<violation number="1" location="core/context/providers/ATRSecurityContextProvider.ts:49">
P2: Failed engine initialization is cached permanently, so the provider cannot recover or retry after a transient load failure.</violation>

<violation number="2" location="core/context/providers/ATRSecurityContextProvider.ts:104">
P2: Empty open files are incorrectly treated as missing files due to a falsy check on `file.contents`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread core/context/providers/ATRSecurityContextProvider.ts
Comment thread core/context/providers/ATRSecurityContextProvider.ts Outdated
@eeee2345
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

recheck

@eeee2345
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

…ovider

Three small fixes in one pass:

1. Cubic P2: failed engine initialization was cached permanently. If the
   first call to getEngine() threw (e.g. transient dynamic-import failure
   or the optional dependency being installed mid-session), enginePromise
   stayed pinned to the rejected promise and every subsequent call returned
   the same rejection. Now the catch block resets enginePromise to null so
   the next invocation can retry.

2. Cubic P2: empty open files were treated as missing files. The old
   `if (!file || !file.contents)` evaluated to true on a freshly-opened
   empty file (contents is the empty string, which is falsy) and surfaced
   the "no file to scan" message. Now the check uses
   `typeof file.contents !== "string"` so empty files are scanned (zero
   matches is itself a useful signal).

3. Prettier: existing `highSeverity = matches.filter(...)` had a line
   break inside the arrow function that prettier 3.3.3 reformats to a
   single line under default printWidth. Applied prettier --write to
   close that nit so prettier-check passes.

Test seams (__setEngine, __setEngineError, __resetEngine) are unaffected.

Signed-off-by: Adam Lin <adam@agentthreatrule.org>
@eeee2345
Copy link
Copy Markdown
Author

@cubic-dev-ai — pushed 14947d5 addressing both P2 findings on core/context/providers/ATRSecurityContextProvider.ts:

  • Line 49: failed engine init is no longer cached permanently. The catch block now clears enginePromise = null before re-throwing so the next invocation can retry after a transient failure (e.g. dynamic-import blip, or the optional agent-threat-rules dependency being installed mid-session).

  • Line 104: empty open files are no longer treated as missing files. The check is now typeof file.contents !== "string" instead of !file.contents. Empty files are valid scan targets — zero matches is itself a useful signal.

Same push also applies prettier --write to close the one formatting nit that was failing prettier-check (a multi-line arrow inside matches.filter(...) that prettier 3.3.3 reformats to a single line under default printWidth). The lint + test-matrix checks should re-evaluate from here.

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 17, 2026

@cubic-dev-ai — pushed 14947d5 addressing both P2 findings on core/context/providers/ATRSecurityContextProvider.ts:

  • Line 49: failed engine init is no longer cached permanently. The catch block now clears enginePromise = null before re-throwing so the next invocation can retry after a transient failure (e.g. dynamic-import blip, or the optional agent-threat-rules dependency being installed mid-session).

  • Line 104: empty open files are no longer treated as missing files. The check is now typeof file.contents !== "string" instead of !file.contents. Empty files are valid scan targets — zero matches is itself a useful signal.
    ...

@eeee2345 Both fixes look correct in the commit.

Engine retry (line 49): Clearing enginePromise = null inside the catch before re-throwing means a transient failure (or mid-session npm install) won't permanently brick the provider. Good catch — the original code had the classic "rejected promise cached forever" bug.

Empty file check (line 104): typeof file.contents !== "string" correctly distinguishes between undefined (no file), "" (empty file), and a real string. Empty files are valid scan targets, and returning ATR: clean for them is the right signal.

Prettier: The single-line .filter() matches what prettier 3.3.3 produces at default printWidth, so the formatting check should clear.

The test matrix should re-evaluate green from here — all four test cases (match surfacing, benign/clean, missing dependency, no open file) are already wired and passed on the first push. Nothing in this commit breaks those seams.

eeee2345 added 2 commits May 18, 2026 07:16
CI surfaced two TypeScript build errors after the prettier fix landed:

1. TS2307 on line 51: `Cannot find module 'agent-threat-rules'`.
   The dependency is optional (not in core/package.json), so the static
   string literal in `await import("agent-threat-rules")` triggers
   tsc's module-resolution check at build time. Switching to a variable
   for the module name keeps tsc from resolving against an installed
   package while the actual dynamic import still works at runtime; the
   surrounding try/catch handles the not-installed case unchanged.

2. TS2339 on line 56: `Property 'createRequire' does not exist on type
   '{ default: typeof Module; ... }'`. The dynamic `await import("node:module")`
   typed the result with only the default export visible. Moving
   `createRequire` (and `dirname`/`join`) to static top-of-file imports
   resolves the type while keeping the same runtime behavior — these
   are Node stdlib modules and were never optional.

Test seams (__setEngine, __setEngineError, __resetEngine) and runtime
behavior are unchanged. The previous P2 fixes (engine-init clearing on
failure, `typeof file.contents !== "string"` check) are preserved.

Signed-off-by: Adam Lin <adam@agentthreatrule.org>
binary-checks and vscode-checks failed with TS1343: 'import.meta' is
only allowed under ES2020+ module targets. binary/tsconfig.json and
extensions/vscode/tsconfig.json both use `"module": "commonjs"` (with
ES2022 target), and they type-check across the core/ source tree via
project references, so a direct `import.meta.url` in core/ rejects
their builds even though core/'s own tsconfig.npm.json uses ESNext.

Defer the import.meta access through `new Function("return
import.meta.url")` so tsc only sees a string-literal eval. At runtime
the IIFE returns the URL under ESM (core/ npm build), or falls back to
`__filename` under CommonJS (binary/, vscode/). The provider itself
only runs at runtime inside core/ — binary/vscode just need to type-
check the source without rejecting it.

Verified that the file now compiles under both module targets locally
(tsc against binary/tsconfig.json and core/tsconfig.npm.json).

Signed-off-by: Adam Lin <adam@agentthreatrule.org>
@eeee2345
Copy link
Copy Markdown
Author

For reviewer context — the single remaining CI failure is unrelated to this PR:

test (ubuntu-latest, 18) → src/onboarding.test.ts > onboarding config flag handling > should handle empty string config path
Error: Test timed out in 30000ms

That test lives in extensions/cli/src/onboarding.test.ts (not in this PR's diff — the only files changed here are core/context/providers/ATRSecurityContextProvider.ts/.test.ts and core/context/providers/index.ts). Test result was 1 failed / 1846 passed / 29 skipped — a single empty-string-config-path test timing out at 30s.

This looks like a pre-existing flaky onboarding test rather than something caused by the ATR provider. The same test (ubuntu-latest, 18) slot failed across the three earlier pushes on this branch (different commit shas, same failing test) even when my code changes were in different shapes (prettier fix, TS module-target fix, import.meta defer). Happy to re-trigger on a maintainer's nod if it does not pass on the next merge-queue rerun.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant