Local-first scanner for secret-like values, risky configuration changes and opt-in local vulnerability analysis.
CodeRiskTools Secret Scanner Engine is MIT licensed and has no runtime dependencies. It scans diffs, staged changes, local directories and bounded Git history without executing target-project code. Vulnerability analysis uses a local SQLite/SBOM path; the pinned signed global database can be installed automatically on its first use.
Evidence, not guarantees. A clean result is not proof that code is secure. Findings can contain false positives and false negatives. This tool is not a security audit, certification, legal opinion or compliance guarantee.
- unified-diff, staged, directory and bounded Git-history scanning;
- strict JSON, Markdown, HTML, SARIF and GitHub output;
- 299 native detectors in the current public registry, including stable secret-format and CI/CD policy detectors;
- allowlists, strict baselines, severity profiles and signed offline rule packs;
- local Gitleaks JSON/SARIF import and explicitly supplied local Gitleaks binary mode;
- generic, Codex-labelled and Claude Code-labelled offline agent hooks;
- pre-commit integration and composite GitHub Action;
- local dependency inventory from a repository, CycloneDX JSON, SPDX JSON or Syft JSON;
- local OSV-Scanner JSON as explicitly separated external evidence;
- local Trivy JSON as explicitly separated external evidence;
- local Grype JSON as explicitly separated external evidence;
- local SQLite vulnerability snapshot scanning and reports;
- snapshot reconciliation, verification, status, update, rollback, retention pruning and provenance fetch commands;
- OpenVEX/CycloneDX VEX annotations, suppression and vulnerability baselines;
- no telemetry or target-project execution; secret/config scans and vulnerability scans after database bootstrap perform no network I/O.
The stable detector count excludes provisional candidates. See the detector backlog and source records in docs/STAGE6_SECRET_DETECTOR_BACKLOG.md and docs/STAGE8_CI_CD_BATCH1_SOURCES.md.
This repository contains only the public Scanner flagship. It does not contain or license the proprietary MCPwatch Scanner or AI Change Firewall products. See docs/PRODUCT_BOUNDARY.md.
- Python 3.10–3.13;
- Git is required for
--stagedand--git-historymodes; - secret/config scanning does not require network access or third-party runtime packages; first-use default vulnerability scanning downloads a pinned signed database ZIP.
From a checkout:
python -m venv .venv
. .venv/bin/activate
python -m pip install --no-deps .
secret-scanner --versionWhen running without installation, use python -m src in place of secret-scanner.
secret-scanner
├── scan secrets/config scan
├── osv-import import one explicitly supplied local OSV feed
├── vuln inventory local dependency inventory or OSV-Scanner evidence
├── vuln scan local repository/SBOM vulnerability scan against SQLite snapshot
├── vuln-db snapshot store, update and provenance operations
├── verify optional explicit GitHub/Stripe credential check
├── rules signed offline rule-pack install/rollback
└── hook bounded offline AI-agent hook envelope
Every command supports --help; secret-scanner --version prints the installed version.
All scan modes require exactly one input source:
secret-scanner scan \
--diff changes.diff \
--profile balanced \
--format jsonsecret-scanner scan \
--staged \
--profile balanced \
--format githubThis reads staged Git diff bytes and does not invoke external diff/text-conversion helpers.
secret-scanner scan \
--dir . \
--recursive \
--profile strict \
--format sarif \
--output secret-results.sarifDirectory mode skips repository metadata, common dependency/build directories, binary formats and symlinks. Reads are bounded and descriptor-pinned.
secret-scanner scan \
--git-history \
--since-ref origin/main \
--max-commits 100 \
--format json--max-commits accepts 1–1000 and defaults to 100.
secret-scanner scan \
--gitleaks-report gitleaks.json \
--format jsonThe imported report must be a strict redacted Gitleaks JSON/SARIF document. This mode does not claim that the external tool was executed by CodeRiskTools.
secret-scanner scan \
--gitleaks-binary ./bin/gitleaks \
--format jsonThe binary path is explicitly supplied by the operator. Do not use this mode with untrusted binaries.
--format {json,markdown,html,sarif,github}— output format, defaultjson;--output FILE— write report instead of stdout;--profile {balanced,strict,secrets-only}— policy profile;--severity-threshold {low,medium,high,critical}— minimum severity;--config FILE— severity/config policy file;--config-check/--no-config-check— override configuration detection;--allowlist FILE—.secretsallowlistfile;--baseline FILE— strict secret/config finding baseline;--write-baseline FILE— atomically write the current unsuppressed baseline;--force-baseline— allow replacement of an existing regular baseline;--rule-pack FILEand--rule-keyring FILE— use a signed offline rule pack; both must be supplied together;--recursive— recurse in directory mode;--quiet— omit summary where supported.
These options are valid with --dir:
secret-scanner scan \
--dir . \
--recursive \
--vulnerability-db vulnerability.sqlite \
--vulnerability-baseline vulnerability-baseline.json \
--write-vulnerability-baseline next-baseline.json \
--vulnerability-policy vulnerability-policy.json \
--format json--vulnerability-db FILE— local SQLite database;--vulnerability-baseline FILE— suppress explicitly baselined vulnerability fingerprints;--write-vulnerability-baseline FILE— write vulnerability fingerprints;--force-vulnerability-baseline— allow overwrite;--vulnerability-policy FILE— local vulnerability severity policy.
These options require --dir. They do not change legacy secret/config scan contracts.
Secret/config scan formats:
json— machine-readable full report;markdown— human-readable report;html— standalone local HTML report;sarif— SARIF-compatible findings;github— GitHub workflow annotations.
Vulnerability scan formats are json, sarif, markdown, html and csv.
Exit codes:
0— clean or warning-only result;1— secret/vulnerability finding at the configured failure threshold;2— risky configuration/policy finding without a failing secret;3— malformed, unsafe, oversized or operationally rejected input.
Exact runtime evidence is preserved. Findings are not automatically redacted or truncated.
secret-scanner vuln inventory --root .secret-scanner vuln inventory --sbom bom.jsonThe inventory path is read-only and local. It does not resolve packages from the network.
secret-scanner vuln inventory --grype grype.jsonThe Grype adapter accepts bounded native Grype JSON, preserves descriptor version, artifact identity, locations, vulnerability ID and related aliases, and emits coderisktools.vulnerability.external-evidence with evidence_type: external-tool. It does not execute Grype or convert its matches into dependency inventory.
secret-scanner vuln inventory --trivy trivy.jsonThe Trivy adapter accepts bounded local Trivy JSON (SchemaVersion 1 or 2), preserves target/package/vulnerability data and emits the same coderisktools.vulnerability.external-evidence schema with evidence_type: external-tool. It does not execute Trivy or convert its findings into dependency inventory.
secret-scanner vuln inventory --osv-scanner osv-scanner.jsonThis is intentionally not dependency inventory. The output schema is:
coderisktools.vulnerability.external-evidence
Each finding is marked evidence_type: external-tool and retains the OSV-Scanner source path, package identity, vulnerability ID and aliases. The provenance sidecar is optional and is verified against the actual input bytes:
secret-scanner vuln inventory \
--osv-scanner osv-scanner.json \
--provenance osv-scanner.provenance.jsonThe sidecar schema is coderisktools.vulnerability.external-evidence-provenance v1 and includes source_id, source_format, source_sha256, timezone-aware collected_at, collector and tool_version. The source digest and tool identity must match the evidence report. Tampered or mismatched sidecars fail closed. The adapter also validates tool version, bounded arrays, package identity, PURL and aliases. It does not run OSV-Scanner.
Starter release includes 243,381 real CVE records.
CodeRiskTools does not ship invented fixtures. Release
v3.1.0bootstraps a signed, explicitly partial OSV database containing 243,381 real CVE records from the first scan. The signed release manifest is authoritative for the exact count.
The database is designed to grow. secret-scanner vuln-db update --full can build a larger snapshot from configured sources, and --database FILE can select an independently supplied, verified database. After updates the database can contain up to the full OSV source count (currently 813,101 records); the starter snapshot is only the first, bounded release. Future signed release snapshots can therefore expand from the starter 243,381 records toward much larger — and eventually full — OSV coverage without changing the matching format. Every UI and report must retain the profile, completeness and source counts: absence of a match in a partial snapshot is never proof that a dependency is safe.
If --database is omitted and the default database is absent, the first run downloads, verifies, extracts and activates the pinned signed partial OSV snapshot:
secret-scanner vuln scan \
--root . \
--format jsonSBOM scan input is also supported for local CycloneDX, SPDX or Syft JSON documents. This is useful for scanning generated container/VM/package inventories against the signed starter database:
secret-scanner vuln scan \
--sbom bom.json \
--database ~/.local/share/coderisktools/vuln-db/osv-partial.sqlite \
--format jsonOptional annotations:
secret-scanner vuln scan \
--root . \
--database vulnerability.sqlite \
--baseline vulnerability-baseline.json \
--vex openvex-or-cyclonedx-vex.json \
--suppressions suppressions.json \
--format json \
--output vulnerability-report.jsonOptions:
--root DIR— local repository root;--sbom FILE— local CycloneDX, SPDX or Syft JSON SBOM; exactly one of--rootor--sbomis required;--database FILE— local regular SQLite database; defaults to~/.local/share/coderisktools/vuln-db/osv-partial.sqlite;--no-bootstrap— reject a missing default database instead of downloading it;--format {json,sarif,markdown,html,csv};--output FILE— write the report atomically;--baseline FILE— JSON format emits new/existing/resolved delta;--vex FILE— local OpenVEX or CycloneDX VEX;--suppressions FILE— strict local suppression document.
After first-use bootstrap, matching is offline, active-snapshot-only and read-only. The pinned v3.1.0 default is explicitly labeled osv-partial / partial-osv-prefix; an unmatched dependency is not proof of safety or complete OSV coverage. The database path cannot be a symlink, URL or non-regular file. Manual installation is available as secret-scanner vuln-db bootstrap-global.
Import one explicitly supplied local OSV JSON feed:
secret-scanner osv-import \
--input osv-all.json \
--db vulnerability.sqlite \
--snapshot-id osv-2026-07-22 \
--source-id osv \
--activateOptions:
--input FILE— local OSV JSON; URLs are rejected;--db FILE— target local SQLite database;--snapshot-id ID— staged snapshot identifier;--source-id ID— source identifier;--activate— explicitly activate after successful import;--keyring FILE— trusted offline Ed25519 keyring for signed feed envelopes.
Activation is explicit. Ordinary scans do not update the database.
secret-scanner vuln-db reconcile \
--root snapshots \
--active snapshots/activeUse --output report.json for an atomic local report file.
secret-scanner vuln-db status --root snapshots --active snapshots/active
secret-scanner vuln-db list-snapshots --root snapshots --active snapshots/activesecret-scanner vuln-db verify --snapshot snapshots/osv-2026-07-22secret-scanner vuln-db update \
--input osv-all.json \
--root snapshots \
--source-id osv \
--snapshot-id osv-2026-07-22 \
--active snapshots/activeAdd --apply only when the operator explicitly wants to switch the active pointer.
secret-scanner vuln-db source-status \
--root snapshots \
--active snapshots/active
secret-scanner vuln-db database-info \
--active snapshots/activesecret-scanner vuln-db explain \
--database vulnerability.sqlite \
--fingerprint sha256:...secret-scanner vuln-db rollback \
--active snapshots/active \
--target snapshots/previous \
--applyRollback requires --apply; without it the command rejects the operation.
Dry-run is the default:
secret-scanner vuln-db prune \
--root snapshots \
--active snapshots/active \
--keep-snapshot-id osv-2026-07-22Deletion requires explicit --apply:
secret-scanner vuln-db prune \
--root snapshots \
--active snapshots/active \
--keep-snapshot-id osv-2026-07-22 \
--applyThis explicit generic fetch operation requires an explicit allowlist. The separate bootstrap-global operation performs only its built-in, pinned, signature-verified GitHub Release download:
secret-scanner vuln-db fetch \
--url https://example.invalid/feed.json \
--allowed-host example.invalid \
--source-id example \
--output staging/example.json \
--provenance staging/example.provenance.jsonAvailable fetch controls:
--conditions FILE— verified provenance sidecar for conditional headers;--etag VALUEand--last-modified VALUE— conditional request values;--max-bytes N— response size limit;--timeout SECONDS;--max-attempts N;--backoff-seconds SECONDS.
The URL must be HTTPS, host-allowlisted and free of userinfo. Redirects are allowlist-checked. Fetching is separate from staging and activation.
OpenVEX and CycloneDX VEX documents are local inputs. not_affected requires a justification. Suppression schema v1 remains supported; schema v2 can include:
{
"schema": "coderisktools.vulnerability.suppressions",
"version": 2,
"entries": [
{
"fingerprint": "sha256:<64 lowercase hex characters>",
"reason": "accepted risk",
"owner": "security",
"ticket": "SEC-123",
"scope": "service-a",
"expires_at": "2026-12-31"
}
]
}Expiry and usage are reported by the Python API suppression_lifecycle_report; expired entries are not silently discarded. Vulnerability baselines are separate from secret/config baselines.
secret-scanner verify \
--provider github \
--credential-env GITHUB_TOKEN \
--consent-networkSupported providers are github and stripe. The credential is read from the named environment variable. Network access is disabled unless --consent-network is present. Never put a real credential in command arguments, fixtures or documentation.
Install:
secret-scanner rules install \
--source rule-pack.json \
--destination .coderisktools/rules \
--keyring trusted-keyring.jsonRollback:
secret-scanner rules rollback \
--destination .coderisktools/rules \
--keyring trusted-keyring.jsonFor scanning, supply the installed pack and its trusted keyring together:
secret-scanner scan \
--dir . \
--rule-pack .coderisktools/rules/active.json \
--rule-keyring trusted-keyring.jsonThe hook command accepts a bounded offline envelope from a generic, Codex-labelled or Claude Code-labelled adapter:
cat hook-payload.json | secret-scanner hook \
--agent generic \
--baseline hook-baseline.json \
--config severity-config.jsonValid agents: generic, codex, claude-code. This is an explicit input contract, not a claim of undocumented vendor-native compatibility. See integrations/AI_AGENT_HOOKS.md.
repos:
- repo: https://github.com/9batalion/coderisktools-scanner
rev: v3.1.0
hooks:
- id: coderisktools-secret-scanThe hook scans staged Git diff bytes and does not run project builds or tests.
name: CodeRiskTools Scanner
on:
pull_request:
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- uses: 9batalion/coderisktools-scanner@v3.1.0
with:
profile: balancedThe Action analyzes bounded Git diff bytes, does not install target dependencies and does not execute target-project code.
- offline by default; no telemetry;
- no runtime dependencies;
- no target-project execution;
- no ordinary-scan network access;
- explicit local paths only for databases, feeds, SBOMs, VEX and reports;
- symlinks, traversal paths, malformed documents and oversized inputs are rejected;
- external evidence is kept separate from local inventory and vulnerability findings;
- runtime vulnerability evidence is not auto-redacted or truncated;
- signed rule packs and feed envelopes require trusted local keyrings;
- networked credential verification and allowlisted fetch are explicit opt-in operations.
python -m unittest discover -s tests -p 'test_*.py' -v
pytest -q
python -m compileall -q src tests
secret-scanner scan --dir src --recursive --format jsonContributions must use synthetic fixtures only. Never submit live credentials, private findings, customer repositories or proprietary MCPwatch/Firewall source. Changes to controlled fixtures must follow docs/SELF_SCAN_BASELINE.md.
docs/P1-VERSION-COMPARATORS.md— bounded comparator contract and explicit unsupported ecosystem semantics;docs/P0-CONSOLIDATION-AUDIT-V0-V11.md— executable P0 evidence, reproducibility result and remaining gaps;docs/VULNERABILITY-DATABASE-MASTER-TODO-HERMES.md— staged vulnerability roadmap;docs/MASTER-TODO-GAPS-AUDIT.md— confirmed implementation gaps;docs/V4A-VULNERABILITY-PIPELINE.md— local vulnerability pipeline;docs/V9F-OFFLINE-SCAN-CLI.md— vulnerability scan CLI;docs/V10-BASELINE-DELTA.md— baseline and delta;docs/V10-VEX-SUPPRESSION.md— VEX and suppression;docs/V11A-CYCLONEDX-SBOM.md— SBOM inventory;docs/V8A-SAFE-UPDATER-STAGING.mdthroughdocs/V8P-FETCH-RETRY.md— updater, provenance, staging, rollback and fetch boundaries;integrations/AI_AGENT_HOOKS.md— hook envelope contract.
The files in this repository are available under the MIT License. Product names, website content, MCPwatch Scanner, AI Change Firewall and separately distributed commercial materials are not automatically licensed by this repository.