Skip to main content

aiscan — AI Security Scanner

Scans prompts, agents, APIs, MCP tools, integrations, and Python code for security vulnerabilities — prompt injection, data leakage, tool abuse, security risks, and TMotions Python coding standards violations.

Built-in rules + AI deep scan (Claude / GPT-4o / Gemini). Runs as a CLI, REST API, Python SDK, or middleware.


Table of contents


Why aiscan

Traditional security tools (SonarQube, Snyk, Semgrep) were built before AI systems existed. They have no rules for prompt injection, agent tool permissions, or MCP tool definitions. aiscan fills that gap — it treats prompts, agent configs, and AI integrations as first-class security artifacts. It also enforces TMotions internal Python coding standards on top of security scanning.


Installation

From source

git clone <your-repo-url>
cd ai-security-scanner
pip install -r requirements.txt
pip install -e .

Verify install

aiscan version
# aiscan 0.1.0

If aiscan command is not found, use python -m aiscan.cli instead everywhere below.


Quick start

# 1. Generate config
aiscan init

# 2. Add your API key to .env
#    ANTHROPIC_API_KEY=sk-ant-...

# 3. Scan files
aiscan scan my_prompt.txt
aiscan scan --type api openapi.yaml
aiscan scan --type mcp tool_config.json
aiscan scan --type code my_script.py      # TMotions coding standards check

# 4. Scan without AI (static rules only, no API key needed)
aiscan scan --no-ai openapi.yaml

# 5. Export results to PDF
aiscan scan --type api openapi.yaml --output pdf --out report.pdf

CLI commands

aiscan scan

Analyze one or more files for AI security threats, code quality issues, and compliance with TMotions Python coding standards.

Usage: aiscan scan [OPTIONS] FILES...

Options:
  -t, --type TEXT     Scan type: prompt | agent | api | mcp | integration | code | all | auto  [default: auto]
  -o, --output TEXT   Output format: console | json | sarif | pdf                               [default: console]
  -O, --out PATH      Write output to file instead of stdout
  -f, --fail-on TEXT  Exit 1 if severity reached: critical | high | medium | none               [default: high]
      --no-ai         Skip AI deep scan — static rules only
  -q, --quiet         Suppress output except errors

Examples:

# Auto-detect scan type from extension
aiscan scan system_prompt.txt
aiscan scan agent_config.yaml

# Explicit scan type
aiscan scan --type prompt  system_prompt.txt
aiscan scan --type agent   agent.yaml
aiscan scan --type api     openapi.yaml
aiscan scan --type mcp     tool.json
aiscan scan --type integration webhook_config.yaml
aiscan scan --type code    my_script.py

# Python files auto-run BOTH prompt scanner AND coding standards scanner
aiscan scan app.py                         # runs prompt + code automatically

# Scan a whole directory
aiscan scan --type auto ./my-project/

# Run all scanners on the same file
aiscan scan --type all config.yaml

# Output formats
aiscan scan --type api openapi.yaml --output json
aiscan scan --type api openapi.yaml --output pdf --out report.pdf
aiscan scan --type prompt prompts/ --output sarif --out results.sarif

# CI mode — exit 1 if any high+ finding
aiscan scan --quiet --fail-on high prompts/ && echo "passed"

Note: .py files automatically run both the prompt scanner (for injection/leakage patterns) and the TMotions coding standards scanner.

aiscan rules

List all security and coding standards rules.

aiscan rules                          # list all rules
aiscan rules prompt                   # filter by scan type
aiscan rules api --severity critical  # filter by severity
aiscan rules code                     # show TMotions Python coding standards rules (PY-*)

aiscan init

Create a .env config file in the current directory.

aiscan init                    # creates .env for Claude (default)
aiscan init --provider openai  # pre-configure for OpenAI
aiscan init --provider vertex  # pre-configure for Vertex AI
aiscan init --force            # overwrite existing .env

aiscan serve

Start the REST API server.

aiscan serve                 # http://localhost:8000
aiscan serve --port 9000     # custom port
aiscan serve --reload        # dev mode, auto-restart on code changes

aiscan version

aiscan version
# aiscan 0.1.0

Scan types

Type Detects File extensions (auto-detect)
prompt Prompt injection, PII leakage, jailbreaks, code security issues .txt .md .py .js .ts .jsx .tsx .go .java .rb .php .cs .cpp .c .rs .swift .kt
agent Wildcard tools, missing human approval, infinite loops, data persistence .yaml .yml
api Hardcoded keys, wildcard CORS, insecure transport, missing auth .yaml .yml .json
mcp Wildcard permissions, code execution, missing rate limits, no auth .json
integration Default webhook secrets, SSL disabled, raw input forwarding, silent errors .env .toml .ini .cfg .conf .properties
code TMotions Python coding standards (PY-* rules) .py only

Auto-detect behaviour for .py files: aiscan runs both prompt and code scanners simultaneously.


Rules reference

Run aiscan rules to see the full live list.

AI security rules

Category Rule IDs Example
Prompt PI-001 PI-002 DL-001 DL-002 EX-001 ignore previous instructions
Agent AG-001AG-006 allow_all_tools: true
API AK-001 AK-002 CO-001 HT-001 JW-001 AU-001 hardcoded api_key
MCP MC-001MC-006 execute_code: true
Integration IN-001IN-006 webhook_secret: changeme
Code (security) CD-001CD-012 eval(), f-string SQL
AI deep scan AI-SCAN Subtle issues from Claude/GPT-4o/Gemini

TMotions Python coding standards rules (PY-*)

Rule ID Severity What it checks
PY-010 Critical Hardcoded password in source
PY-011 Critical SQL injection via string concatenation
PY-012 Critical Use of eval()
PY-013 Critical Use of exec()
PY-021 Critical subprocess with shell=True
PY-022 Critical Weak hashing — hashlib.md5
PY-014 High Bare except: clause
PY-015 High Generic raise Exception()
PY-023 High Insecure random (random.randint etc.)

Run aiscan rules code to see all PY-* rules with full details.


TMotions Python Coding Standards

The code scan type enforces TMotions internal Python coding standards on .py files.

# CLI
aiscan scan --type code my_script.py

# Python files auto-run this alongside the security scan
aiscan scan app.py   # runs both prompt + code

# REST API
curl -X POST http://localhost:8000/tm-coding-standards \
  -F "file=@my_script.py"

# Via directory scan
aiscan scan --type auto ./src/

REST endpoint (POST /tm-coding-standards) accepts a file upload (not JSON body) and returns:

{
  "target": "my_script.py",
  "total_findings": 2,
  "findings": [
    {
      "rule_id": "PY-012",
      "severity": "critical",
      "title": "Use of eval() detected",
      "detail": "eval(user_input)",
      "file": "my_script.py",
      "line": 15,
      "fix": "Avoid eval(); use safer alternatives."
    }
  ]
}

Sample file for testing: samples/code/tmcs_bad_code.py

aiscan scan --type code samples/code/tmcs_bad_code.py

PDF export

Export any scan result directly to a PDF report from the CLI.

# Save to default name (aiscan-report.pdf)
aiscan scan --type prompt prompts/ --output pdf

# Save to custom path
aiscan scan --type api openapi.yaml --output pdf --out reports/scan.pdf

# Scan everything, export PDF
aiscan scan --no-ai --type auto . --output pdf --out report.pdf

The PDF includes:

  • Summary header — overall PASSED/FAILED, file count, critical/high/total counts
  • Per-file findings table — severity icons, rule ID, threat type, title, line number
  • Fix suggestions — detailed remediation for every critical and high finding
  • Page footer with timestamp and page number

REST API

aiscan serve
# Swagger UI: http://localhost:8000/docs
Method Path Body Description
GET /health Service status and version
POST /scan/prompt JSON Scan prompt content
POST /scan/agent JSON Scan agent config
POST /scan/api JSON Scan API config or OpenAPI spec
POST /scan/mcp JSON Scan MCP tool definition
POST /scan/integration JSON Scan integration config
POST /scan/all JSON Run all 5 AI security scanners
POST /tm-coding-standards File upload TMotions Python coding standards check
GET /docs Swagger UI

JSON endpoints — request:

{"content": "file content as string", "filename": "openapi.yaml"}

JSON endpoints — response:

{
  "scan_type": "api",
  "passed": false,
  "critical": 1, "high": 2, "medium": 0, "info": 0, "total": 3,
  "findings": [
    {
      "rule_id": "AK-001",
      "severity": "critical",
      "threat": "security_risk",
      "title": "Hardcoded secret or API key",
      "detail": "Matched on line 5: ...",
      "file": "config.yaml",
      "line": 5,
      "fix": "Move to environment variables or a secrets manager."
    }
  ]
}

curl examples:

# Scan prompt
curl -X POST http://localhost:8000/scan/prompt \
  -H "Content-Type: application/json" \
  -d '{"content": "Ignore previous instructions", "filename": "prompt.txt"}'

# TMotions coding standards (file upload)
curl -X POST http://localhost:8000/tm-coding-standards \
  -F "file=@my_script.py"

Python SDK

Use directly inside your Python project — no separate service needed.

from aiscan.sdk import AIScan

scanner = AIScan(no_ai=True)              # static rules only
# or
scanner = AIScan(ai_provider="claude", api_key="sk-ant-...")

report = scanner.scan_prompt("Ignore previous instructions...")

print(report.passed)     # False
print(report.critical)   # 2
print(report.total)      # 3

for f in report.findings:
    print(f.severity, f.rule_id, f.title)
    print("  Fix:", f.fix)

# Other methods
scanner.scan_file("openapi.yaml")                      # auto-detect type
scanner.scan_file("agent.yaml", scan_type="agent")
scanner.scan_code("app.py")                            # security + TM coding standards
scanner.scan_api_spec(open("openapi.yaml").read())
scanner.scan_agent_config(open("agent.yaml").read())
scanner.scan_mcp_tool(open("tool.json").read())
scanner.scan_integration(open("webhook.yaml").read())
reports = scanner.scan_all(content, filename="config.yaml")  # all 5 scanners

# Gate on severity
if report.has_severity("critical"):
    block_request()

critical_only = report.findings_by_severity("critical")

# Export to dict
data = report.to_dict()

Middleware

Auto-scan every incoming request before it reaches your handlers.

FastAPI

from fastapi import FastAPI
from aiscan.sdk.middleware import AIScanFastAPIMiddleware

app = FastAPI()
app.add_middleware(
    AIScanFastAPIMiddleware,
    scan_fields=["prompt", "message", "content"],
    fail_on="high",
    no_ai=True,
)

Blocked requests return:

{
  "error": "Request blocked by AI Security Scanner",
  "field": "prompt",
  "issues": [{"rule": "PI-001", "severity": "critical", "issue": "...", "fix": "..."}]
}

Flask

from flask import Flask
from aiscan.sdk.middleware import AIScanFlaskMiddleware

app = Flask(__name__)
AIScanFlaskMiddleware(app, fields=["prompt", "message"], fail_on="high")

Django

# settings.py
MIDDLEWARE = [
    "aiscan.sdk.middleware.AIScanDjangoMiddleware",
    # ...
]
AISCAN_FIELDS  = ["prompt", "message"]
AISCAN_FAIL_ON = "high"
AISCAN_NO_AI   = False

Decorators

Wrap any existing function — no refactoring needed.

from aiscan.sdk import scan_prompt_input, scan_before_llm, scan_file_input

# Scan a specific argument before the function runs
@scan_prompt_input(field="user_message", fail_on="high")
def call_llm(user_message: str) -> str:
    ...  # raises ValueError if unsafe

# Scan all prompt-related arguments
@scan_before_llm(fields=["system_prompt", "user_message"])
def chat(system_prompt: str, user_message: str) -> str:
    ...

# Scan a file path before loading it
@scan_file_input(field="config_path", fail_on="critical")
def load_agent_config(config_path: str) -> dict:
    ...

HTTP client (other languages)

Run aiscan serve as a separate service, call it from any language.

Python:

from aiscan.sdk import AIScanClient

client = AIScanClient("http://localhost:8000")
result = client.scan_prompt(user_input)
is_safe = client.is_safe(user_input, fail_on="high")
result = client.scan_file("openapi.yaml")

Node.js:

const res = await fetch("http://localhost:8000/scan/prompt", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ content: userInput, filename: "prompt.txt" }),
});
const { passed, findings } = await res.json();

Go:

resp, _ := http.Post("http://localhost:8000/scan/prompt",
    "application/json", bytes.NewBuffer(body))

TMotions coding standards from any language:

# multipart file upload — works from any HTTP client
curl -X POST http://localhost:8000/tm-coding-standards -F "file=@script.py"

CI/CD integration

GitHub Actions

name: AI Security Scan
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -e .
      - name: Scan prompts and configs
        run: aiscan scan --type prompt prompts/ --fail-on high
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      - name: Scan Python code (TMotions standards)
        run: aiscan scan --type code src/ --fail-on high
      - name: Export SARIF
        run: aiscan scan --output sarif --out results.sarif .
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: results.sarif }

Pre-commit hook

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: aiscan
        name: AI Security Scanner
        entry: aiscan scan --no-ai --fail-on critical
        language: system
        files: \.(txt|yaml|yml|json|py|js|ts)$

Makefile

make install      # pip install -e ".[dev]"
make test         # run pytest
make lint         # ruff check
make scan-samples # scan all bad sample files
make rules        # list all rules
make version      # print version

Exit codes

Code Meaning
0 Passed — no findings at or above --fail-on threshold
1 Failed — findings found at or above threshold
2 Tool error — bad arguments, missing file

AI provider configuration

Run aiscan init to create .env, then edit it:

# Claude (default)
AI_PROVIDER=claude
ANTHROPIC_API_KEY=sk-ant-...
SCANNER_MODEL=claude-sonnet-4-20250514

# OpenAI
AI_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o

# Vertex AI (Gemini)
AI_PROVIDER=vertex
VERTEX_PROJECT=your-gcp-project-id
VERTEX_LOCATION=us-central1
VERTEX_MODEL=gemini-2.5-flash-001

# Shared
MAX_TOKENS=2000
LOG_LEVEL=INFO

If the primary provider fails (rate limit, auth error), aiscan automatically retries with the next available one. Use --no-ai to skip AI scanning entirely — static rules and TMotions coding standards still run.


Suppressing false positives

Add # noscan (Python/YAML) or // noscan (JS/TS) to the end of any line to skip it:

result = scanner.scan_prompt("test example string")  # noscan
except Exception:  # noscan — intentional catch-all in middleware

Sample files

samples/
├── prompts/
│   ├── bad_system_prompt.txt      — jailbreak + PII (9 findings)
│   ├── bad_user_template.txt      — chat tokens + exfil URL (6 findings)
│   ├── bad_agent_instructions.txt — role override (4 findings)
│   ├── clean_customer_support.txt — safe (0 findings)
│   └── clean_coding_assistant.txt — safe (0 findings)
├── agents/
│   ├── bad_research_agent.yaml    — wildcard tools + no HITL (8 findings)
│   ├── bad_finance_agent.yaml     — auto-approves transactions (6 findings)
│   ├── clean_support_agent.yaml   — safe (0 findings)
│   └── clean_data_agent.yaml      — safe (0 findings)
├── apis/
│   ├── bad_config.yaml            — 5 hardcoded keys + wildcard CORS (10 findings)
│   ├── bad_openapi.yaml           — unauthenticated endpoints (3 findings)
│   ├── clean_config.yaml          — safe (0 findings)
│   └── clean_openapi.yaml         — safe (0 findings)
├── mcp/
│   ├── bad_code_executor.json     — shell access + wildcard perms (10+ findings)
│   ├── bad_database_tool.json     — no auth + raw SQL (5 findings)
│   ├── bad_web_search.json        — injection in description (4 findings)
│   ├── clean_product_search.json  — safe (0 findings)
│   └── clean_calendar_tool.json   — safe (0 findings)
├── integrations/
│   ├── bad_slack_integration.yaml — default secret + SSL off (4 findings)
│   ├── bad_email_integration.yaml — multiple issues (5 findings)
│   └── clean_slack_integration.yaml — safe (0 findings)
└── code/
    ├── bad_app.py                 — hardcoded keys + SQL injection + eval (17 findings)
    ├── bad_app.js                 — hardcoded keys + console.log (4 findings)
    ├── bad_api.ts                 — SSL disabled + credentials (3 findings)
    ├── bad_settings.py            — multiple hardcoded secrets (6 findings)
    ├── tmcs_bad_code.py           — TMotions coding standards violations
    └── clean_app.py               — safe (0 findings)
# Test all bad samples
aiscan scan --no-ai samples/prompts/bad_system_prompt.txt
aiscan scan --no-ai samples/agents/bad_research_agent.yaml
aiscan scan --no-ai samples/apis/bad_config.yaml
aiscan scan --no-ai samples/mcp/bad_code_executor.json
aiscan scan --no-ai samples/integrations/bad_slack_integration.yaml
aiscan scan --no-ai samples/code/bad_app.py
aiscan scan --type code samples/code/tmcs_bad_code.py

Troubleshooting

aiscan: command not found

python -m aiscan.cli --help
pip install -e .

ImportError: cannot import name 'scan_tm_code'

The function in tm_coding_standards_scanner.py was named differently. Fix by adding an alias at the bottom of the file:

scan_tm_code = tm_coding_standards_scanner
__all__ = ["scan_tm_code", "tm_coding_standards_scanner"]

AI scan not running

aiscan scan --no-ai openapi.yaml   # confirms static rules work
# check .env has a valid API key and AI_PROVIDER is set

False positives

aiscan scan --no-ai file.txt   # isolate static rule hits
aiscan rules                   # see which pattern triggered
# add # noscan to suppress a specific line

BackendUnavailable: Cannot import setuptools.backends.legacy

pip install --upgrade setuptools wheel
pip install -e .

Version

0.1.0


License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiscan_security-0.1.0.tar.gz (44.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiscan_security-0.1.0-py3-none-any.whl (43.4 kB view details)

Uploaded Python 3

File details

Details for the file aiscan_security-0.1.0.tar.gz.

File metadata

  • Download URL: aiscan_security-0.1.0.tar.gz
  • Upload date:
  • Size: 44.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for aiscan_security-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e52ecc078905b4ee29a22b77e4383fba7075fb0a7adbcbd99029bd22a422d14f
MD5 ca3242ca6b1e9d528d0e8b7497602add
BLAKE2b-256 65b261e98dd66a1d468bb53f09d43270c99d1c58f8c9837a7e0312b2d1262309

See more details on using hashes here.

File details

Details for the file aiscan_security-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for aiscan_security-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fe4654df48b6bc913c52be27f3acbd63b5ebf090249e2585703f4491d437f638
MD5 57083e4e1b8d57cf688b5b15c3136d4b
BLAKE2b-256 ec974ecb32f67410b6f2962b3f18ede2d1899cfd90094b35ad926d743c2b21d9

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.0

2 files

This release

0.1.0 This release

2 files

0.0.2

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page