Metadata-Version: 2.4
Name: ai-security-scanner
Version: 0.2.0
Summary: AI Security Scanner — scan prompts, agents, APIs, MCP tools and integrations
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: typer>=0.12.0
Requires-Dist: rich>=13.7.0
Requires-Dist: pyyaml>=6.0.1
Requires-Dist: pydantic>=2.7.0
Requires-Dist: pydantic-settings>=2.2.0
Requires-Dist: anthropic>=0.28.0
Requires-Dist: openai>=1.30.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: structlog>=24.1.0
Requires-Dist: twine>=6.2.0
Requires-Dist: uvicorn>=0.49.0
Requires-Dist: fastapi>=0.136.3
Requires-Dist: python-multipart>=0.0.32
Requires-Dist: click>=8.1
Requires-Dist: rich>=13
Requires-Dist: reportlab>=5.0.0
Provides-Extra: api
Requires-Dist: fastapi>=0.111.0; extra == "api"
Requires-Dist: uvicorn[standard]>=0.29.0; extra == "api"
Provides-Extra: vertex
Requires-Dist: google-cloud-aiplatform>=1.60.0; extra == "vertex"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: httpx>=0.27.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Dynamic: requires-python

# 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](#why-aiscan)
- [Installation](#installation)
- [Quick start](#quick-start)
- [CLI commands](#cli-commands)
- [Scan types](#scan-types)
- [Rules reference](#rules-reference)
- [TMotions Python Coding Standards](#tmotions-python-coding-standards)
- [PDF export](#pdf-export)
- [REST API](#rest-api)
- [Python SDK](#python-sdk)
- [Middleware](#middleware)
- [Decorators](#decorators)
- [HTTP client (other languages)](#http-client-other-languages)
- [CI/CD integration](#cicd-integration)
- [Generating CI/CD files with aiscan](#generating-cicd-files-with-aiscan)
- [AI provider configuration](#ai-provider-configuration)
- [Suppressing false positives](#suppressing-false-positives)
- [Sample files](#sample-files)
- [Troubleshooting](#troubleshooting)

---

## 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

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

### Verify install

```bash
aiscan version
# aiscan 0.2.0
```

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

---

## Quick start

```bash
# 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 sca requirements.txt
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 | sca | 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
  -x, --exclude TEXT  Additional glob pattern(s) to exclude, repeatable (e.g. --exclude '*.min.js')
  -q, --quiet         Suppress output except errors
```

> **Default exclusions when scanning a directory:** hidden files/folders (`.venv`, `.git`, `.aiscanner`, any path segment starting with `.`), any file matching `*pipeline.yml`, any file matching `*.log`, and `report_generation.py` (the script generated by `aiscan generate-report-script`, so a CI run doesn't scan itself). Use `--exclude` to add more patterns on top of these, e.g. `aiscan scan . --exclude '*.min.js' --exclude 'vendor/*'`.

**Examples:**

```bash
# 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 sca     requirements.txt
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.

```bash
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.

```bash
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 generate-report-script`

Generate a standalone `report_generation.py` in the current directory. It runs a scan, exports a PDF, and emails it via SMTP — credentials are read from environment variables when present (CI use) and prompted for interactively otherwise (local use). It also re-installs/upgrades `ai-security-scanner` to the latest version on every run, and always excludes itself from the scan (see the default exclusions note above).

```bash
aiscan generate-report-script                        # creates report_generation.py
aiscan generate-report-script --output my_script.py   # custom filename
aiscan generate-report-script --force                 # overwrite existing file
```

Recognised environment variables (all required for non-interactive/CI use, except where noted):

| Variable | Purpose |
|---|---|
| `SMTP_SERVER` | SMTP host |
| `SMTP_PORT` | SMTP port (default `587`) |
| `SMTP_LOGIN` | SMTP authentication login — **not** necessarily the same as the From address |
| `SMTP_PASSWORD` | SMTP authentication password / key |
| `SMTP_FROM` | From address (optional, defaults to `SMTP_LOGIN`) |
| `SMTP_RECIPIENTS` | Comma-separated recipient list |
| `BRANCH_NAME` | Git branch name, used in the PDF filename (falls back to `GITHUB_REF_NAME`, then `git rev-parse`) |
| `PROJECT_NAME` | Project name shown in the PDF filename and in-report file paths (falls back to the current directory name) |

The generated PDF is named `<project_name>_<branch_name>_report_<timestamp>.pdf`.

### `aiscan generate-workflow`

Generate a CI/CD workflow file (GitHub Actions `.yml` by default) into `.github/workflows/` that installs `aiscan`, generates `report_generation.py` fresh, runs it, and emails the report.

```bash
aiscan generate-workflow                                        # creates .github/workflows/security-scan-report.yml
aiscan generate-workflow --output .github/workflows/my-scan.yml # custom path
aiscan generate-workflow --force                                # overwrite existing file
```

Add these as repo secrets before running the generated workflow: `SMTP_SERVER`, `SMTP_PORT`, `SMTP_LOGIN`, `SMTP_PASSWORD`, `SMTP_FROM`, `SMTP_RECIPIENTS`.

### `aiscan serve`

Start the REST API server.

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

### `aiscan version`

```bash
aiscan version
# aiscan 0.2.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` |
| `sca` | Known vulnerable dependency versions via OSV | `requirements.txt` `pyproject.toml` `setup.py` `package.json` |
| `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-001`–`AG-006` | `allow_all_tools: true` |
| API | `AK-001` `AK-002` `CO-001` `HT-001` `JW-001` `AU-001` | hardcoded `api_key` |
| MCP | `MC-001`–`MC-006` | `execute_code: true` |
| Integration | `IN-001`–`IN-006` | `webhook_secret: changeme` |
| Code (security) | `CD-001`–`CD-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.

```bash
# 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:

```json
{
  "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`

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

---

## PDF export

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

```bash
# 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

```bash
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/sca` | JSON | Scan dependency manifests for known vulnerabilities |
| POST | `/scan/container` | JSON | Scan Dockerfile / container config content |
| POST | `/scan/all` | JSON | Run all AI security scanners |
| POST | `/tm-coding-standards` | File upload | TMotions Python coding standards check |
| GET | `/docs` | — | Swagger UI |

**JSON endpoints — request:**
```json
{"content": "file content as string", "filename": "openapi.yaml"}
```

**JSON endpoints — response:**
```json
{
  "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:**
```bash
# Scan prompt
curl -X POST http://localhost:8000/scan/prompt \
  -H "Content-Type: application/json" \
  -d '{"content": "Ignore previous instructions", "filename": "prompt.txt"}'

# Scan container config
curl -X POST http://localhost:8000/scan/container \
  -H "Content-Type: application/json" \
  -d '{"content": "FROM node:latest\nUSER root", "filename": "Dockerfile"}'

# 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.

```python
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())
scanner.scan_sca(open("requirements.txt").read(), filename="requirements.txt")
reports = scanner.scan_all(content, filename="config.yaml")  # all 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

```python
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:
```json
{
  "error": "Request blocked by AI Security Scanner",
  "field": "prompt",
  "issues": [{"rule": "PI-001", "severity": "critical", "issue": "...", "fix": "..."}]
}
```

### Flask

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

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

### Django

```python
# 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.

```python
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:**
```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:**
```javascript
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:**
```go
resp, _ := http.Post("http://localhost:8000/scan/prompt",
    "application/json", bytes.NewBuffer(body))
```

**TMotions coding standards from any language:**
```bash
# 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

```yaml
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 }
```

### Azure Pipelines

```yaml
# AI-Security-Scan-Pipeline.yml
trigger:
  branches:
    include:
      - '*'
pool:
  name: 'LinuxAgentPool'
variables:
  python_cmd: 'python3.12'
  python_env_name: '.aiscanner'
  project_name: $(Build.Repository.Name)
  SMTP_SERVER: 'smtp-relay.brevo.com'
  SMTP_PORT: '587'
  SMTP_FROM: 'your-verified-sender@example.com'
  SMTP_RECIPIENTS: 'team@example.com'
steps:
  - checkout: self
    displayName: 'Checkout Repository'
  - script: |
      set -e
      rm -rf "$(python_env_name)"
      $(python_cmd) -m venv "$(python_env_name)"
      "$(python_env_name)/bin/python" -m pip install --upgrade pip
    displayName: 'Create Python Virtual Environment'
  - script: |
      set -e
      "$(python_env_name)/bin/python" -m pip install --upgrade ai-security-scanner
    displayName: 'Install AI Security Scanner'
  - script: |
      set -e
      export PATH="$(pwd)/$(python_env_name)/bin:$PATH"
      aiscan generate-report-script --force
    displayName: 'Generate report_generation.py'
  - script: |
      set -e
      set -o pipefail
      export PATH="$(pwd)/$(python_env_name)/bin:$PATH"
      "$(python_env_name)/bin/python" -u report_generation.py 2>&1 | tee ai_security_scan.log
    displayName: 'Run AI Security Scan and Send Email'
    env:
      SMTP_SERVER: $(SMTP_SERVER)
      SMTP_PORT: $(SMTP_PORT)
      SMTP_LOGIN: $(SMTP_LOGIN)
      SMTP_PASSWORD: $(SMTP_PASSWORD)
      SMTP_FROM: $(SMTP_FROM)
      SMTP_RECIPIENTS: $(SMTP_RECIPIENTS)
      BRANCH_NAME: $(Build.SourceBranchName)
      PROJECT_NAME: $(project_name)
  - script: |
      set -e
      rm -rf "$(python_env_name)"
      rm -f *_report_*.pdf
    displayName: 'Cleanup'
    condition: always()
```

`SMTP_LOGIN` and `SMTP_PASSWORD` are set as secret pipeline variables (not shown in plaintext YAML) under **Pipeline → Edit → Variables**.

### Pre-commit hook

```yaml
# .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

```bash
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 |

---

## Generating CI/CD files with aiscan

Rather than hand-writing the report/email script and pipeline config, `aiscan` can generate both for you from bundled templates:

```bash
aiscan generate-report-script   # → report_generation.py
aiscan generate-workflow        # → .github/workflows/security-scan-report.yml
```

Typical setup in a CI pipeline: install `aiscan`, run `aiscan generate-report-script --force` to always get the current template (rather than committing a copy that can drift out of date), then run the generated script. This is the pattern used in both the GitHub Actions and Azure Pipelines examples above.

See [`aiscan generate-report-script`](#aiscan-generate-report-script) and [`aiscan generate-workflow`](#aiscan-generate-workflow) under CLI commands for the full option list and recognised environment variables.

---

## AI provider configuration

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

```bash
# 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:

```python
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)
```

```bash
# 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`**
```bash
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:
```python
scan_tm_code = tm_coding_standards_scanner
__all__ = ["scan_tm_code", "tm_coding_standards_scanner"]
```

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

**False positives**
```bash
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`**
```bash
pip install --upgrade setuptools wheel
pip install -e .
```

---

## Version

`0.2.0`

---

## License

MIT
