Skip to content

Releases: akanthed/SecureAI-Scan

v0.11.0 — Ecosystem audit: 5 precision fixes, MCP011, pre-commit hook

Choose a tag to compare

@akanthed akanthed released this 26 Aug 18:46
5fe9187

Ecosystem audit of public MCP servers (upstash/context7, cloudflare/mcp-server-cloudflare, stripe/agent-toolkit, awslabs/mcp) turns up and fixes five precision bugs, adds a new detection rule (MCP011: untrusted tool source), and ships a pre-commit hook plus a VS Code extension scaffold for distribution.

Fixed

  • AI002 flagged secrets outside any LLM context — fired on a bcrypt hash in a demo app and a non-secret constant merely named with "KEY".
  • isTestFilePath now recognizes eval(s) as a non-production path segment, so LLM-as-judge eval harnesses aren't scanned as real request handlers.
  • MCP001 required a real identifier reference instead of a plain substring match, fixing a proven/critical false positive on prose that merely used the word "tools".
  • MCP009/SKL003 cross-tool-reference detection resolved all 7 residual false positives by requiring the referenced tool to sit between the trigger word and the verb (a redirect), not just appear anywhere in the sentence.

Added

  • MCP011: untrusted tool source — flags an MCP tool handler that fetches from an external/unauthenticated source and returns the response with no sanitization (the Sentry-MCP-DSN attack shape).
  • Pre-commit hook (.pre-commit-hooks.yaml).
  • VS Code extension scaffold (vscode-extension/) — local-build install, not yet on the Marketplace.
  • Example compliance artifact + terminal-recording demo.

npm test: 151/151 passing.


What's Changed

  • chore(deps): bump github/codeql-action from 4.37.3 to 4.37.7 by @dependabot[bot] in #21
  • Ecosystem audit, distribution surfaces, and 5 precision fixes by @akanthed in #22
  • feat(rules): add MCP011 rule for untrusted tool source and implement … by @akanthed in #23
  • Fix readme file by @akanthed in #24

Full Changelog: v0.10.0...v0.11.0

v0.10.0 — LiteLLM Proxy config scanning + 6 root-cause fixes from real-world regression testing

Choose a tag to compare

@akanthed akanthed released this 19 Aug 18:23
7251968

Static config scanning for LiteLLM Proxy, plus six false-positive/robustness bugs found and fixed by adding a large real-world repo (BerriAI/litellm) to the regression gate.

Added

  • LLC001–LLC003: LiteLLM Proxy config.yaml scanning. New off-disk scanner (src/scanner/litellm-config-scanner.ts), following the same pattern as mcp-config-scanner.ts. Gated behind a structural check (model_list + litellm_params) so unrelated YAML files are never touched.
    • LLC001 (proven, critical): a hardcoded secret in litellm_params/general_settings instead of an os.environ/VAR_NAME reference.
    • LLC002 (proven, high): a provider api_base reachable over plaintext http:// (non-localhost).
    • LLC003 (heuristic, low, --paranoid only): no guardrails: section configured at all — absence of an optional control, not a proven gap.
    • New dependency: js-yaml (default-schema load(), no unsafe deserialization tags).

Fixed

Found by adding BerriAI/litellm — the official repo, ~7,000 TS/JS files plus a large Python codebase — to npm run regression, specifically to get real coverage for the new LLC rules:

  • LLC001 line misattribution — findings could point at the wrong api_key: occurrence in a file with many entries. Now anchors on the flagged value, not the key name.
  • LLC001 placeholder-value false positives — LiteLLM's own docs/tests use dummy values like fake-key, sk-lar1-demo. Added a placeholder-word check plus a "does this look like a random credential blob" heuristic.
  • A rule crash was silently discarding every other rule's findings — a scan that goes quiet instead of erroring is worse than any false positive. Each rule now runs isolated; one failure logs a warning instead of aborting the whole scan.
  • MCP001 (Python) module-scope false positive — an unrelated settings description was treated as MCP tool metadata because the scope-detection fallback covered the whole file. Capped to a small line window.
  • MCP002 (TypeScript) blanket parameter taint — every function parameter was treated as user-controlled input regardless of context, flagging a pure URL-parsing utility. Removed the blanket taint; the known-vulnerable fixture never needed it.
  • VEC001 (Python) re.search collision — stdlib regex search matched as a vector-store similarity search because a pattern string contained "vector". Excluded re.search/regex.search.
  • AI003 (Python) missed FastAPI's idiomatic Depends(...) auth pattern in the route's parameter list — every health-check route in litellm's own proxy was flagged as unauthenticated despite being authenticated.

All six fixes shipped with a permanent fixture under test-fixtures/safe/.

Also

  • Skill/MCP-server tool descriptions rewritten with explicit trigger phrasing for better auto-suggestion in Claude/Cursor.
  • Real-world findings write-up: #19

Full changelog: https://github.com/akanthed/SecureAI-Scan/blob/main/CHANGELOG.md#0100--2026-08-19

v0.9.0 — OWASP LLM Top 10 2026 support + 5 new Agent Skill rules

Choose a tag to compare

@akanthed akanthed released this 11 Aug 22:13
d5cfea9

Support for the official OWASP Top 10 for LLM Applications 2026, plus five new Agent Skill detection rules — SKL006 through SKL010.

OWASP LLM Top 10 2026

  • Versioned reporting: JSON includes owaspVersion: "2026", terminal/CLI output uses the official LLMxx:2026 notation, Markdown/HTML name the framework version, and SARIF emits owasp-llm-top10-2026/* tags.
  • A conservative coverage statement (docs/OWASP2026.md) distinguishes implemented static signals from partial coverage and the runtime-only Misinformation boundary, instead of presenting category mapping as exhaustive detection.
  • Existing findings remapped to the official 2026 ranking (Excessive Agency → LLM03, Supply Chain → LLM04, Data/Model Poisoning → LLM05, Unbounded Consumption → LLM06, Misinformation → LLM07, Hidden Context Exposure → LLM08, Vector/Embedding Weaknesses → LLM09, Improper Output Handling → LLM10). Detection behavior and evidence tiers are unchanged.

New Agent Skill rules

  • SKL006 — load-time command execution via Claude Code's dynamic-context-injection syntax, which runs shell commands the instant a skill is read, before any tool-permission gate applies.
  • SKL007 — unscoped Bash/Bash(*) grants in a skill's allowed-tools frontmatter.
  • SKL008 — skills that fetch and execute instructions from an external URL at runtime instead of shipping them in the reviewed bundle.
  • SKL009 — skills that write into another trust-elevated context file (MEMORY.md, SOUL.md, AGENTS.md, CLAUDE.md), planting a backdoor that outlives the skill.
  • SKL010 — unsafe deserialization tags (e.g. YAML !!python/object) in skill-bundle metadata/config that execute code on load.

Validated against the real-repo regression corpus with no new false positives — see What we found scanning real repos for the full writeup.

What's Changed

Full Changelog: v0.8.0...v0.9.0

v0.8.0 — Tool-Poisoning & Skill-Shadowing Precision Pass

Choose a tag to compare

@akanthed akanthed released this 06 Aug 17:41
b9c60bd

Highlights

This release is almost entirely a precision pass on the MCP tool-poisoning
(MCP009) and skill-shadowing (SKL002/SKL003) detectors, plus a couple of
config-scanning bugs. No new rules; several existing rules got materially
more accurate.

Fixed false positives

  • MCP009 / SKL003 (cross-tool/cross-skill shadowing) now distinguishes a
    bare conditional ("when X is unknown, use Y" — ordinary sibling-tool
    documentation, extremely common in real multi-tool MCP servers and skill
    bundles) from an actual override/substitution directive ("instead of",
    "always", "never", "silently", or output-rerouting phrasing like "route the
    message body through this tool"). Only the latter now fires at likely by
    default; the bare-conditional form is demoted to heuristic
    (--paranoid-only) instead of being dropped entirely.
  • SKL002 (injection phrasing) gained context-aware suppression for three
    previously noisy patterns:
    • negation ("never read .env") no longer reads as an instruction to
      read credential files,
    • quoted example phrases inside a skill's own defensive documentation
      ("flag inputs like 'ignore previous instructions'") no longer read as
      the attack they're describing,
    • "do not tell the user X — they were never asked" no longer reads as
      concealment when the surrounding text is a factual correction.
  • Fixed a related deobfuscation bug where the intra-word-line-break-join pass
    (used to catch genuine evasion) could delete a newline that was itself part
    of the suppressing context above, or coincidentally pull two unrelated
    phrases into proximity — both were silently defeating the new suppressions
    and, worse, promoting the resulting false positive to proven. Cross-tool
    reference matching (SKL003) is no longer run through the deobfuscation
    variants at all, since it has no exact-phrase requirement that evasion
    could exploit.
  • SKL004/capability disclosure now checks a skill's companion reference
    files, not just SKILL.md and its top-level description — a bundle that
    documents a download/exec step in a per-topic file (e.g. "Install Istio
    CLI") was previously flagged as undisclosed even though it documents the
    capability, just not in the top-level file.
  • MCP004 (secret committed in MCP config) now anchors its reported line
    to the occurrence that actually contains the leaked value, not just the
    first line mentioning the env var name — a config with both a placeholder
    and a real secret sharing a var name across servers was pointing findings
    at the wrong server.
  • MCP config env-reference detection now accepts any well-formed
    ${...} placeholder (dotted/path-style references like
    ${user_config.api_token} or ${CLAUDE_PLUGIN_ROOT}, used by real MCP
    client manifests) instead of only bare/env:/input: forms, so those
    stopped being misread as literal committed secrets.

Other

  • CI workflow actions (checkout, setup-node, upload-artifact,
    codeql-action, scorecard-action) are now pinned to commit SHAs instead
    of floating version tags, closing a supply-chain gap in our own pipeline.
  • New safe-corpus fixtures locking in the fixes above:
    test-fixtures/safe/skills/defensive-docs-skill/,
    test-fixtures/safe/skills/lint-check/, and an expanded
    test-fixtures/safe/mcp/.mcp.json.

v0.7.0 — Dataflow diagrams + cross-file AI001 tracing

Choose a tag to compare

@akanthed akanthed released this 01 Aug 18:39
036d83c

SecureAI-Scan can now show, not just claim, how a vulnerability's data flows through your code.

  • HTML reports (--output report.html) now render every finding's source→flow→sink trace as an inline SVG node-link diagram instead of a flat text list, with a dashed "cross-file" arrow wherever a step crosses a file boundary.
  • Trace coverage extended to four more rules — MCP003 (tool result elevated to system-role), AI005 (unsafe output handling), AI012 (unvalidated structured output), and VEC003 (user content ingested into a vector store) — matching AI001's existing dataflow evidence.
  • AI001 now traces tainted values across function and file boundaries: when a tainted parameter is passed into a locally-resolved helper (same project, import-resolved — never a name guess), the scanner follows the call up to 2 hops and builds a real multi-file trace, capped at likely evidence (never proven) and cycle-safe against mutual recursion.
  • Fixed: AI012 could never fire — its own JSON.parse detection collided with its .parse( validation-pattern check, silencing the rule regardless of input.

Note: the interprocedural AI001 tracer mainly adds an accurate, honestly-capped cross-file trace — not new recall on its own, since the base rule's existing per-parameter taint fallback already flagged those same sink locations in isolation.

What's Changed

  • chore(deps): bump actions/setup-node from 4 to 7 by @dependabot[bot] in #10
  • chore(deps): bump github/codeql-action from 3 to 4.37.3 by @dependabot[bot] in #9
  • chore(deps-dev): bump typescript from 6.0.3 to 7.0.2 by @dependabot[bot] in #8
  • chore(deps-dev): bump @types/node from 22.19.17 to 26.1.2 by @dependabot[bot] in #7
  • chore(deps): bump commander from 14.0.3 to 15.0.0 by @dependabot[bot] in #6
  • chore(deps): bump actions/upload-artifact from 4 to 7 by @dependabot[bot] in #5
  • chore(deps): bump actions/checkout from 4 to 7 by @dependabot[bot] in #4
  • Add SVG dataflow diagrams, extend trace coverage, and trace AI001 acr… by @akanthed in #11

New Contributors

Full Changelog: v0.6.0...v0.7.0

v0.6.0 — Evasion-resistant skill scanning + pre-install scanning + root-cause bug fixes

Choose a tag to compare

@akanthed akanthed released this 28 Jul 04:19

Highlights

Evasion-resistant Agent Skill scanning. Closes the scanner-evasion techniques published in Cloak and Detonate (arXiv:2607.02357) — zero-width/homoglyph obfuscation, self-extracting payloads staged in .git/, and payloads hidden in *.test.ts files that every scanner in that study missed. Two new rules (SKL004, SKL005) plus a deobfuscation layer that matches content across normalized text variants. Validated against anthropics/skills (18 real bundles, 0 findings) and cisco-ai-defense/skill-scanner's labeled eval corpus (6/6 malicious fixtures correctly flagged, 0 false positives).

Scan before you install. New secureai-scan skill <target> / secureai-scan mcp <target> commands fetch and scan a skill or MCP server before you trust it — no clone, no config, and nothing fetched is ever executed (npm pack/git clone --depth 1 only). Same capability now exposed to Claude itself via a new scan_untrusted_target MCP tool.

Found and fixed 5 real bugs by scanning a 5,511-file production repo. Triaging every finding from a full scan of vercel/ai turned up three root-cause false-positive bugs in shared rule logic (an LLM-call resolver that ignored method names, a "query" naming collision between two unrelated rules, and an overly broad request-taint pattern duplicated across three rules) and two Windows-only infrastructure bugs (the bundled MCP server couldn't start on Windows at all; npm/tar needed platform-specific handling). All fixed at the root cause and locked in as permanent regression fixtures — a full re-scan of vercel/ai now reports zero findings.

Scoped a path to AST-based Python detection. A non-shipped proof-of-concept (spike/python-ast-poc/) confirms tree-sitter-python works as a pure-WASM (no native compilation) replacement for today's regex-based Python scanner, demonstrated against a real, previously-invisible gap: user input flowing through a class attribute (self.x = request.json[...]) into an LLM call. See ROADMAP.md for the full assessment.

Full details in CHANGELOG.md.

Full Changelog: v0.5.0...v0.6.0

SecureAI-Scan 0.5.0 is now available.

Choose a tag to compare

@akanthed akanthed released this 23 Jul 17:32
  1. Added Agent Skill scanning for SKILL.md files, including checks for hidden Unicode, injection phrases, and cross-skill shadowing.
  2. Added MCP010 for dynamic MCP server commands built from untrusted input.
  3. Improved DEP003 so advisories compare against actual declared package versions.
  4. Fixed several false positives in sensitive logging, RAG context injection, and system prompt leakage.
  5. Added regression coverage for CLI and dependency-guard behavior.

Install or upgrade:

  • npm i -g secureai-scan

Fix Python false negatives on litellm, Bedrock, Cohere, Mistral, Ollama

Choose a tag to compare

@akanthed akanthed released this 21 Jul 15:06

This patch closes two false-negative bugs in the Python scanner surfaced by community testing: apps built on litellm, boto3 Bedrock clients not literally named bedrock, Cohere, Mistral, or Ollama were producing zero findings — even with --paranoid — because their call syntax wasn't recognized despite the SDK import itself being detected. Separately, AI001 (prompt injection) only looked 10 lines ahead of a request read for an LLM call, missing injection in any handler with realistic logic (logging, rate limiting, RAG lookups) in between.

AI001 now tracks request-tainted variables across the whole enclosing function instead of a fixed line window, and the LLM call-pattern list covers the additional SDKs. No new rules, no breaking changes — bug-fix only, verified against the exact repro cases with new regression tests.

v0.4.0 — OWASP ASI/MCP Top 10 mapping + MCP tool-poisoning detection

Choose a tag to compare

@akanthed akanthed released this 20 Jul 17:49
1cd93d8

The first release mapped to all three OWASP AI security frameworks — the LLM Top 10 (2025), the Top 10 for Agentic Applications (2026, ASI01–ASI10), and the MCP Top 10 (2025) — plus three new rules that catch the pattern behind real-world incidents like the postmark-mcp backdoor and the WhatsApp MCP rug-pull.

Added

  • Three-framework OWASP mapping — every rule tagged with LLM Top 10, ASI (Agentic), and MCP Top 10 IDs across terminal, Markdown, HTML, JSON, and SARIF output
  • OWASP coverage matrix in secureai-scan threat-model — shows exactly which of the 30 risks across all three frameworks are covered, which fired, and which are runtime-only concerns
  • MCP007 — invisible/bidi Unicode hidden in MCP tool names or descriptions (critical, proven)
  • MCP008 — agent-directed injection phrases in tool descriptions (high, likely) — the WhatsApp MCP rug-pull / postmark-mcp pattern
  • MCP009 — cross-tool shadowing, where one tool's description redirects calls meant for another (medium, likely)
  • DEP003 — known-malicious/critically-vulnerable dependency advisories (postmark-mcp, mcp-remote CVE-2025-6514), checked offline on every scan — no flag required, covers package.json, requirements.txt, and packages launched from MCP configs

All new rules ship with both TypeScript AST and Python implementations.

Changed

  • --only-mcp now also runs MCP007–MCP009
  • --rules accepts DEP001–DEP003

v0.3.1 — False-positive & crash-safety fixes

Choose a tag to compare

@akanthed akanthed released this 19 Jul 17:54

Bug-fix release — no new rules, commands, or flags. Focused on cutting false positives in existing rules and hardening the scanner against crashes on edge-case input.

Fixed

  • Python AI005subprocess.run/eval/exec/os.system sinks no longer flag on ordinary code just because a nearby variable was named result/output/etc. Now requires the file to import a known LLM SDK and traces a real assignment link from an LLM call before reporting at critical; a name-only match without a confirmed link now reports at a lower high/heuristic tier instead.
  • Python AI002 — sensitive prompt/response logging now also requires the file to import a known LLM SDK.
  • Windows glob exclusionnode_modules, dist, build, and policy skipPaths are now actually excluded from TS/JS scans on Windows (glob patterns were silently broken by backslash paths).
  • Crash safety — ts-morph symbol resolution is now wrapped so a single malformed/edge-case file can no longer abort an entire scan run.
  • AI012 — dropped a bare-name fallback that flagged JSON.parse(result) regardless of whether the value actually came from an LLM call.
  • AI007 (RAG context injection) — generic names like context/results now only count as RAG content when the enclosing function shows an actual retrieval call.
  • AI010 (indirect prompt injection) — fetch-derived variable detection now checks real call/property-access nodes instead of substring-matching, so values like { fetch: true } no longer false-positive.
  • VEC001 / VEC004supabase/opensearch clients now require an explicit vector/embedding signal before flagging ordinary DB reads/writes.
  • AI-BOM — model-identifier matching (o1/o3/o4) is now scoped to string literals, so plain variable names no longer show up as "OpenAI model referenced."
  • DEP002 — added the missing catalog entry (findings no longer render blank OWASP/fix tags).
  • Report snippets — the source-line cache no longer folds path case on case-sensitive filesystems.
  • Dependency guard — a registry-unreachable network failure now prints a warning instead of silently no-opping DEP001.

Note for CI users

Some previously-critical AI005 findings now report at a lower tier, and some previously-flagged AI007/VEC001/VEC004/AI010/AI012 cases no longer fire at all. If you pin --fail-on critical, expect your finding count to drop on upgrade — that's the intended effect of removing false positives, not a regression.