agentsec · Tools · vibeaudit

Offline · stdlib-only · MIT

vibeaudit: detect security smells in AI-written code in AI agents


An offline, standard-library-only Python 3 CLI that scans Python, JavaScript and TypeScript (and optionally shell) source for security smells that show up a lot in AI-generated ("vibe-coded") code: eval on user input, shell=True with f-strings, verify=False, SQL built with f-strings or template literals, pickle/yaml.load, md5 password hashes, Math.random() tokens, hardcoded keys, app.run(debug=True, host="0.0.0.0"), CORS * with credentials, unverified JWTs, innerHTML/dangerouslySetInnerHTML, and the classic LLM placeholders such as # TODO: add authentication and return True # bypass for now.

What it detects

An offline, standard-library-only Python 3 CLI that scans Python, JavaScript and TypeScript (and optionally shell) source for security smells that show up a lot in AI-generated ("vibe-coded") code: eval on user input, shell=True with f-strings, verify=False, SQL built with f-strings or template literals, pickle/yaml.load, md5 password hashes, Math.random() tokens, hardcoded keys, app.run(debug=True, host="0.0.0.0"), CORS * with credentials, unverified JWTs, innerHTML/dangerouslySetInnerHTML, and the classic LLM placeholders such as # TODO: add authentication and return True # bypass for now.

Honesty note. vibeaudit is a heuristic pattern scanner, not a SAST replacement. It uses regular expressions on single lines, a few multi-line regexes, and (for Python) cheap AST checks. It does not do data-flow or taint analysis, so it cannot tell whether a value is really attacker-controlled. It cannot tell whether code was written by an AI. The rules are just patterns often seen in LLM output, and humans write them too. Expect false positives and false negatives. A clean run does not mean the code is secure. Use it as a quick first-pass review aid next to real tools (Semgrep, CodeQL, Bandit, ESLint security plugins) and human review.

Rules

This tool does not publish a numbered rules table; see the README for what it flags.

Usage

python3 vibeaudit.py .                           # scan the current project
python3 vibeaudit.py src app.py --shell          # include shell scripts found in dirs
python3 vibeaudit.py . --min-severity medium     # hide low findings
python3 vibeaudit.py . --json > findings.json
python3 vibeaudit.py . --sarif > vibeaudit.sarif
python3 vibeaudit.py . --exclude 'tests/*' --exclude '*.spec.ts'
python3 vibeaudit.py . --select VA05,VA09        # only some rules
python3 vibeaudit.py . --ignore VA15             # drop a noisy rule
python3 vibeaudit.py --list-rules

After pip install of the kit, the same options work as agentsec vibeaudit …. Every tool also accepts --help and almost every tool accepts --json.

Example output

The full sample set (python3 vibeaudit.py examples --shell) triggers all 17 rules:

The safe counterparts produce nothing:

$ python3 vibeaudit.py examples/vulnerable_app.py --select VA02,VA05,VA14
examples/vulnerable_app.py:25:5: HIGH VA05 SQL passed to .execute() is built with string formatting/concatenation (CWE-89)
    > cur.execute(f"SELECT * FROM users WHERE name = '{name}'")
    fix: Use parameterized queries (cursor.execute(sql, params), db.query(sql, [values]), ORM bind parameters).
examples/vulnerable_app.py:31:12: HIGH VA05 SQL variable 'sql' (built with formatting at line 30) passed to .execute() (CWE-89)
    > return sqlite3.connect("app.db").execute(sql).fetchall()
    fix: Use parameterized queries (cursor.execute(sql, params), db.query(sql, [values]), ORM bind parameters).
examples/vulnerable_app.py:35:12: HIGH VA02 subprocess.check_output(shell=True) with a formatted/concatenated command (CWE-78)
    > return subprocess.check_output(f"ping -c 1 {host}", shell=True)
    fix: Pass an argument list without a shell (subprocess.run([...], shell=False)); use shlex.quote only as a last resort.
examples/vulnerable_app.py:39:5: HIGH VA02 os.system() with a non-literal command string (CWE-78)
    > os.system("tar czf /tmp/backup.tgz " + path)
    fix: Pass an argument list without a shell (subprocess.run([...], shell=False)); use shlex.quote only as a last resort.
examples/vulnerable_app.py:70:1: HIGH VA14 Auth/permission function 'is_admin' unconditionally returns True (CWE-287)
    > def is_admin(user):
    fix: Implement the check (or fail closed) before shipping; remove 'return True' stubs and bypass flags.
examples/vulnerable_app.py:71:7: MEDIUM VA14 TODO left in place of an authentication/authorization/validation check (CWE-287)
    > # TODO: add authentication and role checks
    fix: Implement the check (or fail closed) before shipping; remove 'return True' stubs and bypass flags.

vibeaudit: 6 finding(s) (5 high, 1 medium, 0 low) in 1 file(s) scanned.
Heuristic pattern scan only - review each finding; absence of findings is not proof of safety.
$ echo $?
1

SARIF and CI

agentsec vibeaudit path/to/input --sarif > vibeaudit.sarif

# GitHub Actions (composite action ships with the repo)
- uses: dunkleschitz/agentsec@v0
  with:
    tools: vibeaudit
    paths: path/to/input
    fail-on: high

SARIF 2.1.0 can be uploaded to GitHub code scanning. The composite action writes per-tool reports under agentsec-sarif/ by default.

OWASP mapping

Heuristic mapping of what this tool helps a reviewer look for. Not an official OWASP endorsement, and not complete coverage of any item. Sources: OWASP Top 10 for LLM Applications 2025 and OWASP Top 10 for Agentic Applications 2026.

Known limits

Full source, tests, and fixtures: tools/vibeaudit/ in the repository. Install the kit with pip install git+https://github.com/dunkleschitz/agentsec.git.