One engine.Every way in.
The plugin, the CLI, the MCP server, the Action, the image and the hooks all run the same deterministic tier — the one carrying the external number. None is a reduced version of another; the appropriate surface is the one already present in the workflow. Every command on this page is read at build time out of the file that defines it, so a renamed flag breaks this build rather than a user's terminal.
None of these paths requires an API key. The deterministic tier needs no network and no account; the optional model tier remains off until it is enabled.
- 6ways in
- 4slash commands
- 6MCP tools
- 0runtime dependencies
- 01Claude Code plugin
/secaudit .The full P1–P10 methodology, live target included. - 02Standalone CLI
pip install secaudit-kitNo key, no plan, no network. Zero deps. - 03MCP server
python3 -m secaudit_mcpCodex, Cursor, OpenCode — same engine. - 04GitHub Action
uses: mtvrkan/secauditSARIF into code scanning on every push. - 05Docker
docker run secauditPinned by digest, non-root, SBOM attached. - 06pre-commit
repo: mtvrkan/secauditCatch it before it reaches the branch.
Choose one
Which surface, in one table.
Same engine, same findings, same report. What differs is who runs it, when it runs, and what it is allowed to reach.
| Situation | Surface | What it costs |
|---|---|---|
| An existing Claude Code workflow | Claude Code plugin | Two lines; the live-target track comes with it |
| CI, gating pull requests | GitHub Action | Ten lines of workflow, no install step |
| Codex, Cursor, OpenCode or another MCP client | MCP server | One config entry |
| Local, scriptable and offline use | Standalone CLI | One package, zero dependencies |
| A fixed toolchain and a sandboxed scanner | Docker | One build, non-root |
| Before the commit rather than after it | pre-commit | Staged files only, under a second |
The live target is the one real asymmetry. Probing a running system is a human decision, so it exists only in the plugin and only behind the authorization gate — nothing on any other row reaches a host. Everything else is the same code: the detectors, the taint analysis, the dependency reachability pass and the evidence pack.
1 / 6Claude Code
The plugin: the whole methodology, live target included.
Two lines in the Claude Code prompt. The marketplace id and the plugin id come out of .claude-plugin/marketplace.json — the file Claude Code itself reads — so this page cannot end up naming a marketplace that was renamed a release ago. This is the only surface with the live-target track and the only one that runs P1–P10 end to end; the others are the deterministic engine underneath it.
Active testing remains blocked until ownership is declared in scope.yaml. That is a PreToolUse hook rather than an instruction to a model: the harness refuses the call, so no amount of persuasion in the conversation reaches an unclaimed host.
/plugin marketplace add mtvrkan/secaudit
/plugin install secaudit@secaudit-kit
/secaudit .
Then type /secaudit. The command should appear with its argument hint — if it does not, the marketplace was added but the plugin was not installed.
- 1
/secaudit<url | path> [--lang tr|en] [--passive] [--active] [--code] [--deps] - 2
/secaudit-code[path] [--lang tr|en] - 3
/secaudit-deps[path] [--lang tr|en] - 4
/secaudit-passive<url> [--lang tr|en]
2 / 6no key, no network
The CLI: the same engine with nothing around it.
secaudit-kit installs the secaudit command and nothing else: no account, no key, no plan, and no runtime dependency to audit before it can be trusted. It runs on a host with no network connection, which is the state a great deal of code worth auditing lives in. Python 3.9 or newer.
Until the first release is published, pip install secaudit-kit is a command that can succeed with somebody else's package: a pending trusted publisher does not reserve the name. The git+… line above needs no release and is exact. And the zero-dependency claim is a build gate rather than a sentence: assert_no_runtime_deps.py fails the build the moment one is added, and the list rendered beside this paragraph is read out of kit/pyproject.toml.
# the released package
pip install secaudit-kit
# or the same package from any commit, no release needed
pip install "git+https://github.com/mtvrkan/secaudit#subdirectory=kit"
secaudit . # audit this tree
secaudit . --min high --format sarif # for GitHub code scanning
secaudit . --since main # only what this branch introduced
- packagesecaudit-kit
- version1.0.0
- python>=3.9
- commandssecaudit, secaudit-mcp
- runtime depsnone
3 / 6stdio
The MCP server: Codex, Cursor, OpenCode, and whatever is next.
One process over stdio, 6 tools, the same engine the plugin and the CLI run. A second implementation per harness is how two clients end up disagreeing about whether a file is safe, so there is not one — the published number describes this server too.
No tool here accepts a URL, a host or an endpoint, and the test suite asserts that no schema grows one. A tools/call carries no evidence that anybody authorized a probe, and a tool that scans whatever it is handed is a tool that scans whatever a prompt injection puts in front of it.
- Command
python3(orpythonon Windows) - Arguments
-m,secaudit_mcp - Transportstdio
python3 -m secaudit_mcp --tools # prints the tool manifest and exits
Per client
Claude Code — the plugin is the better path here, but the server works too
claude mcp add secaudit -- python3 -m secaudit_mcpCodex CLI — ~/.codex/config.toml
[mcp_servers.secaudit]
command = "python3"
args = ["-m", "secaudit_mcp"]Cursor — .cursor/mcp.json in the project, or the global equivalent
{ "mcpServers": { "secaudit": { "command": "python3", "args": ["-m", "secaudit_mcp"] } } }VS Code / Copilot CLI — .mcp.json or .vscode/mcp.json
{ "servers": { "secaudit": { "command": "python3", "args": ["-m", "secaudit_mcp"] } } }OpenCode — opencode.json
{ "mcp": { "secaudit": { "type": "local", "command": ["python3", "-m", "secaudit_mcp"] } } }4 / 6pull-request gate
The Action: a gate on what the change introduced.
It runs the code in the checkout rather than installing a release, so the version audited is the commit under review and there is no window in which a compromised release of a security scanner runs against an already checked-out repository. On a pull request it compares against the base branch by default, so the gate fires on what the change introduced and stays quiet about the rest.
comment is off by default. A workflow should not acquire write access to pull requests because an example in someone's documentation had the flag set.
.github/workflows/security.yml
- uses: actions/checkout@v5
with: { fetch-depth: 0 } # so --since can see the base branch
- uses: mtvrkan/secaudit@v1.0.0
with:
fail-on: high
sarif: audit.sarif
5 / 6non-root
The image: a fixed toolchain, built from this repository.
There is no image to pull, by design: it is built from the Dockerfile in the repository. The base is pinned by digest rather than by tag, so an image built today is the image someone else built last month, and everything that went into it is readable in one file.
It drops to an unprivileged uid, and the run above mounts the tree read-only. A scanner does not require write access to what it scans, and the one occasion on which that matters is the occasion on which the target has been tampered with.
docker build -t secaudit .
docker run --rm -v "$PWD:/src:ro" secaudit /src --min high
- basepython:3.12-slim-bookworm
- pinned tosha256:4766d8b510c428e595d74b9cc5bbb2fae8e26316fffb4adc89908d79aacd58a2
- runs asuid 10001
- entrypointsecaudit
6 / 6staged files
The hooks: before the branch rather than after it.
Two hooks, both scoped to the files being committed. Budget matters more than coverage here: a hook that takes ten seconds is bypassed with --no-verify inside a week, and a bypassed hook catches nothing. So the default is the deterministic pass with the dependency and external-scanner tiers switched off, and the full audit stays in CI where it can afford to be slow.
Where the full hook is too slow to survive on a given repository, the narrower one applies. A secret is the least recoverable finding after the fact — once committed it is in the history — so catching that alone is worth considerably more than catching nothing.
.pre-commit-config.yaml
repos:
- repo: https://github.com/mtvrkan/secaudit
rev: v1.0.0
hooks:
- id: secaudit
- 1
secauditSecAudit (staged files, deterministic pass)
--min high --no-deps --no-scanners - 2
secaudit-secretsSecAudit (secrets only)
--min high --no-deps --no-scanners --no-taint --only secret
Optional, never required
External scanners, used when present on PATH.
The engine's own findings do not depend on any of them. Where one is installed its results are merged and attributed to it; where it is absent the report says so rather than quietly narrowing what it searched — a clean result has to mean the same thing on both machines.
- semgrep
pipx install semgrepSAST (code) - gitleaks
brew/scoop install gitleakssecret detection - osv-scanner
go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latestdependency CVEs
A scan with none of these installed still runs every detector, the taint analysis and the dependency reachability pass. The live-mode toolchain — testssl.sh and the rest — belongs to the plugin rather than the engine, and docs/tooling-setup.md lists all of it.
After installation.
Getting started walks the first scan, the authorization gate and the report formats. The benchmark page has the external number and everything behind it.