What It Does
| Scanner |
Engine |
AI Layer |
| Secrets |
Titus · 487 rules · Hyperscan |
--ai-filter-secrets eliminates false positives |
| SCA |
osv-scalibr + osv.dev · 20 ecosystems |
--ai-sca-reachability checks if the vuln is actually called · --package-intelligence detects hallucinated/non-existent dependencies |
| SAST |
Together AI · Qwen/Qwen3-Coder-Next-FP8 + regex pre-filter |
Slice-aware multi-file analysis · source-to-sink data flow · 17 deterministic patterns · priority scoring |
| Dockerfile |
AI-powered · Dockerfile, Containerfile, Compose |
Privilege escalation, secret exposure, dangerous mounts |
| Container |
go-containerregistry + osv.dev · Alpine, Debian, Ubuntu, RHEL |
OS package CVEs with layer attribution |
| License |
File-based detection · 13 license types |
Policy engine: allowed_licenses / denied_licenses in .broly.yaml |
| SBOM |
osv-scalibr · 20 ecosystems |
broly sbom generates CycloneDX 1.5 or SPDX 2.3 with PURLs |
Install
Go install:
go install github.com/Shasheen8/Broly/cmd/broly@latest
Pre-built binaries (Linux/macOS) — download from Releases.
From source (full Hyperscan support for secrets engine):
brew install vectorscan # macOS
make build
SAST and AI features require a Together AI API key:
export TOGETHER_API_KEY=your_key_here
Usage
broly scan # all scanners, 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 enhancements
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 --package-intelligence # detect hallucinated/non-existent packages
broly scan --ai-triage # verdict (TP/FP) + fix suggestion per finding
broly scan --ai-triage --explain # + one-sentence attack scenario per finding
# Container scanning
broly scan --container alpine:3.19 # pull and scan a registry image
broly scan --container ./image.tar # scan from a local tarball
# Output
broly scan -f json # JSON output
broly scan -f sarif -o results.sarif # SARIF 2.1.0 for GitHub Code Scanning
broly scan --min-severity high # only high and critical
# SBOM
broly sbom # CycloneDX 1.5 to stdout
broly sbom -f spdx -o sbom.json # SPDX 2.3 to file
# Config and suppression
broly scan --config .broly.yaml # project config; also activates license policy
broly scan --baseline .broly-baseline.yaml # suppress known FPs / require specific findings
broly scan --incremental # skip unchanged files
Scanner Output
Demo videos coming soon.
Each scanner outputs an aligned table in the terminal. Supports JSON (-f json), SARIF (-f sarif), and table (default).
AI Triage
--ai-triage adds a verdict, confidence score, and fix suggestion to every finding. --explain adds a one-sentence attack scenario:
CRITICAL SQL injection via unsanitize.. api/handlers.py:10 User input flows directly ..
🔺 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
CI Integration
GitHub App — install once on your org, scans every PR automatically. No per-repo setup needed.
Reusable workflow — drop one line into any repo's existing CI:
jobs:
security:
uses: Shasheen8/Broly/.github/workflows/broly-scan.yml@main
secrets:
ai_api_key: ${{ secrets.AI_API_KEY }}
Supports min_severity, scanners, and ai_triage inputs. Posts findings as a PR comment and uploads SARIF to the GitHub Security tab.
GitHub App
One install covers your entire org. Every PR gets scanned automatically — no workflow files, no per-repo setup, no secrets to configure per repo.
On every PR, Broly:
- Runs secrets, SCA, and SAST on changed files only — no historic noise
- Posts findings as an inline check run with file:line annotations
- Labels each finding TRUE/FALSE positive with a confidence score and fix suggestion
- Adds a checkbox per finding — check it to suppress it forever
[!NOTE]
Only findings in files changed by the PR are reported. Broly never flags pre-existing issues on a clean PR.
Developer feedback loop: when a developer checks a false positive box, Broly commits the fingerprint to .broly-baseline.yaml on the branch. The finding is suppressed on every future scan — automatically, no config needed. Each repo builds its own false positive memory over time.
Running locally
APP_ID=your_app_id \
PRIVATE_KEY_PATH=./broly.pem \
WEBHOOK_SECRET=your_webhook_secret \
TOGETHER_API_KEY=your_key \
go run ./cmd/broly-app
[!TIP]
Use smee.io to proxy GitHub webhooks to your local server during development.
Deployment
Multi-stage Dockerfile at cmd/broly-app/Dockerfile. Chainguard hardened runtime — non-root, no shell, minimal attack surface.
Environment variables:
| Variable |
Required |
Description |
APP_ID |
✓ |
GitHub App ID |
PRIVATE_KEY_PATH |
✓ |
Path to the .pem private key file |
WEBHOOK_SECRET |
✓ |
Webhook secret from the GitHub App settings |
TOGETHER_API_KEY |
— |
Together AI key — enables SAST and AI triage |
PORT |
— |
HTTP port (default: 8080) |
MAX_CONCURRENT_SCANS |
— |
Parallel scan limit (default: 4) |
docker build -f cmd/broly-app/Dockerfile -t broly-app .
docker run -p 8080:8080 \
-e APP_ID=your_app_id \
-e PRIVATE_KEY_PATH=/secrets/broly.pem \
-e WEBHOOK_SECRET=your_webhook_secret \
-e TOGETHER_API_KEY=your_key \
-v /path/to/broly.pem:/secrets/broly.pem:ro \
broly-app
[!TIP]
In production, mount the private key from a secrets manager rather than the host filesystem. The /healthz endpoint is available for uptime monitoring.
[!WARNING]
Never commit the .pem private key to source control. Add it to .gitignore.
Configuration
Config file
[!TIP]
.broly.yaml is loaded automatically from the repo root. CLI flags always override it.
min_severity: low
exclude_paths:
- vendor
- .git
workers: 8
# License policy (findings only emitted when configured)
allowed_licenses:
- MIT
- Apache-2.0
- BSD-2-Clause
- BSD-3-Clause
- ISC
denied_licenses:
- GPL-3.0
- AGPL-3.0
Baseline
[!NOTE]
suppress silences known false positives. require asserts specific findings must be detected every scan — missing entries cause a non-zero exit.
suppress:
- fingerprint: "abc123..."
reason: "test fixture"
require:
- rule_id: "SQL-INJECTION"
file: "api/handlers.py"
reason: "SQL injection in user lookup - must be detected"
Inline suppression
query = "SELECT * FROM users WHERE id = " + user_id # broly:ignore
query = f"SELECT * FROM users WHERE id = {user_id}" # broly:ignore SQL-INJECTION
License
MIT. See LICENSE for the full text.