All integrations

Bandit Integration with DefectDojo

Bandit Integration with DefectDojo

Bandit is an open source static analysis tool that looks for common security issues in Python code. It was started by the OpenStack Security Project and is now maintained by the Python Code Quality Authority (PyCQA). Bandit parses each Python file into an abstract syntax tree and runs a set of plugins against it, flagging patterns such as hardcoded passwords, use of insecure hash functions, unsafe deserialization, shell invocation with untrusted input, and disabled TLS verification. It writes results in several formats, including the JSON report that DefectDojo imports.

Bandit Integration with DefectDojo

We run Bandit on every Python repository because it is fast, needs no build step, and catches the kind of mistakes that slip through code review. Sending Bandit results into DefectDojo is what turns a list of warnings into tracked work. Each issue becomes a Finding on the Asset that owns the repository, tied to a file and line, deduplicated against earlier scans, and measured against the same SLAs as our other scanners. When a developer fixes the code, the next reimport closes the Finding without anyone editing a spreadsheet.

Why Bandit Matters

Python shows up everywhere in a modern stack: web services, data pipelines, automation scripts, and internal tools that never get a formal review. Bandit covers that code cheaply.

  • It runs in seconds on most repositories and only needs the source tree, so it fits in pre-commit hooks and CI jobs without slowing anyone down.
  • Each result carries a test ID (for example B105 or B602) with a link to Bandit's own explanation, which gives developers context without a separate lookup.
  • Bandit reports both a severity and a confidence for every issue, which helps separate likely problems from pattern matches that need a human look.
  • On its own, Bandit has no history. It cannot tell you whether a warning is new in this pull request or has been sitting in the codebase for two years.

Advantages of This Integration

What we gained by routing Bandit output through DefectDojo:

  • Deduplication at the code location. DefectDojo hashes Bandit findings on file path, line, and the tool's rule identifier, so rescanning an unchanged repository does not create duplicates.
  • A lifecycle for each issue. Reimporting into the same Test mitigates findings that disappeared from the report, adds new ones, and reactivates any that returned.
  • Confidence carried through. Bandit's High, Medium, and Low confidence is stored on the Finding, so triage can start with the issues Bandit is most sure about.
  • SLAs and ownership. Python findings get the same severity-based SLA clocks, assignment, notes, and Jira push as the rest of the program.
  • False positive handling that sticks. When a finding is marked false positive in DefectDojo, deduplication keeps it from reappearing as new on the next scan of the same code.
  • Context next to other tools. Bandit results sit beside SCA and container findings for the same Asset, so an application's risk report covers its own code and its dependencies.

How This Integration Works

DefectDojo imports Bandit results with the Bandit Scan scan type, which reads Bandit's JSON report format.

1. Produce a JSON report. Run Bandit recursively over the source tree with the JSON formatter:

bandit -r ./src -f json -o bandit-report.json

The parser expects the standard Bandit JSON structure, including the generated_at timestamp and the results array. Each result needs issue_text, issue_severity, issue_confidence, filename, line_number, test_id, and test_name. A report produced by the JSON formatter has all of these.

2. Import it. In the UI, open the Engagement, choose Import Scan Results, select Bandit Scan, and upload the file. For automation, use the API, which is available in Community Edition and DefectDojo Pro:

curl "https://YOUR_INSTANCE/api/v2/import-scan/" \
  -H "Authorization: Token $DD_API_TOKEN" \
  -F "scan_type=Bandit Scan" \
  -F "file=@bandit-report.json" \
  -F "product_name=billing-service" \
  -F "engagement_name=CI" \
  -F "auto_create_context=true"

DefectDojo Pro users can run the same import from a pipeline with Universal Importer:

universal-importer import \
  --defectdojo-url "https://YOUR_INSTANCE.cloud.defectdojo.com/" \
  --scan-type "Bandit Scan" \
  --report-path "./bandit-report.json" \
  --product-name "billing-service" \
  --engagement-name "CI" \
  --auto-create-context

3. Reimport on each run. For a repository you scan on every commit or nightly, send later reports to /api/v2/reimport-scan/ against the same Test. Fixed issues are mitigated and the Test keeps a single history for the branch.

Data Granularity: What Gets Imported

DefectDojo Field Source in Bandit Report Notes
Title issue_text Bandit's short description of the issue
Severity issue_severity LOW, MEDIUM, HIGH become Low, Medium, High. Bandit never produces Critical
Description test_name, test_id, filename, line_number, issue_confidence, code Built as labeled lines, with the code snippet in a code block
Scanner Confidence issue_confidence High, Medium, Low stored as DefectDojo's numeric confidence (2, 3, 6)
References more_info Link to the Bandit documentation page for the plugin
File Path filename Path as Bandit reported it, relative to where it ran
Line line_number First line of the flagged code
Vuln ID from Tool test_name and test_id Joined as test_name:test_id, for example assert_used:B101
Date generated_at The report timestamp, applied to every finding
Finding type Static All Bandit findings are marked static
Deduplication Hashcode File path, line, vuln ID from tool

The parser does not set CWE or a separate mitigation field. Remediation guidance is reached through the References link to Bandit's plugin documentation.

Use Cases

In a pull request pipeline: A CI job runs Bandit on every merge request and reimports into a Test for the main branch after merge. Developers see new findings tied to their change, and a gate can query DefectDojo for new High findings instead of failing on every legacy warning.

Across a Python monorepo: A platform team with dozens of services in one repository runs Bandit per service directory and imports each into its own Asset. Findings then roll up to the team that owns the service rather than one shared pile.

For internal scripts and automation: Ops and data teams often write Python that never gets a security review. Scanning those repositories on a schedule and importing into DefectDojo gives the security team visibility into code that would otherwise go unchecked.

During an audit: Because each Finding keeps its discovery date, status changes, and closure, the team can show how long static analysis issues stayed open without rebuilding the history from CI logs.

Operational Tips

  • Point Bandit at the same root directory every time. File paths are part of the dedupe hash, so scanning from a different working directory makes every finding look new.
  • Expect some churn when code moves. Line number is a dedupe field, so a refactor that shifts a flagged call by a few lines can close one finding and open another for the same issue.
  • Exclude test directories with Bandit's own options (for example -x ./tests) before import. Test code triggers many assert_used results that rarely need tracking.
  • Since Bandit tops out at High, review your SLA settings so High Bandit findings get a realistic deadline, and consider raising specific rule IDs during triage when they warrant it.
  • Use minimum_severity=Medium on import if Low findings would bury the team, then lower it once the backlog is under control.
  • Filter on Scanner Confidence during triage. A High severity, Low confidence result is often worth a quick look before assigning it to a developer.
  • Tag imports with the branch or service name (for example tags=main,billing) so findings can be filtered in metrics.