Guardrails for vibe-coded software.
"AI coding tools made it cheap to generate code. They did not make it cheap to trust code."
Security boundary: read the normative Trustworthy Observe threat model for the exact guarantees, non-guarantees, trust assumptions, and residual-risk policy. A complete or no-findings run is not a security certification.
The deterministic pre-merge safety gate for AI-generated diffs — runs in single-digit seconds, fully offline, no API key.
Try it locally (full walkthrough in Try it in 30 seconds):
pip install vibeguard-gate
vibeguard scan --path .Add it as a GitHub Actions PR gate — one command generates the workflow:
vibeguard setup github-actionsThis writes .github/workflows/vibeguard.yml wiring three PR surfaces in one
job: inline code-scanning annotations (SARIF), a single summary comment that
updates in place on each push, and a gate that fails the PR on high-severity
findings. Commit the file and open a PR. See
one-command setup for options
(--policy-pack, --fail-on, --with-config, --dry-run).
Prefer to copy/paste instead? The minimal action snippet:
# .github/workflows/vibeguard.yml
name: VibeGuard
on: [pull_request]
jobs:
vibeguard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dgenio/vibeguard@v0.8.0
with:
diff: "true"
fail-on: highWhen a finding blocks, VibeGuard prints a severity/rule/path table and exits non-zero — see Example Output. It complements Semgrep, CodeQL, gitleaks, and Dependabot rather than replacing them; see the tool comparison guide for when to use it and when not to.
AI coding tools let developers ship in hours what used to take days. That is genuinely great. But accepting large AI-generated diffs without scrutiny creates a new failure mode that traditional security tools were not designed to catch:
- Accidentally committed secrets — API keys, tokens, and database URLs sneak in through AI-generated config files
- Package publish leaks — source maps, .env files, and test fixtures end up in npm/PyPI packages
- Dependency supply chain risks — AI agents pull in git URLs, typosquatted packages, or broad version ranges
- Security control bypasses — AI comments out auth checks or disables SSL verification to "make things work"
- Risky code changes without tests — huge diffs touching auth, crypto, and database writes with zero test coverage
- AI footprints — placeholder credentials,
# TODO: implement real auth, andtrust all certificates
VibeGuard is not another AI wrapper, SAST scanner, or dependency checker.
It is a fast, deterministic pre-merge safety gate specifically designed for the failure modes of AI-assisted coding ("vibe coding"). It runs in seconds, works offline, and requires no API key.
Think of it as the check between "AI generated this diff" and "this diff reaches production."
| Category | Examples |
|---|---|
| 🔑 Secrets | AWS keys, GitHub tokens, OpenAI keys, database URLs, private keys, .env files |
| 🗺️ Source maps | .map files in dist/, sourceMappingURL in bundles, npm packages that publish maps |
| 📦 Packaging leaks | .env, tests/, .github/, source maps in npm/PyPI packages |
| 🔗 Dependency risks | git/URL deps, typosquatted packages, unpinned versions (strict mode) |
| eval/exec, shell=True, JWT verify=False, CORS wildcard, pickle.loads, SQL construction | |
| 🧪 Missing tests | Source changes with no corresponding test changes |
| 🤖 AI footprints | Placeholder creds, disabled auth, trust-all-certs, TODO stubs, temporary bypasses |
| 🐚 Slopsquatting | AI-hallucinated dependency names (offline heuristic + opt-in registry check) |
| 💉 Prompt injection | Agent-directed instructions, exfiltration directives, hidden/zero-width Unicode in comments/docs/config |
pip install vibeguard-gateOr from source:
git clone https://github.com/dgenio/vibeguard
cd vibeguard
python -m pip install -e . --group devInitialize a config file:
vibeguard initScan a directory:
vibeguard scan --path .Gate your CI (exits 1 if blocking findings found):
vibeguard gate --diff --fail-on highFor security-sensitive repositories, add --strict-errors so the gate also
fails closed when the scan itself ran degraded (a rule crashed, a plugin failed
to load, git context was unavailable, or a registry lookup failed) instead of
showing a green check on a partial scan. Routine binary/oversize skips never
trip it:
vibeguard gate --diff --fail-on high --strict-errorsThe repo ships two deliberately-vulnerable example packages with fake secrets, so you can see a real, meaningful set of findings without pointing VibeGuard at anything sensitive:
git clone https://github.com/dgenio/vibeguard
cd vibeguard
pip install -e .
vibeguard scan --path examples/vulnerable-node-packageYou should see ~16 findings spanning secrets, source-map leaks, packaging
leaks, risky patterns, and AI footprints — none of which touch the network
or call out to any external service. Try examples/vulnerable-python-package
for a Python-flavored version.
To use VibeGuard as a CI gate (exits non-zero on blocking findings):
vibeguard gate --path examples/vulnerable-python-package --fail-on mediumNo API key, no telemetry, no network calls.
Want changes that look like a real PR rather than a kitchen-sink demo? See
examples/pr-scenarios/ — six self-contained
scenarios (disable-TLS-to-pass-tests, temporary auth bypass, packaging leaks,
committed agent memory, git-URL dependency, risky DB write) each with the
exact command, expected findings, and a fix.
Creates a vibeguard.yaml config file with sensible defaults.
vibeguard init
vibeguard init --path /path/to/repoGenerates .github/workflows/vibeguard.yml — a complete PR gate that uploads
SARIF for inline code-scanning annotations, posts a single self-updating summary
comment, and fails the PR on findings at/above the threshold.
vibeguard setup github-actions # fail-on: high
vibeguard setup github-actions --policy-pack web-app # also writes vibeguard.yaml; fail-on from pack
vibeguard setup github-actions --fail-on medium # override the threshold
vibeguard setup github-actions --dry-run # print the workflow, write nothing
vibeguard setup github-actions --force # overwrite an existing workflowExisting files are never overwritten without --force.
Scans a repository and prints findings. Always exits 0 (informational).
vibeguard scan
vibeguard scan --path .
vibeguard scan --diff # only changed files (requires git)
vibeguard scan --diff --base origin/develop # compare against a non-default base
vibeguard scan --json # machine-readable output
vibeguard scan --markdown # for PR comments
vibeguard scan --sarif # GitHub code scanning
vibeguard scan --rdjson # reviewdog (GitHub/GitLab/Gerrit/Bitbucket)
vibeguard scan --sonar # SonarQube generic issue import
vibeguard scan --verbose # detailed descriptions
vibeguard scan --fail-on medium # set threshold (informational only)
vibeguard scan --sarif --output vibeguard.sarif # write to a file (no shell redirection)
vibeguard gate --report sarif=vg.sarif --report pr-comment=comment.md # several reports, one scanOutput formats can be written to a file with --output PATH (- = stdout), or
emitted several at once from a single scan with the repeatable
--report FORMAT=PATH. See docs/output-schemas.md for
every format (including the machine-actionable remediation metadata and the
SARIF code-scanning result cap).
Same as scan but exits 1 when findings meet or exceed the threshold.
vibeguard gate --path . --fail-on high
vibeguard gate --diff --fail-on medium
vibeguard gate --diff --base origin/develop --fail-on mediumIn --diff mode VibeGuard reports only findings introduced or touched by the
change set — findings whose file is not part of the diff (pre-existing
repository state) are not reported, so a PR gate cannot be blocked by unrelated
history. The base ref is resolved as: --base flag → git.base_branch config →
automatic detection (origin/main → origin/master → main → master). When
no base can be detected the scan degrades to git diff HEAD and emits a visible
diagnostic — in CI use fetch-depth: 0 (and/or --base) so the base branch is
available. See docs/stability-contract.md.
Simulate npm publish or python -m build and gate on findings in the
published file set, before anything reaches the registry.
vibeguard publish-check --path .
vibeguard publish-check --ecosystem npm --manifest-out publish-manifest.json
vibeguard publish-check --ecosystem python-sdist --fail-on mediumSee docs/pre-publish.md for the full guide, finding IDs, and a GitHub Actions release-gate template.
Print a detailed explanation and remediation guide for a finding.
vibeguard explain SEC-ENV
vibeguard explain MAP-DIST
vibeguard explain TEST-MISSINGBelow is the actual output of vibeguard scan --path examples/vulnerable-node-package
on this repository (regenerate with the same command if rules change):
VibeGuard Findings
┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Sev ┃ Rule ┃ Path ┃ Title ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ ☠ CRITICAL │ secrets │ src/server.js:12 │ GitHub Token detected │
│ ✗ HIGH │ secrets │ .env │ Sensitive file committed: .env │
│ ✗ HIGH │ sourcemaps │ package.json │ package.json 'files' includes source maps │
│ ✗ HIGH │ packaging │ package.json │ npm package may publish Environment files (.env) │
│ ✗ HIGH │ packaging │ package.json │ npm package may publish Source map files │
│ ✗ HIGH │ dependencies │ package.json │ URL/git/path dependency: axios │
│ ✗ HIGH │ ai_footprints │ src/server.js:14 │ AI footprint: Security disabled in code │
│ ✗ HIGH │ ai_footprints │ src/server.js:31 │ AI footprint: Trust-all certificates │
│ ✗ HIGH │ auth │ src/server.js:16 │ Auth: Auth bypass TODO/FIXME/HACK comment │
│ ⚠ MEDIUM │ packaging │ .npmignore:8 │ Overly broad .npmignore negation: '!*' │
│ ⚠ MEDIUM │ risky_diff │ src/server.js:9 │ Risk-sensitive area changed: CORS configuration │
│ ⚠ MEDIUM │ risky_diff │ src/server.js:27 │ Risk-sensitive area changed: eval() or exec() usag │
│ ⚠ MEDIUM │ risky_diff │ src/server.js:32 │ Risk-sensitive area changed: Environment variable │
│ ⚠ MEDIUM │ ai_footprints │ src/server.js:17 │ AI footprint: Temporary security bypass or mock │
│ ↓ LOW │ packaging │ package.json │ package.json runs `prepare` at publish time │
│ ℹ INFO │ ai_footprints │ src/server.js:11 │ AI footprint: AI-generated code comment │
└────────────┴────────────────┴──────────────────────┴────────────────────────────────────────────────────┘
Scanned 5 file(s) • 16 finding(s) | critical: 1 high: 8 medium: 5 low: 1 info: 1 • policy: balanced
vibeguard scan always exits 0 (informational). vibeguard gate runs
the same checks but exits 1 when findings meet or exceed --fail-on:
vibeguard gate --path examples/vulnerable-node-package --fail-on high
echo "exit: $?" # exit: 1| Policy | Description |
|---|---|
relaxed |
Only critical and high findings |
balanced |
High + medium findings (default) |
strict |
All findings; unpinned dependencies and missing tests are elevated |
Set in vibeguard.yaml:
policy: strict
fail_on: mediumOr override on the command line:
vibeguard gate --fail-on mediumRun vibeguard init to create a vibeguard.yaml:
policy: balanced
fail_on: high
ignore:
# gitignore-style patterns (same syntax as .vibeguardignore and .gitignore);
# multi-segment patterns like packages/*/build/ are supported.
paths:
- .git/
- node_modules/
- .venv/
- dist/
- build/
findings: [] # suppress specific finding IDs
scanner:
max_file_size_kb: 1024
# Honor the scan root's .gitignore by default. Git-tracked files are always
# scanned (so a committed .env still triggers SEC-ENV); set this to false to
# scan gitignored files too. config ignore.paths + .vibeguardignore form the
# hard-ignore layer applied first; .gitignore only excludes additional
# *untracked* files and cannot re-include a hard-ignored path.
respect_gitignore: true
secrets:
enabled: true
min_entropy: 3.5
sourcemaps:
enabled: true
packaging:
enabled: true
dependencies:
enabled: true
# `risky_patterns:` tunes the `risky_diff` rule — the YAML section name and
# the rule id differ for historical reasons. Use `vibeguard rules explain
# <rule-id>` to look up the config section for any rule.
risky_patterns:
enabled: true
tests:
enabled: true
ai_footprints:
enabled: trueAdd to your pull request workflow:
name: VibeGuard
on: [pull_request]
jobs:
vibeguard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install vibeguard-gate
- run: vibeguard gate --diff --fail-on highFor local development (before publishing to PyPI):
- run: pip install -e .
- run: vibeguard gate --fail-on highVibeGuard ships 13 deterministic rules. Each rule emits one or more
stable finding IDs you can target with vibeguard explain <ID>,
suppress via vibeguard.yaml, or remap via severity_overrides.
| Rule | Default severity | Detects |
|---|---|---|
secrets |
high | AWS keys, GitHub/OpenAI/Slack/Stripe tokens, private keys, bearer tokens, hardcoded passwords, DB URLs with credentials, committed .env files. |
sourcemaps |
high | .map files in dist//build/, sourceMappingURL comments, package.json files entries that include source maps. |
packaging |
medium | npm/PyPI manifests that would publish .env, tests, source maps, build configs; broad MANIFEST.in grafts, .npmignore negations. |
depe |
0 comments
log in to comment.