vis secrets
Scan the workspace for hardcoded credentials with a gitleaks-compatible Rust engine
vis secrets
Scan the workspace (or a subset) for hardcoded secrets and credentials. vis secrets is a workspace-aware wrapper around @visulima/secret-scanner — a Rust port of the gitleaks detection engine — that adds git-native file selection (--staged, --since, --affected), baseline tooling, SARIF rendering, opt-in rule presets, and optional live validation against provider APIs.
Usage
vis secrets [paths...] [options]Positional paths default to the workspace root. Pass one or more directory or file paths to scope the scan.
Examples
# Scan the workspace (grouped, colourised output)
vis secrets
# Pre-commit mode — scan only files staged for the current commit
vis secrets --staged
# Scan only files changed since a ref
vis secrets --since main
# Scan projects affected by the current branch (git diff against HEAD~1 or $VIS_BASE)
vis secrets --affected
# Seed an initial baseline from current findings
vis secrets --init
# Preview the baseline without writing files
vis secrets --init --dry-run
# Enable opt-in rule groups additively (defaults still fire)
vis secrets --enable-rule tag:preset:weak-passwords
vis secrets --enable-rule tag:preset:password-manager
vis secrets --enable-rule tag:preset:exposed-files
# Restrict output to one opt-in group only (whitelist)
vis secrets --include-rule tag:preset:password-manager
# Audit a release artefact for accidentally-shipped exposures
vis secrets dist --enable-rule tag:preset:exposed-files
# Single-rule spot check
vis secrets --include-rule stripe-access-token
# Drop a noisy rule but keep the rest of the defaults
vis secrets --exclude-rule generic-api-key
# Raise the confidence floor — CI-friendly precision filter
vis secrets --min-confidence high
# Live-verify findings against provider APIs, then fail CI on unverified matches
vis secrets --validate --only-verified
# List non-HTTP validators referenced by the ruleset + install hints
vis secrets --list-validators
# Baseline suppression + diff
vis secrets --baseline .secrets-baseline.json
# Merge current findings into the baseline and exit 0
vis secrets --update-baseline
# SARIF output for GitHub code scanning
vis secrets --format sarif > report.sarifModes
vis secrets picks the scan target from the first matching flag (in order):
| Flag | Source | Best for |
|---|---|---|
--staged | git diff --cached --name-only | Pre-commit hooks |
--since <ref> | git diff <ref>..HEAD --name-only | CI on pull-request branches |
--affected | git diff $VIS_BASE..HEAD (default HEAD~1) | Incremental CI / monorepos |
| (none — default) | Positional paths, else workspace root | Full audit / seeding |
--staged, --since, and --affected all require a git working tree — the command exits with code 2 if none is detected (except --affected, which falls back to a full scan with a warning).
Alongside those, three flags run a one-shot reporting pass instead of scanning:
--initscaffolds.secrets-baseline.jsonfrom current findings. It refuses to overwrite an existing baseline; delete it first to re-init. Pair with--dry-runto preview.--list-rulesprints every compiled rule (id, description, keywords, tags) and exits.--list-validatorsprints non-HTTP validator types referenced by the current ruleset withnpm add …hints for the optional peer dependencies that unlock them.
Options
Input selection
| Option | Type | Default | Description |
|---|---|---|---|
--staged | boolean | false | Scan only files staged for commit. Requires git. |
--since <ref> | string | — | Scan only files changed since <ref> (e.g. main, origin/HEAD). |
--affected | boolean | false | Scan only files changed against $VIS_BASE (default HEAD~1). |
--exclude <pattern> | string[] | — | Gitignore-syntax pattern to exclude from the walk. Repeatable. |
--exclude-from <path> | string[] | — | Gitignore-shaped file the walker should honor (e.g. .secretsignore). Repeatable. |
--include-hidden | boolean | false | Visit dotfiles and hidden directory entries. |
--no-gitignore | boolean | false | Do not respect .gitignore / .ignore / global excludes. |
--max-size <bytes> | number | 10 MiB | Skip files larger than this. |
Rule selection
| Option | Type | Default | Description |
|---|---|---|---|
--enable-rule <spec> | string[] | — | Additive — turn on opt-in rules without restricting output. Rule id or tag:<name>. Repeatable. |
--include-rule <spec> | string[] | — | Whitelist — only emit matching findings. Implies enablement. Rule id or tag:<name>. Repeatable. |
--exclude-rule <spec> | string[] | — | Blacklist — drop matching findings. Rule id or tag:<name>. Repeatable. |
--min-confidence <lvl> | string | low | Drop rules whose author-declared confidence is below this floor: low, medium, high. |
--config <path> | string | — | Path to a gitleaks-compatible JSON config. |
--no-extend-bundled | boolean | false | With --config, replace the bundled ruleset instead of layering on top of it. |
Bundled opt-in groups: tag:preset:weak-passwords (low-entropy credentials complementing generic-api-key), tag:preset:password-manager (1Password / Bitwarden / LastPass / KeePass / browser-CSV export detection), tag:preset:exposed-files (37 path-driven rules covering committed .env, .git/, .aws/credentials, private keys, DB dumps, source maps with sourcesContent, lockfiles in dist/, IDE workspace dumps, …). All three ship defaultEnabled: false. Shorthand alternative: list them under secrets.config.presets in vis.config.ts (see below) and skip the long tag:preset:* strings on the CLI.
Confidence semantics: every bundled gitleaks rule defaults to low (no explicit label). Raising the floor to medium or high drops every unlabeled rule along with explicit low-confidence rules, so a ruleset needs confidence metadata before --min-confidence high is useful.
Validation
| Option | Type | Default | Description |
|---|---|---|---|
--validate | boolean | false | Live-verify findings against the provider's API after detection. |
--only-verified | boolean | false | With --validate, drop every finding whose validation is not verified. Useful for CI. |
--list-validators | boolean | false | Print non-HTTP validator types referenced by the current ruleset, with peer-dep install hints. |
Validation sends one HTTP request per finding (per-host concurrency capped at 4, global at 8). Supports Kingfisher-style HTTP validators with StatusMatch / WordMatch response matchers. Other validation kinds (gRPC, multi-step HTTP, checksum) mark the finding as validation=skipped — they don't block the pass.
Warning:
--validatesends the candidate secret to the provider. Some providers alert their security team on failed auth attempts. Only enable on repos you own or have explicit authorisation to test.
Output
| Option | Type | Default | Description |
|---|---|---|---|
--format | string | text | Output format: text, json, sarif. |
--redact | boolean | false | Mask the match and secret strings in every emitted finding. |
--quiet | boolean | false | Suppress progress output. Only findings are emitted. |
--verbose | boolean | false | Print diagnostic info to stderr (skipped rules, invalid regex, etc.). |
--concurrency <n> | number | auto | Rayon worker threads. 0 / omit = auto. |
text is the default TTY format — grouped per file, colourised when stdout is a TTY. json emits an array of Finding objects with paths relative to the workspace root. sarif emits SARIF v2.1.0 with file:// URIs, a tool.driver.rules[] cross-reference, and result.level — compatible with GitHub code scanning and GitLab security dashboards.
Baseline
| Option | Type | Default | Description |
|---|---|---|---|
--baseline <path> | string | .secrets-baseline.json | Suppress findings whose fingerprint already appears in the baseline. |
--init | boolean | false | Scaffold a baseline from current findings. Refuses to overwrite. |
--dry-run | boolean | false | With --init, preview without writing files. |
--update-baseline | boolean | false | Merge current findings into the baseline and exit 0. |
--replace-baseline | boolean | false | With --update-baseline, replace rather than merge. |
Baselines are fingerprint-keyed JSON files (<file>:<ruleID>:<startLine>) committed to the repo. A missing file is treated as empty; a malformed file emits a stderr warning but never fails the scan. When a baseline is present the default text output appends a diff summary (+N new, N unchanged, -N resolved).
Exit codes
| Code | Meaning |
|---|---|
0 | No findings after suppression, or --update-baseline succeeded. |
1 | One or more findings emitted. |
2 | Usage / configuration error (invalid flag value, no git tree). |
Workspace config
Commit defaults once in vis.config.ts under secrets. The grouped shape mirrors ScanOptions:
import { defineConfig } from "@visulima/vis/config";
export default defineConfig({
secrets: {
baseline: ".secrets-baseline.json",
config: {
extendBundled: true,
minConfidence: "medium",
onlyVerified: false,
// Opt-in preset shorthand — each entry expands to `tag:preset:<name>`
// and is merged with `rules.enable`. See "Bundled opt-in groups"
// above for the available presets.
presets: ["weak-passwords", "exposed-files"],
validate: false,
},
rules: {
// `enable` still works for ad-hoc rule ids or tags; `presets` is the
// ergonomic path for the bundled groups.
exclude: ["generic-api-key"],
},
redact: false,
walk: {
excludeFromFiles: [".secretsignore"],
excludePatterns: ["dist/**", "coverage/**"],
gitignore: true,
includeHidden: false,
maxFileSize: 10 * 1024 * 1024,
},
},
});CLI flags always take precedence over the config file. The full reference lives in @visulima/secret-scanner → Configuration.
Pre-commit hook
vis hook add secretsAppends a managed block to .husky/pre-commit (or creates one) that runs vis secrets --staged --quiet. Remove the block — or delete the file — to disable.
CI recipe (SARIF upload)
# .github/workflows/secrets.yml
name: Secrets
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: pnpm/action-setup@v4
- run: pnpm install --frozen-lockfile
- run: pnpm vis secrets --format sarif > report.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: report.sarif }For pull-request gating without a baseline, swap the scan command for:
pnpm vis secrets --since origin/${{ github.base_ref }} --min-confidence mediumMigrating from other tools
vis migrate ports existing gitleaks / secretlint setups to vis secrets:
vis migrate gitleaks # keeps gitleaks.toml, rewrites scripts and hooks
vis migrate secretlint # removes @secretlint/*, rewrites scripts and hooks
vis migrate verify # sanity-check the migrationRelated
@visulima/secret-scanner— the underlying engine and library API.- Configuration reference — every field in
ScanOptions. - Suppression guide — inline markers, baseline workflow, allowlists.
vis hook— install / uninstall / migrate git hooks.