Broly

module
v1.0.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT

README

Broly

Broly

CLI-first berserker code security scanner.

Secrets · SCA · SAST · Containers · SBOM. AI-powered findings. Run locally or in CI with one binary.

CI Go License Release Together AI


What It Does

Scanner Engine AI Layer
Secrets Titus · 487 rules · Hyperscan locally (Go regex in CI) --ai-filter-secrets reduces false positives
SCA osv-scalibr + osv.dev · 20 ecosystems --ai-sca-reachability · --package-intelligence for hallucinated deps
SAST Together AI · Qwen/Qwen3.5-9B · 17 regex prefilter patterns Slice-aware multi-file analysis · includes Dockerfile, Containerfile, and Compose files when --sast is enabled
Container go-containerregistry + osv.dev Pulls and scans registry, local daemon, or tarball images · auto-discovers base images from Dockerfiles and Compose under scan targets
License File-based detection · 13 license types Only runs when allowed_licenses or denied_licenses is set in .broly.yaml
SBOM osv-scalibr · 20 ecosystems broly sbom · CycloneDX 1.5 or SPDX 2.3 with PURLs

Install

go install github.com/Shasheen8/Broly/cmd/broly@latest

For fastest local secrets scanning, install Vectorscan (Hyperscan). Without it, Broly falls back to the Go regex engine (same rules, slower):

brew install vectorscan                   # macOS
sudo apt-get install -y libhyperscan-dev  # Ubuntu / Debian

The reusable GitHub workflow installs Broly with CGO_ENABLED=0, so CI always uses the Go regex engine.

Or download a pre-built binary from Releases.

SAST and AI features require a Together AI API key:

export TOGETHER_API_KEY=your_key_here

Usage

broly scan                                        # secrets + SCA + SAST (SAST needs TOGETHER_API_KEY)
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 (pulls and analyzes images — full OS package/CVE pass)
broly scan --container alpine:3.19                # explicit image: pull + scan
broly scan --container ./image.tar              # tarball
broly scan .                                      # default scan also walks targets for Dockerfile / Compose and pulls each referenced base image

# 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 SAST files since last run

[!NOTE] GitHub Actions security (zizmor) is not in this repo — no pkg/workflow scanner and broly-app does not run workflow scans either. If enable_workflow: true is in .broly.yaml, broly scan exits with an error (the message mentions broly-app; that path is not implemented here). Use a hosted Broly deployment or another tool for Actions static analysis.


Scanner Output

Video for each scanner output 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 an AI verdict in the terminal table for SAST and SCA findings (secrets and container findings are not triaged in the CLI orchestrator):

  • TRUE_POSITIVE or FALSE_POSITIVE
  • confidence score
  • short reasoning in the ASSESSMENT / CONTEXT column
  • targeted remediation in the TARGETED FIX column, including a concrete code fix when the model has enough local context

--ai-triage --explain adds one more thing: a plain-language attack scenario or impact sentence. The table format stays the same, but each finding becomes more verbose.

Agentic Triage (auto-enabled)

When --ai-triage is used against a local directory (the default . target), high-severity SAST findings are automatically triaged with agentic repo tool use. The AI can read related files, search the repository, and trace cross-file data flow before deciding a verdict — instead of relying only on the visible code snippet.

Three tools are available to the model:

  • repo_file_read — read any file in the repo with optional line range
  • repo_code_search — search repo text (uses git grep when available)
  • repo_find_files — find tracked files by basename pattern

The agent loop runs up to 5 rounds with a maximum of 8 tool executions. Tool results are capped at 16K characters. All file content is redacted for secrets before being returned to the model.

No extra flags are needed — agentic triage activates automatically when:

  1. --ai-triage is enabled
  2. The scan target is a directory (not a single file)
  3. The finding is SAST with severity >= high

If the repo directory can't be opened (e.g., permission error), triage silently falls back to the non-agentic single-prompt mode.

Use:

broly scan . --ai-triage
broly scan . --ai-triage --explain

Rule of thumb:

  • --ai-triage is better for day-to-day developer scans
  • --ai-triage --explain is better for reviews, demos, and cases where you want the exploit path spelled out more clearly

Example difference:

--ai-triage
| Critical | SQL injection in login query | TRUE_POSITIVE [HIGH] | Recommendation: Use a prepared statement. |
|          | Code: $query = "SELECT ..."  | User input is        | Code fix: $stmt = $db->prepare(...);      |
|          |                              | concatenated into a  |                                           |
|          |                              | SQL query.           |                                           |

--ai-triage --explain
| Critical | SQL injection in login query | TRUE_POSITIVE [HIGH] | Recommendation: Use a prepared statement. |
|          | Code: $query = "SELECT ..."  | User input is        | Code fix: $stmt = $db->prepare(...);      |
|          |                              | concatenated into a  |                                           |
|          |                              | SQL query.           |                                           |
|          |                              | An attacker can send |                                           |
|          |                              | ' OR 1=1 -- to       |                                           |
|          |                              | bypass login or dump |                                           |
|          |                              | data.                |                                           |

CI Integration

Run the broly CLI in GitHub Actions with the reusable workflow:

jobs:
  security:
    uses: Shasheen8/Broly/.github/workflows/broly-scan.yml@main
    secrets:
      ai_api_key: ${{ secrets.AI_API_KEY }}

Inputs: min_severity, scanners (all | sast | sca | secrets), and ai_triage. On pull requests it runs secrets + SCA on the full tree (and pulls base images referenced in Dockerfiles/Compose, same as local broly scan), runs SAST on changed code files only when ai_api_key is set, uploads SARIF to the GitHub Security tab, and posts a summary PR comment (findings table only — no fix blocks or false-positive checkboxes). Push/workflow_dispatch runs a full-repo scan with the same scanner selection rules.

Optional: broly-app (local GitHub App)

cmd/broly-app is a webhook server for testing the full PR experience locally. It is not the production hosted service (no DynamoDB, org registry, zizmor, etc.).

On each pull request it:

  • Clones the PR head and runs secrets + SCA on the repo
  • Runs SAST on changed code files only when TOGETHER_API_KEY is set (with AI triage enabled)
  • Posts a check run (summary + file annotations) and a PR comment (severity table, triage verdicts, collapsible fix suggestions, false-positive checkboxes)

Checkboxes in the PR comment are handled by .github/workflows/feedback.yml: a maintainer checks a box, and the workflow commits the fingerprint to .broly-baseline.yaml on the PR branch.

APP_ID=... PRIVATE_KEY_PATH=./broly.pem WEBHOOK_SECRET=... TOGETHER_API_KEY=... \
  go run ./cmd/broly-app

Use smee.io to forward GitHub webhooks to your machine while developing.


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
baseline_file: .broly-baseline.yaml                  # optional: suppress / require rules
path_strip_prefix: /home/runner/work/myrepo/myrepo   # optional: strip clone path from finding paths
additional_suppressions:                             # optional: suppress by fingerprint
  - "abc123..."

# Do not set enable_workflow: true — the CLI does not run zizmor

# 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.

Directories

Path Synopsis
cmd
broly command
broly-app command
broly-app is a GitHub App webhook server that scans PRs and pushes with Broly.
broly-app is a GitHub App webhook server that scans PRs and pushes with Broly.
pkg
ai
Package ai provides a shared Together.ai client for all Broly scanners.
Package ai provides a shared Together.ai client for all Broly scanners.
cache
Package cache provides a file-hash cache for incremental scanning.
Package cache provides a file-hash cache for incremental scanning.
container
Package container scans container images for OS and language package vulnerabilities.
Package container scans container images for OS and language package vulnerabilities.
license
Package license detects software licenses and checks them against a policy.
Package license detects software licenses and checks them against a policy.
sast
Package sast provides AI-powered static application security testing.
Package sast provides AI-powered static application security testing.
sbom
Package sbom generates Software Bill of Materials in CycloneDX and SPDX formats.
Package sbom generates Software Bill of Materials in CycloneDX and SPDX formats.
sca
Package sca adapts osv-scalibr + osv.dev to Broly's core.Scanner interface.
Package sca adapts osv-scalibr + osv.dev to Broly's core.Scanner interface.
secrets
Package secrets adapts Titus's engine to Broly's core.Scanner interface.
Package secrets adapts Titus's engine to Broly's core.Scanner interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL