Static Analysis Tools Overview: Bandit, Bundler‑Audit, FindSecBugs & AuditJS
Static application security testing (SAST) is a cornerstone of any DevSecOps pipeline. In the Practical DevSecOps training you’ll encounter four widely‑used open‑source scanners: Bandit, Bundler‑Audit, FindSecBugs, and AuditJS. This article explains why some tools require extra prerequisites, how to handle false‑positives, filter results by severity, improve scan commands, and troubleshoot common errors such as exit‑code handling and ignore‑file formatting.
1. Bundler‑Audit – Why Ruby Is Needed on Your Local Machine but Not in GitLab
1.1 Installation methods matter
| Method | How it works | When you need Ruby | Typical use case |
|---|---|---|---|
| Docker (GitLab CI) | The tool runs inside a pre‑built container that already contains Ruby, Bundler, and the bundler-audit gem. |
No – the container isolates the runtime. | Fast, reproducible CI pipelines; no host‑level dependencies. |
| Native (local or self‑hosted CI) | You install the gem directly with gem install bundler-audit. |
Yes – the host must have a compatible Ruby interpreter and the gem command. |
Quick local testing, custom environments, or when Docker is not an option. |
1.2 Practical tip
- CI/CD (GitLab/GitHub): Use the official Docker image
ruby:2.7-bundler-audit(or a custom image) and invokebundler-auditinside the job script. - Local development: Install Ruby (via rbenv, rvm, or your package manager) then run
gem install bundler-audit.
By leveraging Docker, the pipeline avoids “Ruby not found” errors and guarantees the same version of the scanner across all runs.
2. Bandit – Handling False Positives in the Baseline File
2.1 What is a baseline file?
A baseline (.bandit-baseline.json) stores findings from a previous scan. During a new scan, Bandit compares current results against the baseline and flags any new issues.
2.2 Why your changes seem ignored
- All baseline entries are still displayed – Bandit shows every issue that exists in the baseline, regardless of whether you edited the source file.
- Only one entry needs to be marked as false‑positive – As soon as a single issue in the baseline is flagged with
"false_positive": true, the overall scan will display a green tick.
2.3 Step‑by‑step fix
- Open the baseline snippet:
https://gitlab.practical-devsecops.training/-/snippets/15. - Locate the issue you want to suppress and set
"false_positive": true. - Commit the updated baseline file.
- Re‑run
bandit -r . -c .bandit-baseline.json.
Now the scan will treat that entry as ignored, and the pipeline will pass.
3. Bandit – Failing a Build Only on High‑Severity Findings
Bandit provides a simple flag to limit the exit status to a chosen severity level.
# Fail the job only when HIGH or CRITICAL issues are found
bandit -r . -lll # -lll = low, low, low → only high & above trigger non‑zero exit
| Flag | Meaning |
|---|---|
-l |
Show low severity only (does not affect exit code). |
-ll |
Show low + medium. |
-lll |
Show low + medium + high (default). |
-x |
Exclude files or directories. |
You can also combine with --exit-zero to always succeed and rely on a separate script to parse the JSON output for high‑severity findings.
4. FindSecBugs – Aligning the Scan with DevSecOps Best Practices
4.1 Current command (example)
findsecbugs -output findsecbugs-report.xml -progress -low -medium -high .
4.2 Recommendations for a production‑grade pipeline
- Limit output to actionable severities – Drop low‑severity findings to reduce noise.
findsecbugs -output findsecbugs-report.xml -medium -high . - Use a machine‑readable format – XML (or SARIF) integrates easily with CI dashboards, DefectDojo, or GitLab Security Reports.
- Fail the job on critical findings – Add
-failOnHigh(or parse the XML after the scan).
4.3 Sample improved command
findsecbugs -output findsecbugs-report.sarif \
-medium -high \
-failOnHigh \
-progress .
This command produces a SARIF file that can be uploaded to GitLab/GitHub security dashboards and aborts the pipeline if any high‑severity issue is detected.
5. AuditJS – Ignoring Specific Vulnerabilities
AuditJS reads an ignore file (auditjs-ignore.json) that must be valid JSON. A common mistake is using trailing commas or comments, which break the parser.
5.1 Correct ignore‑file structure
{
"ignore": [
{
"module": "lodash",
"version": "4.17.15",
"reason": "Patched in downstream library"
},
{
"module": "express",
"version": "4.16.0",
"reason": "False positive – not used in production"
}
]
}
5.2 How to use it
auditjs scan . --ignore-file auditjs-ignore.json
If you still see errors, run jq . auditjs-ignore.json to validate the JSON syntax.
6. Bandit Exit Codes – Why Piping Changes the Result
- Without piping:
bandit -r . -f jsonreturns exit code 1 when any issue (of any severity) is found. - With piping:
bandit -r . -f json | tee bandit-output.jsonreturns exit code 0 because the pipeline’s final command (tee) succeeds, masking Bandit’s original status.
6.1 Preserve Bandit’s exit code
bandit -r . -f json | tee bandit-output.json
# Capture Bandit’s status in $PIPESTATUS (bash)
if [ ${PIPESTATUS[0]} -ne 0 ]; then
echo "Bandit detected vulnerabilities"
exit 1
fi
Or use set -o pipefail at the start of the script to propagate the first non‑zero status.
7. Quick Reference & Tips
| Topic | Command / Tip |
|---|---|
| Run Bundler‑Audit in CI | docker run --rm -v $(pwd):/app -w /app ruby:2.7 bundler-audit check --update |
| Mark Bandit false‑positive | Edit baseline JSON → "false_positive": true |
| Fail on high Bandit issues only | bandit -r . -lll |
| FindSecBugs high‑severity only | findsecbugs -output report.sarif -medium -high -failOnHigh . |
| Validate AuditJS ignore file | jq . auditjs-ignore.json |
| Preserve exit code with pipe | set -o pipefail or check ${PIPESTATUS[0]} |
Final Thought
Integrating these static analysis tools with the right installation method, output handling, and error‑checking logic turns a simple scan into a robust DevSecOps control. By following the patterns above, you’ll keep pipelines fast, results reproducible, and security findings actionable. Happy scanning!