https://github.com/dicklesworthstone/ultimate_bug_scanner
Static analysis tool that catches 1000+ bug patterns across all popular programming languages, with auto-wiring into AI coding agent quality guardrails
https://github.com/dicklesworthstone/ultimate_bug_scanner
agent-tools ai-agents bash bugs cli code-quality developer-tools linting static-analysis
Last synced: about 1 month ago
JSON representation
Static analysis tool that catches 1000+ bug patterns across all popular programming languages, with auto-wiring into AI coding agent quality guardrails
- Host: GitHub
- URL: https://github.com/dicklesworthstone/ultimate_bug_scanner
- Owner: Dicklesworthstone
- License: other
- Created: 2025-11-16T10:01:59.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-04-25T03:31:54.000Z (about 2 months ago)
- Last Synced: 2026-04-25T03:38:48.281Z (about 2 months ago)
- Topics: agent-tools, ai-agents, bash, bugs, cli, code-quality, developer-tools, linting, static-analysis
- Language: Shell
- Homepage:
- Size: 17.2 MB
- Stars: 216
- Watchers: 1
- Forks: 32
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Security: docs/security.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# π¬ Ultimate Bug Scanner v5.0
### **The AI Coding Agent's Secret Weapon: Flagging Likely Bugs for Fixing Early On**
[](./LICENSE)
[](https://github.com/Dicklesworthstone/ultimate_bug_scanner)
[](https://github.com/Dicklesworthstone/ultimate_bug_scanner)
```bash
# One command to catch 1000+ bug patterns (always main, cache-busted)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" \
| bash -s --
```
**Or via Homebrew (macOS/Linux):**
```bash
brew install dicklesworthstone/tap/ubs
```
---
Just want it to do everything without confirmations? Live life on the edge with easy-mode to auto-install every dependency, accept all prompts, detect local coding agents, and wire their quality guardrails with zero extra questions:
```bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" \
| bash -s -- --easy-mode
```
Note: Windows users must run the installer one-liner from within Git Bash, or use WSL for Windows.
---
## π€ Agent Quickstart (JSON/TOON)
**Use machine-readable output in agent contexts.** stdout = data, stderr = diagnostics, exit 0 = success.
```bash
# Scan current repo (JSON)
ubs . --format=json
# Token-optimized output (TOON)
ubs . --format=toon
# Scan only staged changes
ubs --staged --format=json
# CI-strict (fail on warnings)
ubs . --profile=strict --fail-on-warning --format=json
```
## π₯ **The Problem: AI Moves Fast, Bugs Move Faster**
You're coding faster than ever with Claude Code, Codex, Cursor, and other AI coding agents. You're shipping features in minutes that used to take days. **But here's the painful truth:**
### **Even the best AI makes these mistakes:**
**JavaScript/TypeScript example** *(similar patterns exist in Python, Go, Rust, Java, C++, Ruby)*:
```javascript
// β CRITICAL BUG #1: Null pointer crash waiting to happen
const submitButton = document.getElementById('submit');
submitButton.addEventListener('click', handleSubmit); // π₯ Crashes if element doesn't exist
// β CRITICAL BUG #2: XSS vulnerability
function displayUserComment(comment) {
document.getElementById('comments').innerHTML = comment; // π¨ Security hole
}
// β CRITICAL BUG #3: Silent failure (missing await)
async function saveUser(data) {
const result = validateUser(data); // π₯ Should be 'await validateUser(data)'
await saveToDatabase(result); // Saves undefined!
}
// β CRITICAL BUG #4: Always false comparison
if (calculatedValue === NaN) { // π₯ This NEVER works (always false)
console.log("Invalid calculation");
}
// β CRITICAL BUG #5: parseInt footgun
const zipCode = parseInt(userInput); // π₯ "08" becomes 0 in old browsers (octal!)
```
**Each of these bugs could cost 3-6 hours to debug in production.** Similar issues plague every language: unguarded null access, missing `await`, security holes from `eval()`, buffer overflows from `strcpy()`, `.unwrap()` panics, goroutine leaks... **You've probably hit all of them.**
---
## π― **The Solution: Your 24/7 Bug Hunting Partner**
### π§ Language-Aware Meta-Runner
- `ubs` auto-detects **JavaScript/TypeScript, Python, C/C++, Rust, Go, Java, Ruby, Swift, C#, and Elixir** in the same repo and fans out to per-language scanners.
- Each scanner lives under `modules/ubs-.sh`, ships independently, and supports `--format text|json|jsonl|sarif|toon` for consistent downstream tooling.
- Modules download lazily (PATH β repo `modules/` β cached under `${XDG_DATA_HOME:-$HOME/.local/share}/ubs/modules`) and are validated before execution.
- Results from every language merge into one text/JSON/SARIF report via `jq`, so CI systems and AI agents only have to parse a single artifact.
### π Supply-Chain Safeguards
- Every lazily-downloaded module (and its helper assets) ships with pinned SHA-256 checksums baked into the meta-runner. Files fetched from GitHub are verified before they can execute, preventing tampering between releases.
- The cache lives under `${XDG_DATA_HOME:-$HOME/.local/share}/ubs/modules` by default; use `--module-dir` to relocate it (e.g., inside a CI workspace) while retaining the same verification guarantees.
- Run `ubs doctor` at any time to audit your environment. It checks for curl/wget availability, writable cache directories, and per-language module integrity. Add `--fix` to redownload missing or corrupted modules proactively.
- Scanner runs still respect `--update-modules`, but an invalid checksum now causes an immediate failure with remediation guidance rather than executing unverified code.
- **Developer Pre-commit Hook**: The repository ships with a `.githooks/pre-commit` hook that auto-updates `SHA256SUMS` when modules change and blocks commits with stale checksums. This ensures every release has verified checksums without manual intervention.
- **Minisign Support**: For additional assurance, set `UBS_MINISIGN_PUBKEY` to verify cryptographic signatures on `SHA256SUMS` via [minisign](https://jedisct1.github.io/minisign/).
### π Category Packs & Shareable Reports
- `--category=resource-lifecycle` focuses the scanners on Python/Go/Java resource hygiene (context managers, defer symmetry, try-with-resources). UBS automatically narrows the language set to those with lifecycle packs enabled and suppresses unrelated categories.
- `--comparison=` diff the latest combined summary against a stored run. Deltas feed into console output, JSON, HTML, and SARIF automation metadata so CI can detect regressions.
- `--report-json=` writes an enriched summary (project, totals, git metadata, optional comparison block) that you can archive or share with teammates/CI.
- `--html-report=` emits a standalone HTML preview showing totals, trends vs. baseline, and per-language breakdownsβideal for attaching to PRs or chat updates.
- All shareable outputs inject GitHub permalinks when UBS is run inside a git repo with a GitHub remote. Text output automatically annotates `path:line` references, JSON gains `git.*` metadata, and merged SARIF runs now include `versionControlProvenance` plus `automationDetails` keyed by the comparison id.
#### Resource lifecycle heuristics in each language
- **Python** β Category 16 now correlates every `open()` call against matching `with open(...)` usage and explicit `encoding=` parameters, while Category 19 uses the new AST helper at `modules/helpers/resource_lifecycle_py.py` to walk every file, socket, subprocess, asyncio task, and context cancellation path. The helper resolves alias imports, context managers, and awaited tasks so the diff counts (`acquire=X, release=Y, context-managed=Z`) show the exact imbalance per file.
- **Go** β Category 5/17 now run a Go AST walker (`modules/helpers/resource_lifecycle_go.go`) that detects `context.With*` calls missing cancel, `time.NewTicker/NewTimer` without `Stop`, `os.Open/sql.Open` without `Close`, and mutex `Lock`/`Unlock` symmetry. Category 9 also tracks request query/header/form/framework values into response headers unless they strip or reject CR/LF, tracks redirect targets into `http.Redirect`, framework `Redirect` calls, and `Location` headers unless they pass through same-origin or explicit allow-list validation, flags request-derived reverse proxy targets flowing into `httputil.NewSingleHostReverseProxy`, `ProxyRequest.SetURL`, or `Director` URL mutation without HTTPS plus host allow-list validation, flags request-derived SQL text flowing into `ExecContext`/`QueryContext`, sqlx-style helpers, or query-builder predicates unless request data is passed as bound parameters, flags credentialed wildcard or reflected-origin CORS responses, and catches auth/session cookies missing `HttpOnly`, `Secure`, or `SameSite` protections. Findings come straight from the AST/resource helper or taint pass positions, so βticker missing Stop()β and header/open-redirect/reverse-proxy/CORS/cookie/SQL lines map to exact `file:line` references instead of coarse regex summaries.
- **Java / Kotlin** β Category 5 surfaces `FileInputStream`, readers/writers, JDBC handles, etc. that were created outside try-with-resources, while Category 19 keeps tracking executor services and file streams that never close. Category 4 also tracks servlet/Spring/Ktor request parameters, headers, and annotated parameters into response headers unless they strip/reject CR/LF or encode header fragments, and into redirect sinks such as `sendRedirect`, `respondRedirect`, Spring `redirect:` views, `RedirectView`, `ModelAndView`, and `Location` headers unless a same-origin or explicit allow-list helper is applied first. The summary text matches the manifest fixtures, so CI will fail if regression swallows these warnings.
#### Shareable output quickstart
```bash
# 1) Capture a baseline JSON (checked into CI artifacts or local history)
ubs --ci --only=python --category=resource-lifecycle \
--report-json .ubs/baseline.json test-suite/python/buggy
# 2) Re-run with comparison + HTML preview for PRs or chat threads
ubs --ci --only=python --category=resource-lifecycle \
--comparison .ubs/baseline.json \
--report-json .ubs/latest.json \
--html-report .ubs/latest.html \
test-suite/python/buggy
```
`latest.json` now contains the git metadata (repo URL, commit, blob_base) plus a `comparison.delta` block, and `latest.html` renders a lightweight dashboard summarising the deltas. SARIF uploads also pick up the comparison id so repeating runs in CI stay grouped by automation id.
---
## π‘ **Basic Usage**
```bash
# Scan current directory
ubs .
# Scan specific directory
ubs /path/to/your/project
# Verbose mode (show more code examples)
ubs -v .
# Save report to file
ubs . bug-report.txt
# CI mode (exit code 1 on warnings)
ubs . --fail-on-warning
# Quiet mode (summary only)
ubs -q .
# Skip specific categories (e.g., skip TODO markers)
ubs . --skip=11,14
# Custom file extensions
ubs . --include-ext=js,ts,vue,svelte
```
### Handy switches
```bash
# Git-aware quick scans (changed files only)
ubs --staged # Scan files staged for commit
ubs --diff # Scan working tree changes vs HEAD
# Strictness profiles
ubs --profile=strict # Fail on warnings, enforce high standards
ubs --profile=loose # Skip TODO/debug/code-quality nits when prototyping
# Machine-readable output
ubs . --format=json # Pure JSON on stdout; logs go to stderr
ubs . --format=jsonl # Line-delimited summary per scanner + totals
ubs . --format=toon # TOON format (~50% smaller than JSON, LLM-optimized)
ubs . --format=jsonl --beads-jsonl out/findings.jsonl # Save JSONL for Beads/"strung"
```
### Keeping noise low
- UBS auto-ignores common junk (`node_modules`, virtualenvs, dist/build/target/vendor, editor caches, etc.).
- Inline suppression is available when a finding is intentional: `eval("print('safe')") # ubs:ignore`
## π **Quick Install (30 Seconds)**
### **Recommended: Homebrew (macOS/Linux)**
```bash
brew install dicklesworthstone/tap/ubs
```
This method provides:
- Automatic updates via `brew upgrade`
- Dependency management
- Easy uninstall via `brew uninstall`
### **Windows: Scoop**
```powershell
scoop bucket add dicklesworthstone https://github.com/Dicklesworthstone/scoop-bucket
scoop install dicklesworthstone/ubs
```
### **Alternative: Automated Install**
```bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" | bash
```
### **Option 2: Integrity-first install (signed checksums)**
```bash
export UBS_MINISIGN_PUBKEY="RWQg+jMrKiloMT5L3URISMoRzCMc/pVcVRCTfuY+WIzttzIr4CUJYRUk"
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/scripts/verify.sh | bash
```
The verifier downloads `SHA256SUMS` + `SHA256SUMS.minisig` from the matching release, validates them with minisign, checks `install.sh`, and only then executes it. Use `--insecure` to bypass verification (not recommended).
### **Option 3: Nix**
Run directly (no install):
```bash
nix run github:Dicklesworthstone/ultimate_bug_scanner
```
Dev shell for contributors:
```bash
nix develop
```
### **Option 4: Docker / OCI**
Pull & inspect:
```bash
docker run --rm ghcr.io/dicklesworthstone/ubs-tools ubs --help
```
Scan host code (risk-aware: grants container access to host FS):
```bash
docker run --rm -v /:/host ghcr.io/dicklesworthstone/ubs-tools bash -c "cd /host/path && ubs ."
```
β οΈ Use the host-mount pattern only when you understand the write-access implications.
### Deployment & Security
- Release playbook (how we cut signed releases): [docs/release.md](docs/release.md)
- Supply chain & verification model: [docs/security.md](docs/security.md)
The installer will:
- β
Install the `ubs` command globally
- β
Install/ensure `ast-grep` (required for accurate JS/TS scanning; UBS can auto-provision a pinned binary)
- β
Optionally install `ripgrep` (for 10x faster scanning)
- β
Optionally install `jq` (needed for JSON/SARIF merging across all language scanners)
- β
Optionally install `typos` (smart spellchecker for docs and identifiers)
- β
Optionally install `Node.js + typescript` (enables deep TypeScript type narrowing analysis)
- β
Auto-run `ubs doctor` post-install and append a session summary to `~/.config/ubs/session.md`
- β
Capture readiness facts (ripgrep/jq/typos/type narrowing) and store them for `ubs sessions --entries 1`
- β
Set up git hooks (block commits with critical bugs)
- β
Set up Claude Code hooks (scan on file save)
- β
Add documentation to your AGENTS.md
Need to revisit what the installer discovered later? Run `ubs sessions --entries 1` to view the most recent session log (or point teammates at the same summary).
Need the βjust make it workβ button? Run the installer with `--easy-mode` to auto-install every dependency, accept all prompts, detect local coding agents, and wire their quality guardrails with zero extra questions:
```bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" \
| bash -s -- --easy-mode
```
**Total time:** 30 seconds to 2 minutes (depending on dependencies)
Need to keep your shell RC files untouched? Combine `--no-path-modify` (and optionally `--skip-hooks`) with the command aboveβthe installer will still drop `ubs` into your chosen `--install-dir`, but it will skip both PATH edits and the alias helper entirely.
### **Option 2: Manual Install**
```bash
# Download and install the unified runner
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/ubs \
-o /usr/local/bin/ubs && chmod +x /usr/local/bin/ubs
# Verify it works
ubs --help
# Install dependencies (ast-grep required for JS/TS scanning)
# Required for JS/TS scanning (syntax-aware AST engine)
brew install ast-grep # or: cargo install ast-grep, npm i -g @ast-grep/cli
brew install ripgrep # 10x faster searching (or: apt/dnf/cargo install)
brew install typos-cli # Spellchecker tuned for code (or: cargo install typos-cli)
npm install -g typescript # Enables full tsserver-based type narrowing checks
```
### **Option 3: Use Without Installing**
```bash
# Download once
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/ubs \
-o ubs && chmod +x ubs
# Run it
./ubs .
```
### Installer Safety Nets
#### Uninstall from any shell
Run the installer in `--uninstall` mode via curl if you want to remove UBS and all of its integrations:
```bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" | bash -s -- --uninstall --non-interactive
```
This command deletes the UBS binary, shell RC snippets/aliases, config under `~/.config/ubs`, and the optional Claude/Git hooks that the installer set up. Because it passes `--non-interactive`, it auto-confirms all prompts and runs unattended.
| Flag | What it does | Why it matters |
|------|--------------|----------------|
| `--dry-run` | Prints every install action (downloads, PATH edits, hook writes, cleanup) without touching disk. Dry runs still resolve config, detect agents, and show you exactly what *would* change. | Audit the installer, demo it to teammates, or validate CI steps without modifying a workstation. |
| `--self-test` | Immediately runs `test-suite/install/run_tests.sh` after installation and exits non-zero if the smoke suite fails. | CI/CD jobs and verified setups can prove the installer still works end-to-end before trusting a release. |
| `--skip-type-narrowing` | Skip the Node.js + TypeScript readiness probe **and** the cross-language guard analyzers (JS/Rust/Kotlin/Swift/C#). | Useful for air-gapped hosts or environments that want to stay in heuristic-only mode. |
| `--skip-typos` | Skip the Typos spellchecker installation + diagnostics. | Handy when corp images already provide Typos or when you deliberately disable spellcheck automation. |
| `--skip-doctor` | Skip the automatic `ubs doctor` run + session summary after install. | Use when CI already runs doctor separately or when you're iterating locally and want a faster finish. |
> [!WARNING]
> `--self-test` requires running `install.sh` from a working tree that contains `test-suite/install/run_tests.sh` (i.e., the repo root). Curl-piping the installer from GitHub canβt self-test because the harness isnβt present, so the flag will error out early instead of giving a false sense of safety.
> [!NOTE]
> After every install the script now double-checks `command -v ubs`. If another copy shadows the freshly written binary, youβll get an explicit warning with both paths so you can fix PATH order before running scans.
> [!TIP]
> Type narrowing relies on Node.js plus the `typescript` npm package *and* the Python helpers that power the Rust/Kotlin/Swift/C# checks. The installer now checks Node/TypeScript readiness, can optionally run `npm install -g typescript`, and surfaces the status inside `install.sh --diagnose`. Use `--skip-type-narrowing` if youβre on an air-gapped host or plan to keep the heuristic-only mode.
> [!TIP]
> To avoid global npm permission issues, the installer now detects/installs [bun](https://bun.sh/) just like other dependencies and uses `bun install --global typescript` by default, falling back to npm only if bun isnβt available.
>
> The diagnostics also call out Swift guard readiness: if python3 is available we count `.swift` files under your repo and record whether the guard helper will actually run. That fact shows up in `install.sh --diagnose` output and the auto-generated session log so iOS/macOS teams can tell at a glance whether the ObjC-bridging heuristics are active.
**Common combos**
```bash
# Preview everything without touching dotfiles or hooks
bash install.sh --dry-run --no-path-modify --skip-hooks --non-interactive
# CI-friendly install that self-tests the smoke harness
bash install.sh --easy-mode --self-test --skip-hooks
```
### π **Auto-Update**
The `ubs` meta-runner supports an **opt-in** auto-update check (once every 24 hours). This is **disabled by default** for supply-chain safety.
To enable auto-update:
```bash
export UBS_ENABLE_AUTO_UPDATE=1
```
To disable it (even if enabled):
```bash
export UBS_NO_AUTO_UPDATE=1
# or
ubs --no-auto-update .
```
Ultimate Bug Scanner is like having a senior developer review every line of code **in under 5 seconds**; it's the perfect automated companion to your favorite coding agent:
```bash
$ ubs .
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π¬ ULTIMATE BUG SCANNER v4.4 - Scanning your project... β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Project: /Users/you/awesome-app
Files: 247 JS/TS + 58 Python + 24 Go + 16 Java + 11 Ruby + 12 C++/Rust files
Finished: 3.2 seconds
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Summary Statistics:
Files scanned: 247
π₯ Critical: 0 β Would have crashed in production!
β οΈ Warnings: 8 β Should fix before shipping
βΉοΈ Info: 23 β Code quality improvements
β¨ EXCELLENT! No critical issues found β¨
```
---
## β‘ **Why Developers + AI Agents Will Love This Tool**
### π **1. Catches What Humans & AI Miss**
**18 specialized detection categories** covering the bugs that *actually* matter:
Category
What It Prevents
Time Saved Per Bug
Null Safety
"Cannot read property of undefined" crashes
2-4 hours
Security Holes
XSS, code injection, prototype pollution
8-20 hours + reputation damage
Async/Await Bugs
Race conditions, unhandled rejections
4-8 hours
Memory Leaks
Event listeners, timers, detached DOM
6-12 hours
Type Coercion
JavaScript's === vs == madness
1-3 hours
+ 13 more categories
100+ hours/month saved
### π¨ **2. Blazing Fast (Because Your Time Matters)**
```
Small project (5K lines): 0.8 seconds β‘
Medium project (50K lines): 3.2 seconds π
Large project (200K lines): 12 seconds π¨
Huge project (1M lines): 58 seconds π
```
**That's 10,000+ lines analyzed per second.** Faster than you can say "but it worked on my machine."
### π€ **3. Built FOR AI Agents, BY Developers Who Use AI**
Unlike traditional linters that fight AI-generated code, this scanner **embraces** it:
```markdown
β
Designed for Claude Code, Cursor, Windsurf, Aider, Continue, Copilot
β
Zero configuration - works with ANY JS/TS, Python, C/C++, Rust, Go, Java, or Ruby project
β
Integrates with git hooks, CI/CD, file watchers
β
Actionable output (tells you WHAT's wrong and HOW to fix it)
β
Fails fast in CI (catch bugs before they merge)
β
React Hooks dependency analysis that spots missing deps, unstable objects, and stale closures
β
Lightweight taint analysis that traces req.body/window.location/localStorage β innerHTML/res.send/eval/exec/db.query and flags flows without DOMPurify/escapeHtml/parameterized SQL
```
### π **4. Real-World Impact**
Scenario
Without Scanner
With Scanner
AI implements user auth
β’ 3 null pointer crashes (9h debugging)
β’ 1 XSS vulnerability (8h + incident)
β’ 2 race conditions (4h debugging)
Total: ~21 hours + security incident
β’ All issues caught in 4 seconds
β’ Fixed before commit (15 min)
Total: 15 minutes
Savings: 84x faster β‘
Refactor payment flow
β’ Division by zero in edge case (3h)
β’ Unhandled promise rejection (2h)
β’ Missing error logging (1h)
Total: 6 hours debugging
β’ Caught instantly (3 sec)
β’ Fixed before merge (10 min)
Total: 10 minutes
Savings: 36x faster π
---
## π€ **AI Agent Integration (The Real Magic)**
### On-Device Agent Guardrails
`install.sh` now inspects your workstation for the most common coding agents (the same set listed below) and, when asked, drops guardrails that remind those agents to run `ubs --fail-on-warning .` before claiming a task is done. In `--easy-mode` this happens automatically; otherwise you can approve each integration individually.
| Agent / IDE | What we wire up | Why it helps |
|-------------|-----------------|--------------|
| **Claude Code Desktop** (`.claude/hooks/on-file-write.sh`) | File-save hook that shells out to `ubs --ci` whenever Claude saves JS/TS files. | Keeps Claude from accepting βApply Patchβ without a fresh scan. |
| **Cursor** (`.cursor/rules`) | Shared rule block that tells Cursor plans/tasks to run `ubs --fail-on-warning .` and summarize outstanding issues. | Cursorβs autonomous jobs inherit the same QA checklist as humans. |
| **Codex CLI** (`.codex/rules/ubs.md`) | Adds the identical rule block for OpenAI's Codex terminal workflow. Supports both file and directory formats (v0.77.0+). | Ensures Codex sessions never skip the scanner during long refactors. |
| **Gemini Code Assist** (`.gemini/rules`) | Guidance instructing Gemini agents to run `ubs` before closing a ticket. | Keeps Geminiβs asynchronous fixes aligned with UBS exit criteria. |
| **Windsurf** (`.windsurf/rules`) | Guardrail text + sample command palette snippet referencing `ubs`. | Windsurfβs multi-step plans stay grounded in the same quality gate. |
| **Cline** (`.cline/rules`) | Markdown instructions that Clineβs VS Code extension ingests. | Forces every βtool callβ from Cline to mention scanner findings. |
| **OpenCode MCP** (`.opencode/rules`) | Local MCP instructions so HTTP tooling always calls `ubs` before replying. | Makes OpenCode's multi-agent swarms share the same notion of "done". |
#### Codex CLI v0.77.0+ Migration Note
Starting with Codex CLI v0.77.0, the rules storage changed from a **single file** (`.codex/rules`) to a **directory** (`.codex/rules/`) containing individual rule files. The UBS installer handles both formats automatically:
| Codex Version | Rules Location | UBS Installer Behavior |
|---------------|----------------|------------------------|
| < v0.77.0 | `.codex/rules` (file) | Appends UBS quick reference to file |
| β₯ v0.77.0 | `.codex/rules/` (directory) | Creates `.codex/rules/ubs.md` |
**If you upgraded Codex and encounter issues**, migrate manually:
```bash
# Convert file to directory structure
mv ~/.codex/rules ~/.codex/rules.backup
mkdir ~/.codex/rules
mv ~/.codex/rules.backup ~/.codex/rules/ubs.md
```
The installer's `append_quick_reference_block()` function detects the storage format at runtime and writes to the appropriate location, so re-running `install.sh` after upgrading Codex will "just work."
### **Why This Matters for AI Workflows**
When you're coding with AI, you're moving **10-100x faster** than traditional development. But bugs accumulate just as quickly. Traditional tools slow you down. This scanner keeps pace:
```
Traditional workflow: AI-powered workflow with scanner:
ββββββββββββββββββββ ββββββββββββββββββββ
β AI writes code β β AI writes code β
ββββββββββ¬ββββββββββ ββββββββββ¬ββββββββββ
β β
β β
ββββββββββββββββββββ ββββββββββββββββββββ
β You review β β Scanner runs β
β (15 min) β β (3 seconds) β
ββββββββββ¬ββββββββββ ββββββββββ¬ββββββββββ
β β
β β
ββββββββββββββββββββ ββββββββββββββββββββ
β Tests pass? β β Critical bugs? β
ββββββββββ¬ββββββββββ ββββββββββ¬ββββββββββ
β NO! β YES!
β β
ββββββββββββββββββββ ββββββββββββββββββββ
β Debug in prod β β AI fixes them β
β (6 hours) β β (5 minutes) β
ββββββββββββββββββββ ββββββββββ¬ββββββββββ
β
ββββββββββββββββββββ
β Ship with β
β confidence β
ββββββββββββββββββββ
Total: 6.25 hours Total: 8 minutes
```
### **Pattern 1: Claude Code Integration (Real-Time Scanning)**
Drop this into `.claude/hooks/on-file-write.sh`:
```bash
#!/bin/bash
# Auto-scan UBS-supported languages (JS/TS, Python, C/C++, Rust, Go, Java, Ruby, Swift, C#, Elixir) on save
if [[ "$FILE_PATH" =~ \.(js|jsx|ts|tsx|mjs|cjs|py|pyw|pyi|c|cc|cpp|cxx|h|hh|hpp|hxx|rs|go|java|rb|cs|csx|ex|exs|eex|heex|leex|sface)$ ]]; then
echo "π¬ Quality check running..."
if ubs "${PROJECT_DIR}" --ci 2>&1 | head -30; then
echo "β
No critical issues"
else
echo "β οΈ Issues detected - review above"
fi
fi
```
**Result:** Every time Claude writes code, the scanner catches bugs **instantly**.
### **Pattern 2: Git Pre-Commit Hook (Quality Gate)**
The installer can set this up automatically, or add to `.git/hooks/pre-commit`:
```bash
#!/bin/bash
# Block commits with critical bugs
echo "π¬ Running bug scanner..."
if ! ubs . --fail-on-warning 2>&1 | tee /tmp/scan.txt | tail -30; then
echo ""
echo "β Critical issues found. Fix them or use: git commit --no-verify"
echo ""
echo "Top issues:"
grep -A 3 "π₯ CRITICAL" /tmp/scan.txt | head -20
exit 1
fi
echo "β
Quality check passed - committing..."
```
**Result:** Bugs **cannot** be committed. Period.
### **Pattern 3: Cursor/Windsurf/Continue Integration**
Add to your `.cursorrules` or similar:
```markdown
## Code Quality Standards
Before marking any task as complete:
1. Run the bug scanner: `ubs .`
2. Fix ALL critical issues (π₯)
3. Review warnings (β οΈ) and fix if trivial
4. Only then mark task complete
If the scanner finds critical issues, your task is NOT done.
```
**Result:** AI agents have **built-in quality standards**.
### **Pattern 4: CI/CD Pipeline (GitHub Actions Example)**
```yaml
name: Code Quality Gate
on: [push, pull_request]
jobs:
bug-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Bug Scanner
run: |
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" | bash -s -- --non-interactive
- name: Scan for Bugs
run: |
ubs . --fail-on-warning --ci
```
**Result:** Pull requests with critical bugs **cannot merge**.
### **Pattern 5: The Fix-Verify Loop (For AI Agents)**
This is the golden pattern for AI coding workflows:
```bash
#!/bin/bash
# Have your AI agent run this after implementing features
echo "π¬ Post-implementation quality check..."
# Run scanner
if ubs . --fail-on-warning > /tmp/scan-result.txt 2>&1; then
echo "β
All quality checks passed!"
echo "π Ready to commit"
exit 0
else
echo "β Issues found:"
echo ""
# Show critical issues
grep -A 5 "π₯ CRITICAL" /tmp/scan-result.txt | head -30
echo ""
echo "π€ AI: Please fix these issues and re-run this check"
exit 1
fi
```
**Usage pattern:**
```markdown
User: "Add user registration with email validation"
AI Agent:
1. Implements the feature
2. Runs quality check (scanner finds 3 critical bugs)
3. Fixes the bugs
4. Re-runs quality check (passes)
5. Commits the code
Total time: 12 minutes (vs. 6 hours debugging in production)
```
### **Pattern 6: The "AI Agent Decision Tree"**
Train your AI agent to use this decision tree:
```
Did I modify code in any supported language?
(JS/TS, Python, Go, Rust, Java, C++, Ruby)
β
β YES
Changed more than 50 lines?
β
β YES
Run scanner ββββββββββββ
β β
β β
Critical issues found? βββββ€ YES
β NO β
β β
Warnings? β
β β
β YES β
Show to user β
Ask if should fix ββββββββ€
β NO β
β β
Commit code Fix issues
```
---
> [!IMPORTANT]
> **Copy the blurb below to your project's `AGENTS.md`, `.claude/claude_docs/`, or `.cursorrules` file for comprehensive UBS integration guidance.**
````markdown
## UBS Quick Reference for AI Agents
UBS stands for "Ultimate Bug Scanner": **The AI Coding Agent's Secret Weapon: Flagging Likely Bugs for Fixing Early On**
**Install:** `curl -sSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh | bash`
**Golden Rule:** `ubs ` before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
**Commands:**
```bash
ubs file.ts file2.py # Specific files (< 1s) β USE THIS
ubs $(git diff --name-only --cached) # Staged files β before commit
ubs --only=js,python src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode β before PR
ubs --help # Full command reference
ubs sessions --entries 1 # Tail the latest install session log
ubs . # Whole project (ignores things like .venv and node_modules automatically)
```
**Output Format:**
```
β οΈ Category (N errors)
file.ts:42:5 β Issue description
π‘ Suggested fix
Exit code: 1
```
Parse: `file:line:col` β location | π‘ β how to fix | Exit 0/1 β pass/fail
**Fix Workflow:**
1. Read finding β category + fix suggestion
2. Navigate `file:line:col` β view context
3. Verify real issue (not false positive)
4. Fix root cause (not symptom)
5. Re-run `ubs ` β exit 0
6. Commit
**Speed Critical:** Scope to changed files. `ubs src/file.ts` (< 1s) vs `ubs .` (30s). Never full scan for small edits.
**Bug Severity:**
- **Critical** (always fix): Null safety, XSS/injection, async/await, memory leaks
- **Important** (production): Type narrowing, division-by-zero, resource leaks
- **Contextual** (judgment): TODO/FIXME, console logs
**Anti-Patterns:**
- β Ignore findings β β
Investigate each
- β Full scan per edit β β
Scope to file
- β Fix symptom (`if (x) { x.y }`) β β
Root cause (`x?.y`)
````
---
## π¬ **See It In Action**
*Examples show JavaScript output; each language has equivalent detections (Python: None checks, Go: nil guards, Rust: Option handling, etc.)*
### **Example 1: Catching a Null Pointer Bug**
```bash
$ ubs src/
βββ NULL SAFETY & DEFENSIVE PROGRAMMING
Detects: Null pointer dereferences, missing guards, unsafe property access
π₯ CRITICAL (5 found)
Unguarded property access after getElementById
Consider: const el = document.getElementById('x'); if (!el) return;
src/components/form.js:42
const submitBtn = document.getElementById('submit-button');
submitBtn.classList.add('active'); // β Crashes if element missing
src/utils/dom.js:87
const modal = document.querySelector('.modal');
modal.style.display = 'block'; // β Runtime crash guaranteed
π‘ Fix: Always check for null before accessing properties
```
**Before:** 3 production crashes this week
**After:** 0 crashes, caught in 2 seconds
### **Example 2: Security Vulnerability Detection**
```bash
βββ SECURITY VULNERABILITIES
Detects: Code injection, XSS, prototype pollution, timing attacks
π₯ CRITICAL (3 found)
innerHTML without sanitization - XSS risk
Use textContent or DOMPurify.sanitize()
src/comments.js:156
element.innerHTML = userComment; // β XSS vulnerability!
π₯ CRITICAL (1 found)
Hardcoded API keys detected
Use environment variables or secret managers
src/config.js:23
const apiKey = "sk_live_abc123xyz"; // β Security breach!
```
**Before:** Security incident, customer data at risk
**After:** Vulnerability caught before git commit
### **Example 3: Async/Await Gotchas**
```bash
βββ ASYNC/AWAIT & PROMISE PITFALLS
Detects: Missing await, unhandled rejections, race conditions
π₯ CRITICAL (8 found)
await used in non-async function
SyntaxError in JavaScript
src/api/users.js:67
function saveUser(data) {
await database.insert(data); // β SyntaxError!
}
β οΈ WARNING (12 found)
Promises without .catch() or try/catch
Unhandled rejections crash Node.js
src/services/email.js:45
sendEmail(user.email).then(result => ...) // β No error handling!
```
**Before:** Silent failures, mysterious bugs in production
**After:** All async bugs caught and fixed before deploy
---
## π **What It Detects (The Complete Arsenal)**
*Each language module has specialized detections. Examples below are representative (JavaScript shown; Python has `eval()`, Go has goroutine leaks, Rust has `.unwrap()` panics, C++ has buffer overflows, etc.)*
### π΄ **Critical Issues (Production Blockers)**
These **WILL** cause crashes, security breaches, or data corruption:
| Pattern | Example | Why It's Dangerous |
|---------|---------|-------------------|
| `eval()` usage | `eval(userInput)` | Allows arbitrary code execution - **RCE vulnerability** |
| Direct NaN comparison | `if (x === NaN)` | Always returns false - **logic bug** |
| Missing await | `asyncFunc()` in async context | Silent failures, race conditions - **data corruption** |
| Prototype pollution | `obj.__proto__ = {}` | Security vulnerability - **privilege escalation** |
| Unguarded null access | `el.style.color` without null check | **Runtime crash** guaranteed |
| `parseInt` without radix | `parseInt("08")` | Returns 0 in some browsers - **calculation bug** |
| Empty catch blocks | `catch(e) {}` | Swallows errors - **debugging nightmare** |
| `innerHTML` with user data | `el.innerHTML = userInput` | **XSS vulnerability** |
| Missing async keyword | `await` without `async function` | **SyntaxError** |
| Hardcoded secrets | `const key = "sk_live..."` | **Security breach** |
### π‘ **Warnings (Should Fix Before Shipping)**
These cause bugs, performance issues, or maintenance headaches:
| Pattern | Example | Impact |
|---------|---------|--------|
| Promises without `.catch()` | `promise.then(...)` | Unhandled rejections crash Node.js |
| Division without zero check | `total / count` | Returns `Infinity` or `NaN` |
| Event listeners without cleanup | `addEventListener` in React | **Memory leak** (app gets slower over time) |
| `setInterval` without clear | `setInterval(fn, 1000)` | **Timer leak** (infinite timers) |
| `fetch()` without cancellation | `fetch(url)` | Stalled requests can hang workflows and exhaust resources |
| `await` inside loops | `for(...) { await api.call() }` | **Slow** (sequential, not parallel) |
| Array mutation during iteration | `arr.forEach(() => arr.push(...))` | **Skipped/duplicate** elements |
| Missing switch default | `switch(x) { case 1: ... }` | Unhandled values cause silent failures |
| `isNaN()` instead of `Number.isNaN()` | `isNaN("foo")` | Type coercion bugs |
### π΅ **Info (Code Quality & Best Practices)**
Improvements that make code cleaner and more maintainable:
- Optional chaining opportunities (`obj?.prop?.value`)
- Nullish coalescing opportunities (`value ?? default`)
- TypeScript `any` usage (reduces type safety)
- `console.log` statements (remove before production)
- Technical debt markers (TODO, FIXME, HACK)
- Performance optimizations (DOM queries in loops)
- `var` usage (use `let`/`const` instead)
- Deep property access without guards
- Large inline arrays (move to separate files)
- Complex nested ternaries (readability)
---
## βοΈ **Advanced Configuration**
### **Command-Line Options (Full Reference)**
```bash
ubs [OPTIONS] [PROJECT_DIR] [OUTPUT_FILE]
Core Options:
-v, --verbose Show 10 code samples per finding (default: 3)
-q, --quiet Minimal output (summary only)
--ci CI mode (stable output, no colors by default)
--fail-on-warning Exit with code 1 on warnings (strict mode)
--version Print UBS meta-runner version and exit
--profile=MODE strict|loose (sets defaults for strictness)
--baseline=FILE Compare findings against a baseline JSON (alias for --comparison)
-h, --help Show help and exit
Git Integration:
--staged Scan only files staged for commit
--diff, --git-diff Scan only modified files (working tree vs HEAD)
Output Control:
--format=FMT Output format: text|json|jsonl|sarif|toon (default: text)
--beads-jsonl=FILE Write JSONL summary alongside normal output for Beads/"strung"
--no-color Force disable ANSI colors
OUTPUT_FILE Save report to file (auto-tees to stdout)
File Selection:
--include-ext=CSV File extensions (default: auto-detect by language)
JS: js,jsx,ts,tsx,mjs,cjs | Python: py,pyi,pyx
Go: go | Rust: rs | Java: java | C++: cpp,cc,cxx,c,h
Ruby: rb,rake,ru | C#: cs,csx | Custom: --include-ext=js,ts,vue
--exclude=GLOB[,...] Additional paths to exclude (comma-separated)
Example: --exclude=legacy (deps ignored by default)
--skip-size-check Skip directory size guard (use with care)
Performance:
--jobs=N Parallel jobs for ripgrep (default: auto-detect cores)
Set to 1 for deterministic output
Rule Control:
--skip=CSV Skip categories by number (see output for numbers)
Example: --skip=11,14 # Skip debug code + TODOs
--skip-type-narrowing Disable helper-backed guard analysis (falls back to text heuristics)
--rules=DIR Additional ast-grep rules directory
Rules are merged with built-in rules
--no-auto-update Disable automatic self-update
--suggest-ignore Print large-directory candidates to add to .ubsignore (no changes applied)
Environment Variables:
JOBS Same as --jobs=N
NO_COLOR Disable colors (respects standard)
CI Enable CI mode automatically
UBS_MAX_DIR_SIZE_MB Max directory size in MB before refusing to scan (default: 1000)
UBS_SKIP_SIZE_CHECK Skip directory size guard entirely (set to 1)
Arguments:
PROJECT_DIR Directory to scan (default: current directory)
OUTPUT_FILE Save full report to file
Exit Codes:
0 No critical issues (or no issues at all)
1 Critical issues found
1 Warnings found (only with --fail-on-warning)
2 Invalid arguments or environment error (e.g., missing ast-grep for JS/TS)
```
**Directory size guard**
UBS computes scan size **after ignore filters** (defaults + `.ubsignore`) and prints:
`Scan size after ignores: XMB (limit YMB)` before enforcing the limit. Override via
`UBS_MAX_DIR_SIZE_MB` or `UBS_SKIP_SIZE_CHECK=1`, or pass `--skip-size-check`.
### Environment errors (exit 2)
If UBS prints an **Environment error** and exits `2`, a required dependency is missing or unusable.
Most common fix for JS/TS projects:
```bash
ubs doctor --fix
```
Or install the dependency manually:
```bash
brew install ast-grep # or: cargo install ast-grep, npm i -g @ast-grep/cli
```
If youβre intentionally scanning non-JS languages only, exclude JS:
```bash
ubs --exclude=js .
```
### **Examples**
```bash
# Basic scan
ubs .
# Verbose scan with full details
ubs -v /path/to/project
# Strict mode for CI (fail on any warning)
ubs --fail-on-warning --ci
# Save report without cluttering terminal
ubs . report.txt
# Scan Vue.js project
ubs . --include-ext=js,ts,vue
# Skip categories you don't care about
ubs . --skip=14 # Skip TODO/FIXME markers
# Maximum performance (use all cores)
ubs --jobs=0 . # Auto-detect
ubs --jobs=16 . # Explicit core count
# Exclude vendor code
ubs . --exclude=node_modules,vendor,dist,build
# Large directories (size guard)
UBS_MAX_DIR_SIZE_MB=5000 ubs .
UBS_SKIP_SIZE_CHECK=1 ubs .
# Custom rules directory
ubs . --rules=~/.config/ubs/custom-rules
# Combine multiple options
ubs -v --fail-on-warning --exclude=legacy --include-ext=js,ts,tsx . report.txt
```
### JSONL schema
`--format=jsonl` (and `--beads-jsonl=FILE`) emit newline-delimited objects for easy piping into tools like Beads or `jq`:
```jsonl
{"type":"scanner","project":"/path/to/project","language":"python","files":42,"critical":1,"warning":3,"info":12,"timestamp":"2025-11-22T09:04:20Z"}
{"type":"totals","project":"/path/to/project","files":99,"critical":1,"warning":3,"info":27,"timestamp":"2025-11-22T09:04:22Z"}
```
### **Custom AST-Grep Rules**
You can add your own bug detection patterns:
```bash
# Create custom rules directory
mkdir -p ~/.config/ubs/rules
# Add a custom rule (YAML format)
cat > ~/.config/ubs/rules/no-console-in-prod.yml <<'EOF'
id: custom.no-console-in-prod
language: javascript
rule:
any:
- pattern: console.log($$$)
- pattern: console.debug($$$)
- pattern: console.info($$$)
severity: warning
message: "console statements should be removed before production"
note: "Use a proper logging library or remove debug statements"
EOF
# Run with custom rules
ubs . --rules=~/.config/ubs/rules
```
**Common custom rules:**
```yaml
# Enforce specific naming conventions
id: custom.component-naming
language: typescript
rule:
pattern: export function $NAME() { $$$ }
not:
pattern: export function $UPPER() { $$$ }
severity: info
message: "React components should start with uppercase letter"
```
```yaml
# Catch specific anti-patterns in your codebase
id: custom.no-direct-state-mutation
language: typescript
rule:
pattern: this.state.$FIELD = $VALUE
severity: critical
message: "Never mutate state directly - use setState()"
```
### **Excluding False Positives**
If the scanner reports false positives for your specific use case:
```bash
# Skip entire categories
ubs . --skip=11,14 # Skip debug code detection and TODO markers
# Exclude specific files/directories
ubs . --exclude=legacy,third-party,generated
# For persistent config, create a wrapper script
cat > ~/bin/ubs-custom <<'EOF'
#!/bin/bash
ubs "$@" \
--exclude=legacy,generated \
--skip=14 \
--rules=~/.config/ubs/rules
EOF
chmod +x ~/bin/ubs-custom
```
---
## π **How It Works (Under the Hood)**
### **Multi-Layer Analysis Engine**
The scanner uses a sophisticated 4-layer approach:
```
Layer 1: PATTERN MATCHING (Fast) βββ
ββ Regex-based detection β
ββ Optimized with ripgrep β
ββ Finds 70% of bugs in <1 second β
ββββΊ Combined Results
Layer 2: AST ANALYSIS (Deep) βββββββ€
ββ Semantic code understanding β
ββ Powered by ast-grep β
ββ Catches complex patterns β
β
Layer 3: CONTEXT AWARENESS (Smart) β€
ββ Understands surrounding code β
ββ Reduces false positives β
ββ Knows when rules don't apply β
β
Layer 4: STATISTICAL (Insightful) β
ββ Code smell detection β
ββ Anomaly identification β
ββ Architectural suggestions β
β
Final Report (3-5 sec)
```
### **Technology Stack**
| Component | Technology | Purpose | Why This Choice |
|-----------|-----------|---------|-----------------|
| **Core Engine** | Bash 4.0+ | Orchestration | Universal compatibility, zero dependencies |
| **Pattern Matching** | Ripgrep | Text search | 10-100x faster than grep, parallelized |
| **AST Parser** | ast-grep | Semantic analysis | Understands code structure, not just text |
| **Fallback** | GNU grep | Text search | Works on any Unix-like system |
| **Rule Engine** | YAML | Pattern definitions | Human-readable, easy to extend |
### **AST Rule Architecture: Ancestor-Aware Pattern Matching**
UBS's ast-grep rules use a sophisticated technique called **ancestor traversal** to drastically reduce false positives. The key directive `stopBy: end` ensures patterns check the *entire* ancestor chain rather than just the immediate parent.
**The Problem Without Ancestor Traversal:**
```javascript
// This code is SAFE - the fetch is properly handled:
async function safeFetch() {
try {
fetch('/api'); // Inside try block - exception will be caught
} catch (e) {
handleError(e);
}
}
// Naive AST rule checking only immediate parent:
// β False positive! Reports "fetch without catch" because
// fetch()'s immediate parent is the ExpressionStatement,
// not the try block.
```
**The Solution - Ancestor Traversal with `stopBy: end`:**
```yaml
# ast-grep rule with proper ancestor checking
rule:
all:
- pattern: fetch($ARGS)
- not:
inside:
kind: try_statement
stopBy: end # β Key directive: traverse ALL ancestors
- not:
inside:
pattern: $_.catch($$) # Check for .catch() in chain
stopBy: end
```
The `stopBy: end` directive instructs ast-grep to walk up the *entire* ancestor tree until it finds a match (or reaches the root). Without it, only the immediate parent is checkedβmissing try blocks, function boundaries, and method chains.
**Real-World Impact:**
| Scenario | Without `stopBy: end` | With `stopBy: end` |
|----------|----------------------|-------------------|
| `try { fetch() } catch {}` | β False positive | β
Correctly ignored |
| `fetch().then().catch()` | β False positive | β
Correctly ignored |
| `return fetch()` | β False positive | β
Correctly ignored |
| `fetch()` standalone | β
Detected | β
Detected |
This technique is applied across 19+ rules in the JavaScript module alone, covering:
- Promise chain detection (`.then()`, `.catch()`, `.finally()`)
- Try-catch context awareness
- Return statement handling
- Async/await scope analysis
### **Inline Suppression Comments**
When a finding is intentional or a known false positive, suppress it inline:
```javascript
// Suppress a single line:
eval(trustedCode); // ubs:ignore
// Suppress with reason (recommended):
eval(adminScript); // ubs:ignore -- admin-only trusted input
```
```python
# Python suppression:
exec(validated_code) # ubs:ignore
# Ruby suppression:
eval(safe_string) # ubs:ignore
```
**Suppression Rules:**
- Must appear on the **same line** as the flagged code
- Works across all 10 supported languages
- Suppresses all findings on that line (use sparingly)
- Survives formatting tools that preserve trailing comments
**Anti-patterns to avoid:**
```javascript
// β Wrong - comment on previous line doesn't suppress:
// ubs:ignore
eval(code); // Still flagged!
// β Wrong - don't blanket-suppress large blocks:
/* ubs:ignore */ // Doesn't work for block comments
```
### **Cross-Language Async Error Detection**
UBS detects unhandled async errors consistently across all 10 languages. The patterns adapt to each language's idioms while providing equivalent coverage:
| Language | Pattern | What UBS Detects |
|----------|---------|------------------|
| **JavaScript/TypeScript** | `promise.then()` without `.catch()`, `new Promise(async ...)`, `forEach(async ...)`, async array predicates, async timer/event/JSX-handler callbacks, `Promise.all(map(...))` without callback return | Dangling promises, missing `await`, unawaitable async callbacks, unhandled rejections |
| **Python** | `asyncio.create_task()` without `await` | Orphaned tasks, missing `await`, unclosed coroutines |
| **Go** | Goroutine without error channel | Fire-and-forget goroutines, leaked contexts |
| **Rust** | `.unwrap()` / `.expect()` after partial guard | Panic after `if let Some`, missing `?` operator |
| **Java** | `CompletableFuture` without `.exceptionally()` | Swallowed exceptions, missing `join()` |
| **Ruby** | `Thread.new` without `.join` | Zombie threads, unhandled thread exceptions |
| **C++** | `std::async` without `.get()` | Ignored futures, exception propagation |
| **Swift** | `Task {}` without error handling | Unstructured concurrency leaks |
| **C#** | `Task.Wait()` / `.Result` / `throw ex;` | Sync-over-async deadlocks, stack-trace loss, unsafe exception surfaces |
**JavaScript Promise Chain Analysis:**
The scanner understands complex promise chains:
```javascript
// β
Handled - .catch() at end of chain:
fetch('/api')
.then(r => r.json())
.then(data => process(data))
.catch(handleError); // Scanner recognizes this catches all above
// β
Handled - .catch() before .then():
fetch('/api')
.catch(e => fallback) // Early catch
.then(r => r.json());
// β Unhandled - .finally() doesn't catch:
fetch('/api')
.then(r => r.json())
.finally(cleanup); // Flagged: finally doesn't handle rejections
// β Unhandled - no error handling:
async function leaky() {
fetch('/api'); // Flagged: fire-and-forget promise
}
```
### **Helper Script Verification**
Language-specific helper scripts (Python AST walkers, Go analyzers, TypeScript type checkers) are verified with SHA-256 checksums before execution:
```bash
# Helper checksums embedded in each module:
modules/helpers/
βββ async_task_handles_csharp.py # SHA-256 verified
βββ resource_lifecycle_csharp.py # SHA-256 verified
βββ resource_lifecycle_py.py # SHA-256 verified
βββ resource_lifecycle_go.go # SHA-256 verified
βββ resource_lifecycle_java.py # SHA-256 verified
βββ type_narrowing_csharp.py # SHA-256 verified
βββ type_narrowing_ts.js # SHA-256 verified
βββ type_narrowing_rust.py # SHA-256 verified
βββ type_narrowing_kotlin.py # SHA-256 verified
βββ type_narrowing_swift.py # SHA-256 verified
```
The `ubs doctor` command validates all helper checksums:
```bash
$ ubs doctor
π₯ UBS Environment Audit
ββββββββββββββββββββββββ
β helper checksum verified (resource_lifecycle_py.py)
β helper checksum verified (type_narrowing_ts.js)
...
```
If a helper is modified or corrupted, the scanner fails safely with remediation guidance rather than executing unverified code.
### **Unified Severity Normalization**
All 10 language modules normalize their findings to a consistent severity scale, ensuring predictable output regardless of source language:
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Language Tool Output β UBS Normalized Severity β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ESLint "error" β critical β
β Pylint "E" / "F" β critical β
β Clippy "deny" β critical β
β Go vet "error" β critical β
β SpotBugs "High" β critical β
β RuboCop "Fatal/Error" β critical β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ESLint "warn" β warning β
β Pylint "W" / "R" β warning β
β Clippy "warn" β warning β
β Go vet "warning" β warning β
β SpotBugs "Medium" β warning β
β RuboCop "Warning" β warning β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ESLint "suggestion" β info β
β Pylint "C" / "I" β info β
β Clippy "note" β info β
β SpotBugs "Low" β info β
β RuboCop "Convention" β info β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
**Benefits of normalization:**
- **Consistent exit codes**: Exit 1 always means "critical issues found" across all languages
- **Unified JSON/SARIF output**: Downstream tools parse one schema, not 8 different formats
- **Predictable `--fail-on-warning`**: Same behavior whether scanning Python, Rust, or TypeScript
- **Cross-language metrics**: Compare code quality across polyglot projects fairly
The `normalize_severity()` function in each module handles edge cases like tool-specific severity strings, numeric levels, and legacy format variations.
### **Performance Optimizations**
```bash
# Automatic parallelization (uses all CPU cores)
- Auto-detects: 16-core = 16 parallel jobs
- Manually set: --jobs=N
# Smart file filtering (only scans relevant files)
- JS/TS: .js, .jsx, .ts, .tsx, .mjs, .cjs (auto-skip node_modules/dist/build)
- Python: .py + pyproject/requirements (skip venv/__pycache__)
- C/C++: .c/.cc/.cpp/.cxx + headers + CMake files (skip build/out)
- Rust: .rs + Cargo manifests (skip target/.cargo)
- Go: .go + go.mod/go.sum/go.work (skip vendor/bin)
- Java: .java + pom.xml + Gradle scripts (skip target/build/out)
- Ruby: .rb + Gemfile/Gemspec/Rakefile (skip vendor/bundle,tmp)
- Custom: --include-ext=js,ts,vue
# Efficient streaming (low memory usage)
- No temp files created
- Results streamed as found
- Memory usage: <100MB for most projects
# Incremental scanning (future feature)
- Only scan changed files (git diff)
- Cache previous results
- 10x faster on large projects
```
---
## π **Comparison with Other Tools**
| Feature | Ultimate Bug Scanner | ESLint | TypeScript | SonarQube | DeepCode |
|---------|---------------------|--------|------------|-----------|----------|
| **Setup Time** | 30 seconds | 30 minutes | 1-2 hours | 2-4 hours | Account required |
| **Speed (50K lines)** | 3 seconds | 15 seconds | 8 seconds | 2 minutes | Cloud upload |
| **Zero Config** | β
Yes | β No | β No | β No | β No |
| **Works Without Types** | β
Yes | β
Yes | β No | β
Yes | β
Yes |
| **Null Safety** | β
Yes | β οΈ Limited | β
Yes | β οΈ Limited | β οΈ Limited |
| **Security Scanning** | β
Yes | β οΈ Plugin | β No | β
Yes | β
Yes |
| **Memory Leaks** | β
Yes | β No | β No | β οΈ Limited | β No |
| **Async/Await** | β
Deep | β οΈ Basic | β
Good | β οΈ Basic | β οΈ Basic |
| **CI/CD Ready** | β
Yes | β
Yes | β
Yes | β
Yes | β οΈ Cloud |
| **Offline** | β
Yes | β
Yes | β
Yes | β οΈ Limited | β No |
| **AI Agent Friendly** | β
Built for it | β οΈ Config heavy | β οΈ Config heavy | β Complex | β οΈ Cloud |
| **Cost** | Free | Free | Free | $$$$ | $$$ |
**When to use what:**
- **Ultimate Bug Scanner**: Quick scans, AI workflows, no config needed
- **ESLint**: Style enforcement, custom rules, team standards
- **TypeScript**: Type safety (use WITH this scanner)
- **SonarQube**: Enterprise compliance, detailed metrics
- **DeepCode**: ML-powered analysis (if you trust cloud)
**Best combo:** TypeScript + ESLint + Ultimate Bug Scanner = Maximum safety
---
## π§ **Project Justification and Rationale**
### **Why This Exists (And Why It's Not "Just Another Linter")**
You might be thinking: *"We already have ESLint, Pylint, Clippy, RuboCop... why build another tool?"*
**Fair question. And honestly, your first reaction is probably right to be skeptical.**
### **The Initial Skepticism is Valid (But Misses the Point)**
When you first look at UBS, it's natural to think:
> *"This is just worse ESLint. It has fewer rules, uses regex (false positives!), and doesn't auto-fix anything. Why would I use this instead of mature, comprehensive linters?"*
**That's analyzing through the wrong lens.**
You're comparing it to tools designed for a **fundamentally different workflow** (human developers writing code manually) when it's solving a **fundamentally different problem** (LLM agents generating code at 100x speed).
It's like comparing a smoke detector to a building inspector:
- **Building inspector (ESLint):** Thorough, comprehensive, finds every issue, takes hours
- **Smoke detector (UBS):** Fast, catches critical dangers, instant alert, always running
**You need both.** But when your house might be on fire (AI just generated 500 lines in 30 seconds), you want the smoke detector first.
### **The Paradigm Shift: AI-Native Development**
Software development is undergoing a **fundamental transformation**:
**2020 (Pre-LLM Era):**
- Developer writes 50-200 lines/day manually
- Deep thought before each line
- Single language per project (mostly)
- Time to review: abundant
- Quality gate: comprehensive linting + code review (hours)
**2025 (LLM Era):**
- AI generates 500-5000 lines/day across projects
- Code appears in seconds
- Polyglot projects standard (microservices in Go, UI in TypeScript, ML in Python, workers in Rust)
- Time to review: scarce
- Quality gate needed: instant feedback (<5s) or the loop breaks
**Traditional tools weren't designed for this.** They were built when "code generation" meant 200 lines/day, not 2000.
### **Here's the Fundamental Difference:**
### **1. This Tool is Built FOR AI Agents, Not Just Humans**
Traditional linters were designed for **human developers** in **single-language codebases**. UBS is designed for **LLM coding agents** working across **polyglot projects**.
**The paradigm shift:**
| Traditional Linting (Human-First) | UBS Approach (LLM-First) |
|---|---|
| **Goal:** Comprehensive coverage + auto-fix
**Speed:** 15-60 seconds acceptable
**Setup:** 30 min config per language
**Languages:** One tool per language
**False positives:** Must be <1% (frustrates humans)
**Output:** Human-readable prose | **Goal:** Critical bug detection + fast feedback
**Speed:** <5 seconds required
**Setup:** Zero config (instant start)
**Languages:** One scan for all 10 languages
**False positives:** 10-20% OK (LLMs filter instantly)
**Output:** Structured file:line for LLM parsing |
### **2. LLMs Don't Need Auto-FixβThey ARE the Auto-Fix Engine**
**Why traditional linters have auto-fix:**
```javascript
// ESLint flags: "Use === instead of =="
if (value == null) // β
// ESLint auto-fix (rigid, no context):
if (value === null) // β
Technically correct, but...
```
**Why UBS doesn't (and shouldn't):**
```javascript
// UBS flags: "Type coercion bug: == should be ==="
if (value == null) // β
// Claude reads the error and understands context:
if (value !== null && value !== undefined) // β
Better - handles both
// OR
if (value != null) // β
Or keeps == for null/undefined (intentional)
```
**The hard part is DETECTION, not fixing.** Once flagged, LLMs can:
- Understand semantic context
- Consider surrounding code
- Apply the right fix (not just the mechanical one)
- Refactor holistically
Auto-fix would be **worse** because it's context-free. LLMs need to know **WHAT'S wrong** and **WHERE**, then they fix it properly.
### **3. The Multi-Language Zero-Config Design is the Moat**
**Imagine asking Claude to set up quality gates for a polyglot project:**
**Traditional approach (15-30 min per project):**
```bash
# JavaScript/TypeScript
npm install --save-dev eslint @eslint/js @typescript-eslint/parser
# Create .eslintrc.js (200 lines of config)
# Python
pip install pylint black mypy
# Create .pylintrc, pyproject.toml sections
# Rust
# Add to Cargo.toml: [lints]
# Configure clippy rules
# Go
# Install golangci-lint, create .golangci.yml
# Java
# Setup Checkstyle + PMD + SpotBugs + config XMLs
# C++
# Setup clang-tidy, create .clang-tidy config
# Ruby
# Create .rubocop.yml with 150+ lines
# Now run 7 different commands and parse 7 different output formats...
```
**UBS approach (30 seconds):**
```bash
curl -fsSL https://raw.githubusercontent.com/.../install.sh | bash
ubs .
# Done. All 10 languages scanned, unified report.
```
**This matters because:**
- LLMs generate code across languages in one session (Python API β Go service β TypeScript UI β Rust worker)
- Configuring 7 tools is error-prone for LLMs
- Humans don't want to maintain 7 different config files
- CI/CD pipelines want one command, one exit code
### **Type Narrowing Coverage Across Languages**
- **TypeScript** β UBS shells out to `tsserver` (via the bundled helper) whenever Node.js + the `typescript` package are available. The installer surfaces a "Type narrowing readiness" diagnostic so you immediately know if tsserver-powered guards are running. Category 7 also flags bearer tokens, webhook signatures, CSRF values, API keys, HMAC digests, and password-reset secrets compared with `==`, `===`, `!=`, or `!==` instead of timing-safe equality, catches request-derived object merges and dynamic key writes that can allow prototype pollution unless prototype keys are rejected, catches request-derived SQL string construction flowing into raw `db.query`/`sequelize.query`/Prisma `$queryRawUnsafe` execution instead of parameterized calls, catches server-side proxy and rewrite targets from `createProxyMiddleware`, `http-proxy`, `proxy.web`/`proxy.ws`, and `NextResponse.rewrite` when they are request-derived without proxy target validation, and catches JWT decode-only flows, explicit verification bypass options, `none` algorithms, and verifier calls that lack issuer/audience claim binding.
- **Rust** β A Python helper inspects `if let Some/Ok` guard clauses and flags subsequent `.unwrap()`/`.expect()` calls outside of exiting blocks. The Rust security pass also catches bearer tokens, webhook signatures, HMAC/MAC digests, CSRF values, API keys, and password-reset secrets compared with `==` or `!=` instead of `subtle::ConstantTimeEq`, `ring::constant_time::verify_slices_are_equal`, `crypto_memcmp`, or a reviewed constant-time helper; JWT decode/validation bypasses such as `dangerous::insecure_decode`, `dangerous_unsafe_decode`, `insecure_disable_signature_validation()`, `validate_exp = false`, `validate_aud = false`, and `decode` calls whose validation does not bind and require both issuer and audience; archive member paths flowing into extraction destinations without `enclosed_name()`/`unpack_in()`-style containment; predictable temp-file writes that use shared temp paths without `tempfile` or `create_new(true)`; request-derived redirect targets flowing into redirects or `Location` headers without local-path or redirect host allow-list validation; request-derived values interpolated into SQL strings that reach raw sqlx/diesel/rusqlite/postgres execution sinks instead of parameterized placeholders; credentialed wildcard or reflected-origin CORS policies without an explicit trusted-origin allow-list; and request/header values flowing into non-`Location` response headers without CR/LF rejection, `HeaderValue::from_str`, encoding, or a header-safe helper. Fixtures and manifest cases keep these regressions tested.
- **Go** β Category 9 now flags bearer tokens, webhook signatures, HMAC/MAC digests, CSRF values, API keys, and password-reset secrets compared with `==` or `!=` instead of `hmac.Equal`, `subtle.ConstantTimeCompare`, or a reviewed constant-time helper. It also catches JWT verification bypasses such as `ParseUnverified`, `SigningMethodNone`, `WithoutClaimsValidation`, `jwt.Parse` / `ParseWithClaims` callbacks that trust claims without signing-method validation, verified JWTs that omit issuer/audience claim binding, request-derived reverse proxy targets flowing into `httputil.NewSingleHostReverseProxy`, `ProxyRequest.SetURL`, or `Director` URL mutation without HTTPS plus host allow-list validation, and request/form/header values interpolated into SQL execution or query-builder strings instead of passed as parameters.
- **Kotlin** β The Java-family module scans `.kt`/`.kts` sources for `if (value == null)` guards that merely log and keep running before hitting `value!!`, and its security pass now catches request/Ktor params, request paths, and upload filenames flowing into file read/write/serve/delete sinks, request-derived values flowing into response headers without CR/LF safety, request-derived query/header URLs flowing into Ktor-style HTTP client calls, zip extraction that writes `entry.name` / `entry.path` into destination paths without containment checks, and security-sensitive tokens, CSRF nonces, API keys, OTPs, reset codes, and invite codes built from `kotlin.random.Random`, `java.util.Random`, `ThreadLocalRandom`, `UUID.randomUUID`, or predictable time material instead of `SecureRandom`.
- **Swift** β The dedicated `ubs-swift` module now ships the guard-`let` helper directly, so optional chaining/ObjectiveβC bridging heuristics fire even when you run `ubs --only=swift` locally (no piggybacking on the Java module). It catches cases where code logs and keeps going before force-unwrapping `value!`, protecting iOS/macOS pipelines that blend Swift + ObjC. The Swift security pass also tracks request query, route, URL path, and upload filename values into file read/write/serve/delete sinks unless they are reduced to a basename or pass a standardized root-containment check, flags request-derived redirect targets that reach redirects or `Location` headers without local-url or host allow-list validation, flags request/header/cookie/content values reaching non-`Location` response headers without CR/LF stripping/rejection, encoding, or a header-safe helper, flags request-derived outbound URLs that reach URLSession, URLRequest handoffs, blocking URL reads, or HTTP clients without https scheme and host allow-list validation, and now flags security-sensitive tokens, CSRF nonces, API keys, OTPs, salts, and reset/invite codes built from non-cryptographic Swift randomness or predictable time/process material instead of `SecRandomCopyBytes` or CryptoKit-backed helpers.
### **Resource Lifecycle AST Coverage**
- **Python** β `modules/helpers/resource_lifecycle_py.py` now reasons over the AST, tracking `with`/`async with`, alias imports, and `.open()`/`.connect()` calls so `ubs-python` warns only when a handle is truly leaking. Pathlib `Path.open()` and similar patterns are handled without brittle regexes.
- **Java** β New ast-grep rules (`java.resource.executor-no-shutdown`, `java.resource.thread-no-join`, `java.resource.jdbc-no-close`, `java.resource.resultset-no-close`, `java.resource.statement-no-close`) ensure ExecutorServices, raw `Thread`s, `java.sql.Connection`s, `Statement`/`PreparedStatement`/`CallableStatement`, and `ResultSet` handles all get proper shutdown/close semantics before the regex fallback ever runs.
- **C#** β `modules/helpers/resource_lifecycle_csharp.py`, `modules/helpers/type_narrowing_csharp.py`, and `modules/helpers/async_task_handles_csharp.py` now catch disposable-handle leaks (`CancellationTokenSource`, stream-like readers/writers, `HttpRequestMessage`), null/`TryGetValue` guards that log but still fall through into dereferences, and `Task.Run`/`Task.Factory.StartNew` handles that are created but never observed. The C# security pass also tracks ASP.NET request/query/header/path values and upload filenames into file read/write/serve/delete sinks unless they go through `Path.GetFileName` or `Path.GetFullPath` containment checks, flags request/header/cookie/route values that reach `Redirect`, `Response.Redirect`, `RedirectResult`, or `Location` headers without local-url or host allow-list validation, flags request/query/header/form and annotated action values reaching response headers without CR/LF stripping, rejection, or encoding, and flags request/header-derived outbound URLs reaching `HttpClient`, `HttpRequestMessage`, `WebRequest`, `WebClient`, or REST-style clients without URI parsing plus scheme and host allow-list validation.
- **C++ / Rust / Ruby / Elixir** β These modules already relied on ast-grep rule packs or language-tailored context passes; the βUniversal AST Adoptionβ epic is now complete with every language module (JS, Python, Go, C++, Rust, Java, Ruby, Swift, C#, Elixir) running semantic detectors instead of fragile grep-only heuristics. C++ now tracks CGI/query/header URL values into redirect functions and `Location` headers unless they pass through same-origin local-path checks or explicit redirect host allow-lists, into non-`Location` response headers unless they reject/strip CR/LF, encode header fragments, or pass through a header-safe helper, into libcurl/common HTTP client URL sinks unless they pass through a safe outbound URL helper, and flags security-sensitive tokens, CSRF nonces, API keys, OTPs, salts, reset codes, and invite codes built from `rand`, `random`, implementation-defined `random_device`, Mersenne Twister-style engines, timestamps, hashes, or process IDs instead of OS/crypto-backed random bytes. Rust tracks query/header/env/CLI URL values into `reqwest`, `ureq`, `surf`, `isahc`, and request-builder sinks unless they pass through a safe outbound URL helper or equivalent URL parsing plus host allow-list validation, tracks query/header/host redirect targets into redirect responses or `Location` headers unless they pass through same-origin local-path checks or redirect host allow-lists, tracks request/header values into non-`Location` response headers unless they reject/strip CR/LF, use `HeaderValue` validation, encode header fragments, or pass through a header-safe helper, and tracks request-derived values interpolated into raw SQL strings that reach sqlx, diesel, rusqlite, postgres, or generic query execution sinks without parameter binding. Ruby's security pass now tracks Rack/Rails params and upload filenames into file read/write/serve/delete sinks unless the path is reduced to `File.basename` or guarded by `File.expand_path` containment checks, flags request-derived redirect targets reaching `redirect_to`, Sinatra/Rack `redirect`, or `Location` headers without local-url or host allow-list validation, flags request/header/cookie/env values reaching non-`Location` response headers without CR/LF stripping, rejection, encoding, or a header-safe helper, and flags request-derived outbound URLs reaching common Ruby HTTP clients without URI parsing plus scheme and host allow-list validation. Elixir's security pass tracks Plug/Phoenix params, request paths, and upload filenames into `File.*`, `send_file`, and `send_download` sinks unless the path is reduced to `Path.basename` or guarded by `Path.expand` containment checks, flags request-derived redirect targets reaching Phoenix/Plug redirects or `Location` headers without local-url or host allow-list validation, flags request/header/cookie values reaching non-`Location` response headers without CR/LF stripping, rejection, encoding, or a header-safe helper, flags request-derived outbound URLs reaching Req, HTTPoison, Finch, Tesla, hackney, Mint, or `:httpc` without URI parsing plus scheme and host allow-list validation, and flags Phoenix/Guardian/Joken hardcoded config secrets such as `secret_key_base`, signing salts, JWT/API secrets, and literal `System.get_env/2` fallbacks.
#### Python β AST helper in action
```python
import asyncio, subprocess
fh = open("/tmp/leaky.txt", "w")
proc = subprocess.Popen(["sleep", "1"])
async def leak_task():
task = asyncio.create_task(asyncio.sleep(1))
await asyncio.sleep(0.1)
return task
asyncio.run(leak_task())
```
```
$ ./ubs --only=python test-suite/python/buggy/resource_lifecycle.py
π₯ File handles opened without context manager/close [resource_lifecycle.py:4]
File handle fh opened without context manager or close()
β Popen handles not waited or terminated [resource_lifecycle.py:7]
```
The helper catches the unguarded file handle, zombie subprocess, and orphaned asyncio task because it walks the AST (tracking aliases and async contexts) instead of grepping for strings.
#### Go β AST helper validating cleanups
```go
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
ticker := time.NewTicker(time.Millisecond * 500)
timer := time.NewTimer(time.Second)
f, _ := os.Open("/tmp/data.txt")
_ = ctx
_ = cancel
_ = ticker
_ = timer
_ = f
```
```
$ ./ubs --only=golang test-suite/golang/buggy/resource_lifecycle.go
π₯ context.With* without deferred cancel [resource_lifecycle.go:10]
β time.NewTicker not stopped [resource_lifecycle.go:13]
β time.NewTimer not stopped [resource_lifecycle.go:15]
β os.Open/OpenFile without defer Close() [resource_lifecycle.go:17]
```
Because the helper hashes AST positions, the manifest can assert on deterministic substrings (context/ticker/timer/file) and we avoid flakiness from color codes or log headings.
Use `--skip-type-narrowing` (or `UBS_SKIP_TYPE_NARROWING=1`) when you want to bypass all of these guard analyzersβfor example on air-gapped CI environments or when validating legacy projects one language at a time.
### **4. Speed Enables Tight Iteration Loops**
The **generate β scan β fix** cycle needs to be **fast** for AI workflows:
```
βββββββββββββββββββββββββββββββββββββββββββ
β Traditional Linter (30-45 seconds) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Claude generates code: 10s β
β Run ESLint + Pylint + ... 30s β³ β
β Claude reads findings: 5s β
β Claude fixes bugs: 15s β
β Re-run linters: 30s β³ β
β ββββββββββββββββββββββββββββββββββ β
β Total iteration: 90s β
βββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββ
β UBS (3-5 seconds) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Claude generates code: 10s β
β Run UBS: 3s β‘ β
β Claude reads findings: 2s β
β Claude fixes bugs: 10s β
β Re-run UBS: 3s β‘ β
β ββββββββββββββββββββββββββββββββββ β
β Total iteration: 28s β
βββββββββββββββββββββββββββββββββββββββββββ
3x faster feedback loop = 3x more iterations in the same time
```
**When you're shipping 10+ features a day with AI assistance, this compounds.**
### **5. Detecting LLM-Specific Bug Patterns**
UBS targets the bugs **AI agents actually generate**, not every possible code smell.
**Bugs LLMs frequently produce:**
| Pattern | Why LLMs Generate It | Traditional Linters |
|---------|---------------------|---------------------|
| Missing `await` | Forgets `async` keyword, syntax looks fine | β TypeScript only |
| Unguarded null access | "Optimistic" coding - assumes happy path | β οΈ Requires strict config |
| `eval()` / code injection | Reaches for "easy" dynamic solution | β
Most flag this |
| Memory leaks (event listeners) | Doesn't think about cleanup lifecycle | β ESLint plugin needed |
| `innerHTML` XSS | Doesn't threat-model user input | β οΈ Security plugins only |
| Division by zero | Doesn't consider edge cases | β Most miss this |
| Hardcoded secrets | Uses placeholder, forgets to externalize | β οΈ Requires secrets scanner |
| Goroutine leaks | Forgets context cancellation | β Go-specific tooling |
| `.unwrap()` panics | Assumes success path | β
Clippy catches |
| Buffer overflows | Forgets bounds checking | β οΈ Sanitizers only |
**UBS is optimized for this specific threat model.**
### **6. Novel Analysis: Deep Property Guard Correlation**
This is genuinely **not available in standard linters**:
```python
# Code LLM generates:
def get_theme(user):
return user.profile.settings.theme # β Unguarded chain
# ESLint/Pylint: β
No error (syntactically correct)
# TypeScript: β
No error (if types claim non-null)
# UBS Deep Guard Analysis:
# 1. Scans for: user.profile.settings.theme (found at line 42)
# 2. Scans for: if user and user.profile and user.profile.settings
# 3. Correlates: NO MATCHING GUARD FOUND
# 4. Reports: β οΈ Unguarded deep property access
```
**This requires:**
- AST extraction of property chains across the file
- AST extraction of conditional guards
- Cross-reference matching with context awareness
- Contextual suggestions
**Nobody else does this by default** because it's not a lint ruleβit's a **correlation analysis** across multiple code patterns.
### **7. Complementary, Not Competitive**
**UBS is designed to work WITH existing tools, not replace them:**
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Quality Stack (Recommended) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β TypeScript β Type safety β
β ESLint/Clippy/etc β Comprehensive linting β
β Jest/PyTest β Unit tests β
β β¨ UBS β AI-generated bug oracle β
β GitHub Actions β CI/CD integration β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
**Use UBS for:**
- β
Fast multi-language scanning in AI workflows
- β
Critical bug detection before commits
- β
Git hooks that block obviously broken code
- β
Claude/Cursor/AI agent quality guardrails
- β
Polyglot projects where configuring 7 linters is painful
**Use ESLint/Pylint/Clippy/etc for:**
- β
Comprehensive style enforcement
- β
Framework-specific rules (React hooks, etc.)
- β
Custom team conventions
- β
Auto-formatting
- β
Deep single-language analysis
**They solve different problems.** UBS is the "smoke detector" (fast, catches critical issues). Traditional linters are the "building inspector" (thorough, catches everything).
### **8. The Technical Moat**
What makes this hard to replicate:
**Multi-layer analysis:**
```
Layer 1: Ripgrep (regex) β 70% of bugs in 0.5s
Layer 2: ast-grep (AST) β Complex semantic patterns
Layer 3: Correlation logic β Cross-pattern analysis (novel)
Layer 4: Metrics collection β Time-series quality tracking
```
**This combination of speed + semantic understanding + correlation is unique.**
**Unified multi-language runner:**
- Auto-detects 10 languages in one scan
- Parallel execution (Go + Python + Rust simultaneously)
- Unified JSON/SARIF output for tooling
- Module system with lazy download/caching
**LLM-optimized integration points:**
- Git hooks (block bad commits)
- Claude Code file-write hooks
- `.cursorrules` / `.aiconfig` integration
- Clean structured output for LLM parsing
### **9. 30 Rules is Better Than 600 (For This Use Case)**
**You might notice:** ESLint has 200+ core rules, Clippy has 600+ lints, but UBS has ~30 patterns per language.
**That's intentional, not a limitation.**
**The 80/20 rule for AI-generated bugs:**
- **80% of production-breaking bugs** come from ~30 common patterns
- **20% of edge cases** require the other 570 rules
**For LLM workflows, you want:**
```
β
Fast scan (3s) that catches 80% of critical bugs
β
LLM fixes them immediately
β
β
Fast re-scan (3s) confirms fixes
β
Then run comprehensive linters (30s) for the remaining 20%
```
**Not:**
```
β Comprehensive scan (30s) that catches 100% of issues
β
LLM waits... workflow broken... context switch...
β
Slower iteration = fewer features shipped
```
**The bugs UBS targets are:**
- Missing `await` (crashes)
- Null pointer access (crashes)
- Security holes: `eval()`, XSS, hardcoded secrets (breaches)
- Memory leaks (performance degradation)
- Race conditions (data corruption)
**The bugs comprehensive linters add:**
- Inconsistent quote style (style)
- Missing trailing commas (style)
- Prefer `const` over `let` when not reassigned (style)
- Function name should be camelCase (style)
- Line too long (style)
**Which matters more when AI just generated 500 lines that might have `eval()` and missing `await` everywhere?**
Target the **critical bugs** that cost hours. Let comprehensive linters handle style in a separate pass.
### **10. Market Timing: This Wouldn't Have Made Sense 3 Years Ago**
**Why this tool exists NOW:**
**2021:**
- GitHub Copilot launches (single-line completions)
- Still mostly human-written code
- Traditional linting workflow works fine
**2023:**
- ChatGPT/GPT-4 can generate full functions
- Claude Code, Cursor emerge
- Devs start AI-assisted workflows
- Pain point appears: "AI is fast but buggy"
**2025:**
- LLMs generate entire features in minutes
- Multi-file refactors happen in seconds
- Polyglot microservices are standard
- **Quality gates can't keep up with generation speed**
**The problem UBS solves didn't exist before LLM coding became mainstream.**
This tool is **perfectly timed** for the AI coding explosion happening RIGHT NOW.
### **11. False Positive Philosophy**
**For human developers:**
- False positive = context switch + investigation + frustration
- Acceptable rate: <1%
**For LLM agents:**
- False positive = parse (0.1s) + analyze (0.5s) + determine safe (0.2s)
- Acceptable rate: 10-20%
**LLMs don't get frustrated.** They evaluate every finding programmatically.
**This means UBS can be more aggressive:**
- Flag suspicious patterns even if not 100% certain
- Catch more bugs at the cost of some noise
- LLMs filter false positives cognitively (for free)
Better to flag 100 issues where 20 are safe than miss 1 critical bug.
---
## **FAQ: Common Questions and Objections**
### **Q: "Isn't this just reinventing the wheel? ESLint already exists."**
**A:** It's not reinventing the wheelβit's building a different vehicle for a different road.
ESLint is a **truck** (heavy, comprehensive, hauls everything).
UBS is a **sports car** (fast, targeted, gets you there quickly).
You wouldn't use a truck for a Formula 1 race. You wouldn't use a sports car to move furniture.
**Different tools, different use cases.** Use UBS for rapid AI iteration, ESLint for comprehensive quality enforcement.
---
### **Q: "Why not just contribute these patterns to existing linters?"**
**A:** Three reasons:
**1. Different design philosophy**
- Existing linters: comprehensive, human-first, single-language
- UBS: fast, LLM-first, multi-language, correlation-based
These are fundamentally incompatible goals. ESLint would never accept "10-20% false positives are fine" or "skip auto-fix entirely."
**2. Multi-language meta-runner**
- The unified runner that auto-detects 10 languages is the core innovation
- This doesn't fit into any single linter's architecture
- Each linter project has different maintainers, philosophies, release cycles
**3. Correlation analysis is novel**
- Deep property guard matching isn't a "lint rule"
- It's cross-pattern analysis that requires a different architecture
- Existing linters don't have this capability baked into their core
Contributing patterns misses the pointβ**the integration IS the innovation.**
---
### **Q: "What about Semgrep? Doesn't it do multi-language pattern matching?"**
**A:** Semgrep is excellent and closer to UBS than traditional linters. Key differences:
| Feature | Semgrep | UBS |
|---------|---------|-----|
| **Setup** | Requires config file + rule selection | Zero config |
| **Speed** | ~10-20s on medium projects | ~3s (optimized for speed) |
| **Target user** | Security teams, human developers | LLM agents |
| **Rule focus** | Security + custom patterns | AI-generated bug patterns |
| **Multi-language** | β
Yes | β
Yes |
| **Correlation analysis** | β Pattern matching only | β
Deep guards, metrics |
| **LLM integration** | Not designed for it | Purpose-built |
**Use Semgrep if:** You need custom security rules and have time to configure them.
**Use UBS if:** You want instant AI workflow integration with zero setup.
**They're complementary.** Some users run both.
---
### **Q: "Won't regex-based detection have tons of false positives?"**
**A:** Less than you'd think, and it's acceptable for LLM consumers.
**Reality check:**
- **Layer 1 (ripgrep/regex):** ~15-20% false positive rate on some patterns
- **Layer 2 (ast-grep/AST):** ~2-5% false positive rate (semantic understanding)
- **Layer 3 (correlation):** ~1-3% false positive rate (contextual analysis)
**Blended approach:** ~8-12% overall false positive rate.
**Why this is OK:**
- LLMs don't get frustrated like humans do
- They evaluate findings in 0.8 seconds total
- Better to flag 100 (20 safe) than miss 1 critical bug
- Humans reviewing AI code ALREADY have to check everything anyway
**For critical patterns** (eval, XSS, hardcoded secrets), we use ast-grep (high precision).
**For style patterns**, we use regex (fast, some FP acceptable).
**And we're always improving.** Each release reduces FP rate through better heuristics.
---
### **Q: "Why Bash? Why not Python/Rust/Go?"**
**A:** Controversial choice, but intentional:
**Advantages of Bash:**
- β
**Zero dependencies** - runs on any Unix-like system
- β
**Universal availability** - every dev machine has Bash 4.0+
- β
**Shell integration** - git hooks, CI/CD, file watchers are natural
- β
**Module system** - each language scanner is standalone
- β
**Rapid prototyping** - adding new patterns is trivial
- β
**LLM-readable** - AI agents can understand and modify rules
**Disadvantages:**
- β Not as "elegant" as Python
- β String handling can be verbose
- β No static typing
**Bottom line:** For a tool that orchestrates existing CLI tools (ripgrep, ast-grep, jq, typos) and needs to be universally available, Bash is pragmatic.
**Future:** Core modules might be rewritten in Rust for speed, but the meta-runner will stay Bash for compatibility.
---
### **Q: "Can I use this if I'm NOT using AI coding tools?"**
**A:** Absolutely! It's optimized for AI workflows, but works great for humans too.
**Scenarios where humans love it:**
**1. Code review speed-up**
```bash
# Reviewing a PR with 800+ lines
git checkout feature-branch
ubs .
# Instantly see critical issues before deep review
```
**2. Legacy code audits**
```bash
# "What's dangerous in this 10-year-old codebase?"
ubs /path/to/legacy-app
# Finds all the eval(), XSS, memory leaks
```
**3. Learning new languages**
```bash
# "I'm new to Rust, what am I doing wrong?"
ubs . --verbose
# Shows common Rust pitfalls in your code
```
**4. Polyglot projects**
```bash
# Microservices in 5 languages
ubs .
# One scan, all languages checked
```
**Humans appreciate:**
- Zero setup (no config files to maintain)
- Fast feedback (3s vs 30s)
- Multi-language support (one command)
- Finding bugs ESLint misses (deep guards)
---
### **Q: "How is this different from security scanners like Snyk or GitHub Advanced Security?"**
**A:** Different focus and scope:
**Security scanners (Snyk, Dependabot, etc.):**
- Focus: **Dependency vulnerabilities**
- Checks: npm packages, CVE databases
- Speed: Seconds to minutes
- Output: "Update package X to fix CVE-2024-1234"
**UBS:**
- Focus: **Code-level bugs** in YOUR code
- Checks: Logic errors, null safety, memory leaks, security anti-patterns
- Speed: 3-5 seconds
- Output: "You have unguarded null access at line 42"
**They're complementary:**
```
βββββββββββββββββββββββββββββββββββββββ
β Complete Security Stack β
βββββββββββββββββββββββββββββββββββββββ€
β Snyk/Dependabot β Dependencies β
β β¨ UBS β Your code bugs β
β SAST tools β Deep security β
β GitHub Advanced β Secrets in Git β
βββββββββββββββββββββββββββββββββββββββ
```
**Security scanners won't catch:** "You forgot `await` and your async function silently fails."
**UBS won't catch:** "Your version of lodash has a known CVE."
Use both.
---
### **Q: "Will you support language X in the future?"**
**A:** Probably! The module system makes it easy to add languages.
**Current:** JavaScript/TypeScript, Python, Go, Rust, Java, C++, Ruby, Swift, C#, Elixir (10 languages)
**Roadmap considerations:**
- **PHP** - High demand, lots of legacy code
- **Kotlin** - Android development
- **Scala** - JVM ecosystem
- **Shell** - DevOps and installer scripts
**How we prioritize:**
1. Community demand (GitHub issues)
2. AI coding tool usage (what LLMs generate most)
3. Module maintainer availability
**Want to contribute?** Writing a new module is ~800-1200 lines of Bash. Check the existing modules as templates.
---
### **Q: "What's the catch? Why is this free?"**
**A:** No catch. It's MIT licensed.
**Philosophy:**
- Built by developers, for developers
- AI coding is exploding, quality tools should be accessible
- Open source enables community contributions (more patterns, better detection)
**Business model:** None currently. This is a **community project**.
**Future possibilities:**
- Enterprise support contracts
- Hosted version for teams
- Premium modules for specific frameworks
But the core tool will always be free and open source.
---
## **The Bottom Line**
**This isn't trying to replace ESLint.** It's solving a different problem:
> **"How do I give LLM coding agents the ability to self-audit across 10 languages with zero configuration overhead and sub-5-second feedback?"**
No existing tool does this because:
- Traditional linters are human-first (need auto-fix, low FP tolerance)
- They're single-language focused (polyglot = 7 different tools)
- They're comprehensive, not fast (30s scan time kills AI iteration loops)
- They're not designed for LLM consumption
**UBS is purpose-built for the AI coding era.**
Use it WITH your existing tools. Let ESLint handle style. Let TypeScript handle types. Let UBS catch the critical bugs that AI agents generate but can't see.
---
## π§ͺ **Development & Internals**
### **Python Tooling (uv + CPython 3.13)**
All helper scripts (manifest runner, fixtures, inline analyzers inside `ubs`) assume a single source of truth: **CPython 3.13 managed by [uv](https://github.com/astral-sh/uv)** living inside `.venv/` at the repo root.
```bash
# 1) Install uv (one-time)
curl -LsSf https://astral.sh/uv/install.sh | sh
# 2) Create the managed environment defined by pyproject.toml / uv.lock
uv sync --python 3.13
# 3) Activate it whenever you work in this repo (puts .venv/bin first on PATH)
source .venv/bin/activate
# 4) Run any Python entrypoint through the env
uv run python test-suite/run_manifest.py --case js-core-buggy
# ...or rely on 'python'/'python3' now that they point at .venv/bin/python3.13
```
> [!NOTE]
> Shell scripts that invoke `python3` (language modules under `modules/`, `test-suite/run_all.sh`, etc.) automatically pick up `.venv/bin/python3` as long as the environment is activated or `.venv/bin` is on your `PATH`. The pinned `pyproject.toml` + `uv.lock` are the single source of truth for this toolchain.
Common uv-powered entrypoints:
- `uv run python test-suite/run_manifest.py --case js-core-buggy` β run the manifest in CI or locally without manually activating the venv.
- `source .venv/bin/activate && python -m pip list` β verify that every inline `python3` invocation maps to CPython 3.13.
- `uv run python - <<'PY' β¦` β mirrors how the language modules embed Python helpers, but now guaranteed to execute inside the managed interpreter.
---
## π« **Ignoring Paths with `.ubsignore`**
Need repo-wide scans to ignore generated code or intentionally buggy fixtures (like this projectβs `test-suite/`)? Drop a `.ubsignore` at the root.
- Format mirrors `.gitignore`: one glob per line, `#` for comments.
- UBS loads `PROJECT/.ubsignore` automatically; override with `--ignore-file=/path/to/file`.
- Built-in ignores already cover `node_modules`, virtualenvs, dist/build/target/vendor, editor caches, and more, so you rarely need to add them yourself.
- Use `--suggest-ignore` to print large top-level directories that might deserve an entry (no files are modified automatically).
- Inline suppression works for intentional one-offs: `eval("print('safe')") # ubs:ignore`.
- Every language module receives the ignore list via their `--exclude` flag, so skips stay consistent.
- This repository ships with a default `.ubsignore` that excludes `test-suite/`, keeping βrealβ source scans noise-free.
Example:
```text
# Ignore fixtures + build output
test-suite/
dist/
coverage/
```
---
## π§ **Language Coverage Comparison**
UBS ships ten language-focused analyzers. The comparison below focuses on the longest-standing mo