{"id":"37690238-57f6-4a48-83da-07fd47a2a10f","slug":"security-ai-scanner","kind":"skill","name":"security-ai-scanner","summary":"AI-powered security scanner for source code — agentic LLM analysis with SARIF output","owner":"elvezjp","repoUrl":"https://github.com/elvezjp/security-ai-scanner","license":"MIT","licenseTier":"permissive","trust":"community","stars":5,"match":null,"fewStars":false,"format":"skill","createdAt":"2026-08-09T06:48:28.000Z","addedAt":"2026-10-21T17:53:06.361Z","logoUrl":"https://avatars.githubusercontent.com/u/52337053?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools"],"tags":[],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-25T22:03:17.093Z","sample":false,"url":"https://conduits.dev/tools/security-ai-scanner","language":"Python","transports":["stdio"],"install":{"pypi":"security-ai-scanner"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# security-ai-scanner\n\n[English](https://github.com/elvezjp/security-ai-scanner/blob/main/README.md) | [日本語](https://github.com/elvezjp/security-ai-scanner/blob/main/README_ja.md)\n\n[![Elvez](https://img.shields.io/badge/Elvez-Product-3F61A7?style=flat-square)](https://elvez.co.jp/)\n[![IXV Ecosystem](https://img.shields.io/badge/IXV-Ecosystem-3F61A7?style=flat-square)](https://elvez.co.jp/ixv/)\n[![PyPI version](https://img.shields.io/pypi/v/security-ai-scanner?style=flat-square)](https://pypi.org/project/security-ai-scanner/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](https://opensource.org/licenses/MIT)\n[![Python](https://img.shields.io/badge/Python-3.11+-blue?style=flat-square&logo=python&logoColor=white)](https://www.python.org/)\n[![Stars](https://img.shields.io/github/stars/elvezjp/security-ai-scanner?style=social)](https://github.com/elvezjp/security-ai-scanner/stargazers)\n\n![security-ai-scanner execute demo](https://raw.githubusercontent.com/elvezjp/security-ai-scanner/main/docs/assets/demo.png)\n\nAI-powered security scanner for source code. An LLM agent reads your\nrepository with read-only tools, traces data flows from source to sink,\nand reports validated vulnerabilities as SARIF, JSON, and a\nhuman-readable Markdown report.\n\n## Shared AI connection settings\n\n`sais` and `cair` read the same machine-owned TOML file at\n`$XDG_CONFIG_HOME/elvez-quality-gate/config.toml` (default:\n`~/.config/elvez-quality-gate/config.toml`). Create it outside the repositories\nyou scan or review:\n\n```toml\n[profiles.local]\nengine = \"openai\"\nbase_url = \"http://127.0.0.1:18080/v1\"\nmodel = \"auto\"\nauth_token_file = \"~/.config/elvez-quality-gate/local-token\"\n```\n\nCreate the token file privately and run `chmod 600` on POSIX. The file is read\nat each run, preserving all contents except one final LF or CRLF. Relative\ncredential paths in a profile resolve against the configuration directory.\nA file readable by group/others, an invalid token, or a connection file inside\nthe target repository is rejected, including symlink destinations and parent\nGit repositories when running from a subdirectory. No repository configuration\nis discovered automatically.\n\n```bash\nsais scan /path/to/repo --profile local --json\ncair review main...HEAD --profile local --json\nsais mcp --profile local\n```\n\nSelect a profile with `--profile` or `SAIS_PROFILE` / `CAIR_PROFILE`; override\nthe configuration file with `SAIS_CONFIG` / `CAIR_CONFIG`. A profile must be\nselected to read the file. Field precedence is CLI > environment > profile >\nexisting defaults. Both tools accept their own `*_ENGINE`, `*_BASE_URL`,\n`*_MODEL`, `*_AUTH_TOKEN`, and `*_AUTH_TOKEN_FILE` variables. Credential value\nand credential file are one setting: a higher-priority value replaces the\nlower-priority file, and vice versa. Supplying both forms at the same level\nis an error. An explicit `--auth-token \"\"` suppresses lower-priority credentials.\n`--auth-token VALUE` remains supported with a process-list/history warning;\nprefer `--auth-token-file PATH` or the environment. Tokens, credential paths,\nand profile documents are never included in provenance; reflected credentials\nare redacted and HTTP error bodies are not printed. Redaction applies to free\ntext after schema and evidence validation; finding keys, severity, and category\nkeep their meaning even when a local token is a common word. HTTP redirects are refused.\n\n`model = \"auto\"` works with the OpenAI-compatible engine only. It queries\n`GET /models` and uses exactly one valid advertised model. Empty, malformed,\nor multiple-model responses fail with exit code 2 (multiple-model errors list\nup to 20 IDs). IXV beast mode is refused when reported by discovery. Each\nresponse's `model`, when present, must equal the selected ID; a change aborts\nwithout publishing findings. Discovery does not change token budgets.\n`summary.json.provenance` records `engine`, `model_requested`, `model`, and\n`model_metadata` (only `file` basename, `context_length`, and `ixv_mode` when\nprovided). Endpoints and arbitrary discovery fields are excluded. Full prompt\nprovenance and SARIF provenance remain separate work.\n\nMCP resolves the operator's settings when the server starts and validates\nconnection-file locations against each scan target. Its model-controlled tool\narguments cannot choose a profile, endpoint, or credential. Restart the server\nto change a profile; rotating the credential file takes effect on the next scan.\nDirect Python `ScanConfig` / `ReviewConfig` calls never load profiles or tool\nconnection environment variables implicitly. Applications can explicitly opt\nin with the package's `connection.load_connection_settings(prefix=\"SAIS\" or\n\"CAIR\", target=..., profile=...)`, then pass `settings.as_kwargs(target)` into\ntheir config. Explicit `auth_token_file` also works in the Python API. Hosted\nClaude authentication still uses the Claude SDK's own authentication mechanism.\nTimeout/retry profile fields are not yet supported.\n\n## Features\n\n- **Agentic Analysis**: The AI agent explores the repository itself — enumerating entry points, tracing untrusted input to dangerous sinks, and validating each candidate before reporting\n- **Read-Only by Design**: The agent is restricted to Read / Glob / Grep; shell, write, and network tools are disallowed during a scan\n- **SARIF 2.1.0 Output**: Findings integrate directly with GitHub Code Scanning and standard AppSec tooling\n- **CI Gate Built In**: `--fail-on high` exits non-zero when findings meet the threshold, so a scan can block a pipeline\n- **Structured Findings**: Results are produced against a JSON Schema (severity, confidence, CWE, evidence, recommendation) — no fragile text parsing\n- **Bilingual Reports**: Finding descriptions and the Markdown report can be generated in English or Japanese (`--language ja`)\n- **Local LLM Support**: Scan without sending code off the machine — point the built-in `openai` engine at any OpenAI-compatible server (vLLM, Ollama, LM Studio, llama.cpp), or the default `claude` engine at an Anthropic-compatible endpoint\n- **Engine-Agnostic Core**: The scanner core talks to a thin engine adapter; the default engine is the Claude Agent SDK, and other backends can be added without touching the core\n\n## Use Cases\n\n- **Pre-Release Audit**: Run a full scan before shipping and review the Markdown report\n- **CI Security Gate**: Fail pull-request pipelines when new high-severity findings appear\n- **GitHub Code Scanning**: Upload `findings.sarif` to surface findings in the Security tab\n- **Security Triage Input**: Feed `findings.json` into your own tracking or ticketing workflow\n\n## Documentation\n\n- [CHANGELOG.md](https://github.com/elvezjp/security-ai-scanner/blob/main/CHANGELOG.md) - Version history\n- [CONTRIBUTING.md](https://github.com/elvezjp/security-ai-scanner/blob/main/CONTRIBUTING.md) - Contribution guidelines\n- [SECURITY.md](https://github.com/elvezjp/security-ai-scanner/blob/main/SECURITY.md) - Security policy and best practices\n- [PROVENANCE.md](https://github.com/elvezjp/security-ai-scanner/blob/main/PROVENANCE.md) - Code and specification provenance\n- [THIRD_PARTY_NOTICES.md](https://github.com/elvezjp/security-ai-scanner/blob/main/THIRD_PARTY_NOTICES.md) - Dependency licenses and service terms\n- [spec.md](https://github.com/elvezjp/security-ai-scanner/blob/main/spec.md) - English-primary technical specification ([Japanese](https://github.com/elvezjp/security-ai-scanner/blob/main/spec_ja.md))\n- [Implementation roadmap](https://github.com/elvezjp/security-ai-scanner/blob/main/docs/implementation-roadmap.md) - Suite order and the 0.3.0 conformance plan\n- [Local LLM E2E verification](https://github.com/elvezjp/security-ai-scanner/blob/main/docs/local-llm-e2e-verification.md) - Measured results for local models on a planted-vulnerability fixture\n- [Suite compatibility and Q8 evidence](https://github.com/elvezjp/quality-keeper/blob/main/docs/compatibility.md) - Supported private three-product matrix and final-gate scenarios\n\n## Installation\n\nRequires Python 3.11 or higher. Install the `claude` extra for the default\nClaude engine. The [Claude Agent SDK](https://pypi.org/project/claude-agent-sdk/)\nbundles the Claude Code CLI and its use is also governed by Anthropic's\npublished commercial terms.\n\n```bash\npip install 'security-ai-scanner[claude]'\n# or with uv\nuv add 'security-ai-scanner[claude]'\n```\n\nThe built-in OpenAI-compatible local engine has no third-party runtime\ndependency. Install the base package when only that engine is needed:\n\n```bash\npip install security-ai-scanner\nsais scan ./repo --engine openai --base-url http://127.0.0.1:11434/v1 --model MODEL\n```\n\nAfter installation, the `security-ai-scanner` command (and its short\nalias `sais`) is available on your `PATH`.\n\n### Authentication\n\nThe default engine authenticates the same way Claude Code does. Pick one:\n\n```bash\n# Option 1: sign in with your Claude account\nclaude login\n\n# Option 2: use an API key\nexport ANTHROPIC_API_KEY=sk-ant-...\n```\n\n## Usage\n\n```bash\nsecurity-ai-scanner scan path/to/repo\n```\n\nThis generates, under `./security-scan-results/`:\n\n- `findings.json`: Structured findings with severity, confidence, CWE, evidence, and recommendations\n- `findings.sarif`: SARIF 2.1.0 log for GitHub Code Scanning and SARIF viewers\n- `report.md`: Human-readable Markdown report\n- `summary.json`: Schema-version-1 run manifest and completion marker\n\nArtifacts are atomically committed within one output directory, with\n`summary.json` committed last. A `.sais.lock` file rejects concurrent writers.\nIf an interrupted process leaves the lock behind, confirm that no scan is\nactive before removing it.\n\nVersion 0.3.0 introduces native artifact schema version 1 as a deliberate\nbreaking change from 0.2.0. Consumers must validate `schema_version` and must\nnot interpret 0.2.0 `summary.json` or `findings.json` files as schema version 1.\n\nThe exit code is `0` when no finding meets the `--fail-on` threshold,\n`1` when one does (CI gate), and `2` on errors.\n\nAfter a run has acquired its output lock, execution failures commit a\nschema-version-1 `status: \"error\"` summary when possible. With `--json`, that\nsame object is printed to stdout while the process still exits `2`. Validation\nor lock failures before publication print no JSON and leave existing artifacts\nunchanged.\n\n### Common Examples\n\n**Scan with a Japanese report:**\n```bash\nsais scan path/to/repo --language ja\n```\n\n**Use as a CI gate (fail the build on critical findings only):**\n```bash\nsais scan . --fail-on critical\n```\n\n**SARIF derived output, into a custom directory:**\n```bash\nsais scan . --format sarif -o ./out\n```\n\nThe native `findings.json` and `summary.json` are always written. `--format`\nselects additional derived outputs.\n\n**Give the scanner extra context (scope, threat model notes):**\n```bash\nsais scan . --context \"Internet-facing Flask API. Focus on the api/ directory.\"\n```\n\n**Watch the agent's progress:**\n```bash\nsais scan . -v\n```\n\n## Scanning with a Local LLM\n\nTwo engines connect to self-hosted endpoints. Either way, nothing is\nsent to a hosted API.\n\nThe OpenAI endpoint must return exactly one completion choice. Final answers require\n`finish_reason: \"stop\"` without a refusal or unfinished tool calls. A tool turn\nrequires `finish_reason: \"tool_calls\"` and valid calls while tools are enabled.\n`length`, `content_filter`, refusal, missing/unknown termination metadata, and\ninconsistent tool states fail with exit code 2 even if the content is valid JSON.\nThis also applies to the final request after a local turn/token limit; a normally\nfinished partial answer still retains the existing `incomplete` status.\n\n### OpenAI-compatible servers (`--engine openai`)\n\nMost self-hosted inference speaks the OpenAI API — vLLM, Ollama,\nLM Studio, llama.cpp server, and corporate gateways. The `openai`\nengine connects to them directly with a minimal built-in agent loop:\nits only tools are `read_file` / `glob` / `grep`, implemented in this\npackage and sandboxed to the scan root, so the read-only guarantee is\nstructural — no shell, write, or network tool exists in the loop, and\nno third-party agent harness is involved.\n\n```bash\nsais scan ./repo \\\n  --engine openai \\\n  --base-url http://127.0.0.1:11434/v1 \\\n  --model qwen2.5-coder:32b\n```\n\n`--model` is required (or set `SAIS_MODEL`): there is no default on\npurpose, because some servers ignore the model field and a guessed\ndefault would record a wrong model name in the scan outputs. When the\nmodel is missing, the error lists what the server actually offers\n(via `GET /v1/models`).\n\n`--max-tokens N` sets a total-token budget for the scan. When the\nbudget is reached the engine stops calling tools, collects the findings\nit has, and marks `summary.json` with `\"stopped\": \"budget_exceeded\"`.\nA budget-stopped scan with no findings still exits 0, and the model's\nprose summary may not admit the scan was cut short — in CI, also check\nthat `stopped` is null before trusting a clean result. Note that\nservers that omit the `usage` field in responses under-count\n`total_tokens`, which weakens budget enforcement on such servers.\n\n### Anthropic-compatible servers (`--engine claude`, the default)\n\nPoint `--base-url` at a self-hosted, Anthropic-compatible endpoint.\nNote that this path runs the Claude Agent SDK's bundled Claude Code CLI\nlocally as the agent harness; only the inference endpoint changes.\n\n```bash\nsais scan ./repo \\\n  --base-url http://127.0.0.1:8000 \\\n  --auth-token local \\\n  --model your-local-model\n```\n\nThe scanner pins every model slot the agent harness uses (opus / sonnet\n/ haiku / subagent) to `--model`, since a local server usually serves a\nsingle model, and clears any hosted credentials from the subprocess\nenvironment so they cannot take precedence over the local endpoint.\n\n### Common to both engines\n\nThe scanner sends repository contents to whatever `--base-url` you\ngive it — treat that URL as trusted infrastructure and never point it\nat an endpoint you do not control.\n\nIf your endpoint requires a real credential, prefer the\n`SAIS_AUTH_TOKEN` environment variable over `--auth-token`:\ncommand-line arguments are visible in process lists, shell history,\nand CI logs.\n\nSchema-constrained structured output is turned **off** automatically for\na custom `--base-url`, because support in local servers is inconsistent.\nThe scanner instead asks for a fenced JSON block and parses that. If\nyour server does support it (`response_format: json_schema` for the\nopenai engine), re-enable with `--structured-output`.\n\n### Scope the target to fit the context window\n\n**This is the main practical constraint.** The agent reads source files\ninto its context as it works, so a whole repository can exhaust a\nsmaller context window mid-scan. In our testing, a 43-file Python\nrepository failed against a 100K-token local endpoint (the run stopped\nat ~98.5K), while the same scan scoped to the application package\n(`backend/app`, 25 files) completed normally.\n\nPoint the scan at a component rather than a repository root when using\na local endpoint. This is rarely a real limitation in practice, since\nthe security-relevant code is usually one package.\n\n### What to expect\n\n#### Hosted vs local (measured with the `claude` engine)\n\nWe measured this by scanning a repository whose vulnerabilities were\nalready known and confirmed by hand:\n\n| | Hosted API | Local endpoint |\n|---|---|---|\n| Known vulnerabilities found | 3 of 3 | 2 of 3, with matching file and line |\n| Additional real issues found | — | CORS wildcard with credentials, debug output leaking request contents, dependencies pinned to a branch |\n| False positives | none observed | none observed |\n| Severity calibration | consistent | inconsistent — rated one path traversal lower and one missing-auth finding higher |\n| Throughput | minutes | roughly an order of magnitude slower |\n\nA local model is a genuine reviewer, not a keyword matcher: it traced\ndata flows and cited accurate line numbers, and it surfaced real issues\nthe hosted run did not. Where it is weaker is judging *how much a\nfinding matters* — treat its severities as a starting point for triage\nrather than a ranking you can act on directly.\n\nA practical split is local for privacy-sensitive screening, hosted for\nrelease audits and anything where the severity ranking itself drives a\ndecision.\n\n#### Model comparison (`openai` engine)\n\nFour live runs against two server implementations, scanning a 4-file\nFlask fixture with four planted vulnerabilities (command injection,\nSQL injection, path traversal, hardcoded key):\n\n| Model (server) | Planted found | False positives | Duration / tokens |\n|---|---|---|---|\n| Muse-Glimmer-30B GGUF (llama.cpp) | 4 / 4 | none | 83 s / 15,310 |\n| qwen3.8:27b (Ollama) | 4 / 4 | none | 131 s / 10,759 |\n| ornith-1.5:35b (Ollama) | 4 / 4 | none | 25 s / 6,887 |\n| qwen2.5-coder:7b (Ollama) | **0 / 4** — cannot drive the tool loop | — | 0.5 s / 1,300 |\n\nEvery passing model also flagged the same unplanted real issue\n(`DEBUG = True`) that was not part of the fixture design. **Among\ncapable models, detection was uniform; what varied was severity\ncalibration and speed.** Model capability — not the server or the\ntransport — is the gating factor: prefer roughly 27B-class or larger.\nA model that cannot emit parseable tool calls produces an instant\n\"clean\" verdict having read nothing, which the engine rejects as an\nerror (exit 2) rather than passing it off as a clean scan.\n\nFull method, per-run findings, and failure analysis:\n[docs/local-llm-e2e-verification.md](https://github.com/elvezjp/security-ai-scanner/blob/main/docs/local-llm-e2e-verification.md).\n\n> **Note:** if your server processes one request at a time, run scans\n> serially rather than in parallel.\n\n## Use as a Library\n\n`security-ai-scanner` is also usable as a Python library.\n\n```python\nfrom pathlib import Path\nfrom security_ai_scanner import ScanConfig, run_scan\n\nresult = run_scan(ScanConfig(target=Path(\"path/to/repo\"), language=\"ja\"))\n\nfor finding in result.output.findings:\n    print(finding.severity, finding.file, finding.title)\n\nprint(result.gate_failed)   # True if findings meet the fail-on threshold\n```\n\nCLI options map 1:1 to `ScanConfig` fields (e.g. `fail_on=\"critical\"`,\n`formats=(\"sarif\",)`).\n\n### From source\n\n```bash\ngit clone https://github.com/elvezjp/security-ai-scanner.git\ncd security-ai-scanner\nuv sync\n```\n\nSee [CONTRIBUTING.md](https://github.com/elvezjp/security-ai-scanner/blob/main/CONTRIBUTING.md) for the full developer setup.\n\n## Key Options\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `-o`, `--output-dir` | `./security-scan-results` | Output directory |\n| `--engine` | `claude` | AI engine backend: `claude` (Claude Agent SDK) or `openai` (built-in loop for OpenAI-compatible endpoints) |\n| `--model` | Engine default (`claude`) / required (`openai`) | Model passed to the engine (or `SAIS_MODEL` env var) |\n| `--base-url` | Hosted API | Self-hosted endpoint: Anthropic-compatible (`claude`) or OpenAI-compatible (`openai`) |\n| `--auth-token` | `SAIS_AUTH_TOKEN` env var | Auth token for `--base-url` (prefer the env var for real credentials) |\n| `--structured-output` | Auto | Force schema-constrained output on/off (`--no-structured-output` to disable) |\n| `--language` | `en` | Language for findings and report (`en` / `ja`) |\n| `--context` | - | Extra security context for the scan |\n| `--fail-on` | `high` | CI gate threshold (`critical`/`high`/`medium`/`low`/`info`/`none`) |\n| `--format` | All | Additional derived output, repeatable (`json`/`sarif`/`markdown`); native JSON is always written |\n| `--max-turns` | `100` | Maximum agent turns |\n| `--max-tokens` | No cap | Total-token budget for the scan (openai engine); stops early with partial findings |\n| `-v`, `--verbose` | false | Stream agent progress to stderr |\n| `--json` | false | Print the machine-readable summary to stdout (for agents and scripts) |\n| `--notify-webhook` | - | Webhook URL to POST the run summary to on completion/failure (or `SAIS_NOTIFY_WEBHOOK`) |\n| `--notify-format` | `generic` | Webhook payload: `generic` JSON, or a `discord` / `slack` message |\n\n## For AI Agents\n\n`sais` is designed to be easy for coding agents (Claude Code, Codex, Cursor,\nVS Code agents, ...) to drive: stable exit codes, a machine-readable\n`summary.json` (always written), and `--json` for a single-line JSON summary\non stdout. See [AGENTS.md](https://github.com/elvezjp/security-ai-scanner/blob/main/AGENTS.md) for the agent-facing specification.\n\n### Claude Code skill\n\n[`skills/sais-scan/SKILL.md`](https://github.com/elvezjp/security-ai-scanner/blob/main/skills/sais-scan/SKILL.md) is a ready-made\nskill for Claude Code and compatible agents. Copy it into your skills\ndirectory (e.g. `.claude/skills/sais-scan/`) and ask for a security scan —\nthe agent runs `sais`, reads the JSON summary, and reports findings as\nreview leads.\n\n### MCP server\n\nWith the `mcp` extra installed (`pip install 'security-ai-scanner[mcp]'`),\n`sais mcp` serves the scanner over the Model Context Protocol (stdio), so\nMCP clients — Claude Code, VS Code, Cursor, Codex — can scan without\nshelling out:\n\n```bash\n# Claude Code\nclaude mcp add sais -- sais mcp\n```\n\n```json\n// VS Code / Cursor (mcp.json)\n{ \"servers\": { \"sais\": { \"command\": \"sais\", \"args\": [\"mcp\"] } } }\n```\n\nTools: `scan_repository(path, ...)` → run summary + `scan_id`,\n`get_summary(scan_id)`, `get_findings(scan_id, min_severity)`. A scan takes\nminutes; the server sends MCP progress notifications while it runs.\n\n## Notifications\n\n`--notify-webhook URL` (or the `SAIS_NOTIFY_WEBHOOK` environment variable)\nPOSTs the run summary when a scan completes or fails — so unattended scans\nnever fail silently:\n\n```bash\n# Discord channel notification (counts and gate verdict only)\nsais scan . --notify-webhook \"$DISCORD_WEBHOOK_URL\" --notify-format discord\n```\n\n`--notify-format` selects the payload: `generic` (the summary JSON, for CI\nand custom receivers), `discord`, or `slack`. Chat formats send a one-line\nmessage with severity counts only — never finding details. A failed\nnotification prints a warning and does not change the scan's exit code; the\nURL is treated as a secret and never printed.\n\n## GitHub Action\n\nThe repository doubles as a composite action on top of the SARIF output:\n\n```yaml\n- uses: elvezjp/security-ai-scanner@main\n  with:\n    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}\n    fail-on: high\n- uses: github/codeql-action/upload-sarif@v3\n  if: always()\n  with:\n    sarif_file: security-scan-results/findings.sarif\n```\n\nInputs: `target`, `fail-on`, `language`, `output-dir`, `version`,\n`extra-args`. Outputs: `sarif-file`, `summary-file`, `exit-code`.\n\nBy default the action installs the latest scanner release from PyPI.\nFor reproducible CI runs, pin it with `version: \"0.3.0\"` — pinning the\naction reference alone does not pin the scanner itself.\n\n## How It Works\n\n```\n┌─────────────┐   prompt +       ┌──────────────────┐\n│  Scanner    │   JSON schema    │  Engine adapter  │\n│  core       │ ───────────────▶ │  (claude, ...)   │\n│             │                  └────────┬─────────┘\n│  findings   │                           │ read-only tools\n│  validation │                  ┌────────▼─────────┐\n│  SARIF /    │ ◀─────────────── │  AI agent reads  │\n│  report     │   structured     │  the repository  │\n└─────────────┘   findings       └──────────────────┘\n```\n\n1. The scanner builds a security-audit prompt and a findings JSON Schema\n2. The engine runs an AI agent over the target directory with read-only tools (Read / Glob / Grep only)\n3. The agent maps entry points, traces data flows, validates candidates, and returns findings as structured output\n4. The scanner validates, ranks, and writes SARIF / JSON / Markdown\n\n## Directory Structure\n\n```\nsecurity-ai-scanner/\n├── security_ai_scanner/     # Main package\n│   ├── cli.py               # Command-line interface\n│   ├── config.py            # Scan configuration\n│   ├── findings.py          # Finding model, schema, validation\n│   ├── sarif.py             # SARIF 2.1.0 export\n│   ├── report.py            # Markdown report generation\n│   ├── runner.py            # Scan orchestration\n│   ├── engine/              # Engine adapters (claude, ...)\n│   └── prompts/             # Scan methodology prompts\n├── tests/                   # Test suite\n├── spec.md                  # Specification\n├── docs/                    # Documentation\n├── pyproject.toml           # Project metadata\n├── LICENSE                  # MIT License\n├── README.md / _ja.md       # README (English / Japanese)\n├── CONTRIBUTING.md / _ja.md # Contribution guide (English / Japanese)\n├── SECURITY.md / _ja.md     # Security policy (English / Japanese)\n└── CHANGELOG.md / _ja.md    # Version history (English / Japanese)\n```\n\n## Security\n\nFor security concerns, please see [SECURITY.md](https://github.com/elvezjp/security-ai-scanner/blob/main/SECURITY.md).\n\n**Key security notes:**\n- The scan agent runs with read-only tools; it does not modify the target or execute repository code\n- Repository contents are sent to the configured AI engine for analysis — only scan code you are authorized to submit to that engine. Use `--base-url` with a self-hosted endpoint when the code must not leave your infrastructure\n- Findings may include false positives and false negatives; treat reports as expert input to human review, not as a certification\n- Scan only code you own or are authorized to assess\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](https://github.com/elvezjp/security-ai-scanner/blob/main/CONTRIBUTING.md) for details.\n\n- Report bugs via [GitHub Issues](https://github.com/elvezjp/security-ai-scanner/issues)\n- Submit pull requests for improvements\n- Follow existing code style\n- Add tests for new features\n\n## Roadmap\n\nPlanned work for making the public scanner usable in CI and on self-hosted\nLLMs. Order is priority, not a promise. Scope may change based on feedback.\nTracked issues use labels `priority:P0` / `P1` / `P2` and `roadmap`.\n\n### P0 — usable in public CI and local scans\n\n- **Path include/exclude and `.saisignore`** — keep agents out of\n  `node_modules`, build artifacts, and other noise ([#3](https://github.com/elvezjp/security-ai-scanner/issues/3))\n- **Project config file** (`sais.toml` / `.sais.yaml`) — pin fail-on, language, and scope;\n  connection settings belong only to machine profiles\n  ([#4](https://github.com/elvezjp/security-ai-scanner/issues/4))\n- **Diff scan** — limit a scan to a PR / commit range\n  ([#5](https://github.com/elvezjp/security-ai-scanner/issues/5))\n- **Baseline, suppressions, and stable finding IDs** — stop recurring false\n  positives from failing every run\n  ([#6](https://github.com/elvezjp/security-ai-scanner/issues/6))\n\n### P1 — generic product depth\n\n- **Completed: Second engine backend** — the OpenAI-compatible adapter now\n  proves the engine-agnostic core beyond Claude\n  ([#7](https://github.com/elvezjp/security-ai-scanner/issues/7),\n  [#17](https://github.com/elvezjp/security-ai-scanner/pull/17))\n- **Scan profiles** — pluggable methodology prompts (`security` default, plus\n  a small set of generic profiles; org-specific checklists stay outside the\n  public core) ([#8](https://github.com/elvezjp/security-ai-scanner/issues/8))\n- **Batch scan** — multiple roots serially with `batch-summary.json` (today:\n  loop `sais scan` and read each `summary.json`)\n  ([#9](https://github.com/elvezjp/security-ai-scanner/issues/9))\n- **Local LLM operational hardening** — clearer context/scope failures, serial\n  defaults for single-session endpoints\n  ([#10](https://github.com/elvezjp/security-ai-scanner/issues/10))\n\n### P2 — trust and ongoing use\n\n- **Known-vulnerability fixture benchmark** — reproducible recall checks for\n  hosted vs local models\n  ([#11](https://github.com/elvezjp/security-ai-scanner/issues/11))\n- **Triage** — re-evaluate findings and learn from false-positive feedback\n  ([#12](https://github.com/elvezjp/security-ai-scanner/issues/12))\n- **Release maturity** — support matrix, SBOM where practical, pinned Action\n  guidance ([#13](https://github.com/elvezjp/security-ai-scanner/issues/13))\n\n### Non-goals for the public core\n\n- Vendor- or org-specific “repo hygiene” checklists (docs drift, internal ADR\n  rules). Those belong in private wrappers or custom profiles passed via the\n  planned `--profile` option ([#8](https://github.com/elvezjp/security-ai-scanner/issues/8)),\n  not hard-coded into this package\n- Turning the tool into a general-purpose code review bot. The product focus\n  stays security findings with structured evidence\n\n## Changelog\n\nSee [CHANGELOG.md](https://github.com/elvezjp/security-ai-scanner/blob/main/CHANGELOG.md) for details.\n\n## Background\n\nThis tool was created as a small utility during the development of\n**IXV (Ixiv)**, a development support AI for Japanese development\ndocuments and specifications.\n\nIXV addresses the challenges of understanding, structuring, and\nutilizing Japanese documents in system development. This repository\npublishes a portion of that work.\n\nThe scan methodology (agentic scan → validation → structured findings)\nis an independent implementation inspired by the design of\n[OpenAI Codex Security](https://github.com/openai/codex-security). No\ncode is shared between the two projects.\n\n## License\n\nMIT License - See [LICENSE](https://github.com/elvezjp/security-ai-scanner/blob/main/LICENSE) for details.\nThird-party components and separate service terms are recorded in\n[THIRD_PARTY_NOTICES.md](https://github.com/elvezjp/security-ai-scanner/blob/main/THIRD_PARTY_NOTICES.md).\n\n## Contact\n\n- **Email**: info@elvez.co.jp\n- **Company**: Elvez, Inc.\n"}