Broly
A berserker code security scanner.
Secrets · SCA · SAST in a single binary.
AI-powered. No rule files. No rule engine.
What It Does
Broly runs three security scanners in parallel on your codebase and delivers results in seconds:
| Scanner |
Engine |
AI Layer |
| Secrets |
Titus · 487 rules · Hyperscan |
--ai-filter-secrets eliminates false positives |
| SCA |
osv-scalibr + osv.dev · 19 ecosystems |
--ai-sca-reachability checks if the vuln is actually called |
| SAST |
Together AI · Qwen/Qwen3-Coder-Next-FP8 · no rule files, no rule engine |
Always-on · data flow analysis · CVSS scoring |
Install
Go install (any platform):
go install github.com/Shasheen8/Broly/cmd/broly@latest
Linux - pre-built binary from Releases.
macOS - build from source with Hyperscan (faster secrets scanning):
brew install vectorscan
git clone https://github.com/Shasheen8/Broly.git
cd Broly && make build
[!TIP]
go install uses pure Go regex for secrets. The source build enables Hyperscan via -tags vectorscan for significantly faster pattern matching on large codebases.
SAST / AI features require a Together AI key:
export TOGETHER_API_KEY=your_key_here
Usage
broly scan # run all scanners on current directory
broly scan /path/to/project # specific path
# Individual scanners
broly scan --secrets # secrets only
broly scan --sca # SCA only
broly scan --sast # SAST only (requires TOGETHER_API_KEY)
# AI triage suggestions
broly scan --ai-filter-secrets # filter secrets false positives with AI
broly scan --ai-sca-reachability # check if vulnerable deps are actually called
broly scan --ai-triage # verdict (TP/FP) + fix suggestion per finding
broly scan --ai-triage --explain # + concise attack-scenario sentence per finding
broly scan --ai-model Qwen/Qwen3-Coder-Next-FP8 # override model (default)
# Outputs
broly scan -f json # JSON output
broly scan -f sarif -o results.sarif # SARIF for GitHub Code Scanning
broly scan --min-severity high # only high and critical
broly scan --sca --offline # skip OSV API lookup
# Scan config
broly scan --config .broly.yaml # load project config file
broly scan --baseline .broly-baseline.yaml # suppress known FPs / require specific findings
broly scan --incremental # skip SAST on unchanged files (uses .broly-cache.json)
broly scan --quiet # suppress progress output
Config file
[!TIP]
.broly.yaml is loaded automatically from the repo root. CLI flags always override it. See .broly.yaml for a working example.
min_severity: low
exclude_paths:
- vendor
- .git
workers: 8
Baseline
[!NOTE]
suppress silences known false positives. require asserts specific findings must be detected every scan; missing entries cause a non-zero exit. See .broly-baseline.yaml for a working example.
suppress:
- fingerprint: "abc123..." # silence accepted risk / known FP
reason: "test fixture"
require:
- rule_id: "SQL-INJECTION"
file: "api/handlers.py"
description: "SQL injection in user lookup - must be detected"
Developer Feedback Loop
[!TIP]
Check a box in the PR comment to mark a finding as a false positive. Broly commits the suppression to .broly-baseline.yaml automatically. The finding never surfaces again.
When Broly scans a PR it posts a Mark as False Positive section at the bottom of the comment:
### Mark as False Positive
- [ ] 🔴 CRITICAL · SQL injection in get_user() · api/handlers.py:7
- [ ] 🔴 CRITICAL · OS command injection in run_job() · api/handlers.py:11
Check a box to suppress a finding. Broly will auto-update .broly-baseline.yaml on the next commit.
Check a box: a GitHub Action fires, verifies you have write access, and commits:
# .broly-baseline.yaml
suppress:
- fingerprint: "a1b2c3d4..."
reason: "marked false positive by @shasheen on PR #5"
The next scan reads the baseline and the finding is gone. The suppression accumulates over time; each repo builds its own false positive memory.
Inline suppression
Add a comment on the finding line or the line above:
query = "SELECT * FROM users WHERE id = " + user_id # broly:ignore
query = f"SELECT * FROM users WHERE id = {user_id}" # broly:ignore SQL-INJECTION
Output Results
AI Triage - verdict and fix per finding
Add --ai-triage to any scan. Each finding gets a TRUE/FALSE positive verdict, a confidence score, and a concrete code fix. Add --explain for a one-sentence attack scenario:
▸ SAST (2 findings)
SEVERITY ISSUE FILE DESCRIPTION
──────────────────────────────────────────────────────────────────────────────────────────────────
CRITICAL SQL injection via unsanitize.. api/handlers.py:10 SQL injection via unsaniti..
🔺 TRUE_POSITIVE [HIGH] User input flows directly into raw SQL query without parameterization
An attacker sends id=1 OR 1=1 to dump the entire users table.
fix:
query = "SELECT * FROM users WHERE id = %s"
cursor.execute(query, (user_id,))
HIGH Path traversal in read_file api/handlers.py:20 Path traversal in read_fil..
🟢 FALSE_POSITIVE [HIGH] File path is validated against an allowlist before use
In PR comments, verdicts appear as a column in the findings table and confirmed true positives get a collapsible fix block.
SAST - AI-powered code analysis
broly vdev - scanning api/handlers.py
scanners: sast | workers: 8
╔══════════════════════════════════════════════════════╗
║ ║
║ ⚡ BROLY Berserker Vulnerability Scanner ║
║ Secrets · SCA · SAST · Powered by Together AI ║
║ ║
╚══════════════════════════════════════════════════════╝
▸ SAST (4 findings)
SEVERITY ISSUE FILE DESCRIPTION
──────────────────────────────────────────────────────────────────────────────────────────────────
CRITICAL SQL injection via unsanitize.. api/handlers.py:10 SQL injection via unsaniti..
CRITICAL OS command injection via uns.. api/handlers.py:15 OS command injection via u..
HIGH Path traversal in read_file api/handlers.py:20 Path traversal in read_fil..
HIGH Insecure deserialization via.. api/handlers.py:25 Insecure deserialization v..
╔══════════════════════════════════════════════════════╗
║ ║
║ 4 total findings ║
║ Critical 2 High 2 Medium 0 Low 0 ║
║ duration: 9.592s ║
║ ║
╚══════════════════════════════════════════════════════╝
The SAST engine sends each file directly to Qwen/Qwen3-Coder-Next-FP8 with a structured security prompt. No rule files. No rule engine. No YAML. The model traces data flow from source to sink, infers CVSS scores, and pinpoints exact line numbers, finding what static rules miss.
Secrets - with AI false positive filtering
Without --ai-filter-secrets (raw regex hits):
▸ SECRETS (3 findings)
SEVERITY RULE FILE REDACTED
──────────────────────────────────────────────────────────────────────────────────
HIGH AWS API Key config/example.py:6 AKIA****MPLE
HIGH AWS API Credentials config/example.py:6 AKIA****KEY"
HIGH GitHub Personal Access Token config/example.py:9 ghp_****8B4a
With --ai-filter-secrets (AI reads surrounding context):
✔ No findings detected. Clean scan!
The AI recognized the file contained documented placeholder values (EXAMPLE in variable names, "Test / dummy values" comment) and filtered them all as false positives, reducing noise to zero.
SCA - dependency vulnerability scan
broly vdev - scanning /path/to/project
scanners: sca | workers: 8
▸ SCA (13 findings)
SEVERITY VULN ID PACKAGE VERSION FIXED ECOSYSTEM
──────────────────────────────────────────────────────────────────────────────────────────────────
MEDIUM GHSA-9hjg-9r4m-mvj7 requests 2.31.0 no fix PyPI
MEDIUM GHSA-496j-2rq6-j6cc grpcio 1.54.0 no fix PyPI
MEDIUM GHSA-cfgp-2977-2fmm grpcio 1.54.0 no fix PyPI
MEDIUM GHSA-wh2j-26j7-9728 google-cloud-ai 1.25.0 no fix PyPI
MEDIUM GHSA-7gcm-g887-7qv7 protobuf 3.20.3 no fix PyPI
...
╔══════════════════════════════════════════════════════╗
║ ║
║ 13 total findings ║
║ Critical 0 High 0 Medium 13 Low 0 ║
║ duration: 388ms ║
║ ║
╚══════════════════════════════════════════════════════╝
Add --ai-sca-reachability to check whether the vulnerable functions are actually called in your code. Unreachable findings are automatically downgraded one severity level and tagged [Unreachable].
What Gets Scanned
Secrets - 487 rules across:
AWS, GitHub, OpenAI, Anthropic, GCP, Azure, Cloudflare, Slack, Stripe, Twilio,
SendGrid, Docker, npm, SSH/PGP/RSA/EC keys, database URIs, JWTs, generic tokens
SCA - 19 ecosystems, 50+ lockfile formats:
Go, Python, JavaScript, Ruby, Rust, Java, PHP, .NET, Dart, C/C++, Haskell,
Elixir, Erlang, R, Swift, Lua, Nim, OCaml, Julia
SAST - AI analysis across 18 languages. No rule files. No rule engine. No maintenance:
Go, Python, JavaScript, TypeScript, Java, Ruby, PHP, C#, Rust, C, C++,
Kotlin, Swift, Bash, and more
| Format |
Flag |
Use case |
| Table (default) |
-f table |
Terminal, human review |
| JSON |
-f json |
CI pipelines, tooling |
| SARIF 2.1.0 |
-f sarif |
GitHub Code Scanning |
Acknowledgments
License
MIT