Skip to content

Security Scanning (SRT)

LMA integrates the AWS Sample Security Review Tool (SRT) for automated security scanning across the codebase, CloudFormation templates, and dependencies. SRT bundles Bandit (Python), Semgrep (SAST), Checkov (IaC), Syft (SBOM), and an AWS-resource security-matrix into a single dashboard with persistent suppression tracking.

Note: SRT is the upstream-renamed successor to DSR (Deliverable Security Review). The one-off import of the legacy .dsr/issues.json decisions has already been done — see Migrating from DSR if you still have that file locally.

Terminal window
make srt # setup → scan → prompt to open dashboard
make srt-setup # download and configure the SRT binary
make srt-scan # run the security assessment
make srt-fix # open the dashboard for interactive triage

After triaging, refresh and commit .srt/suppressions.json to share the review decisions with the team and CI:

Terminal window
make srt-save-suppressions
git add .srt/suppressions.json
git commit -m "chore(security): update SRT review decisions"

SRT is not part of make lint or make test — it runs automatically in GitLab CI on merge requests targeting develop.

make srt-setup downloads the latest SRT binary from GitHub Releases into .srt/. make srt-scan runs srt assess, which:

  1. Identifies CloudFormation / SAM templates and Python source files.
  2. Runs Bandit, Semgrep, Checkov, Syft, and the AWS security-matrix in parallel.
  3. Produces an interactive dashboard at .srt/dashboard.html.
  4. Persists findings in its working file .srt/issues.json with status (Open / suppressed / resolved / reopened).

.srt/issues.json is rewritten by every assessment, so it is generated output and is not committed. The decisions made during triage are kept separately in .srt/suppressions.json, the only tracked file under .srt/.

On a fresh checkout, make srt-scan first runs make srt-seed, which copies .srt/suppressions.json into .srt/issues.json so the scan starts from the reviewed state instead of re-reporting everything already triaged. Without that seed, SRT reports a large number of findings across Bandit (Python), Semgrep (SAST), Checkov (IaC), and the AWS security-matrix.

FileTracked?Role
.srt/suppressions.jsonYesReview decisions — one entry per accepted finding, each with its suppressionReason. Curated during triage.
.srt/issues.jsonNoSRT’s working database. Regenerated by every assessment; seeded from .srt/suppressions.json when absent.

Entries previously marked resolved are not carried in .srt/suppressions.json: a finding that has been fixed in code is not reported by the next scan at all, and if it ever resurfaces it should be reviewed again rather than inheriting an old status.

TargetDescription
make srtFull workflow: setup → scan
make srt-setupDownload and configure SRT (idempotent; pin version with SRT_VERSION=v1.0.2)
make srt-scanRun the assessment (seeds .srt/issues.json first). Non-zero exit in CI on open findings; zero locally
make srt-seedRestore .srt/issues.json from the tracked decisions; no-op if it already exists
make srt-save-suppressionsRefresh .srt/suppressions.json from .srt/issues.json after triage (commit the result)
make srt-fixOpen .srt/dashboard.html for triage; falls back to srt status -a on headless servers
make srt-migrate-dsrOne-shot legacy import: .dsr/issues.json → .srt/issues.json (needs a local .dsr/issues.json; no longer tracked)

SRT tracks each finding’s status in .srt/issues.json:

StatusMeaning
OpenNew finding — needs review
suppressedFalse positive or accepted risk — always include a suppressionReason
resolvedFixed in code — SRT will reopen if the issue resurfaces
reopenedA previously-resolved issue has returned

.srt/suppressions.json is the only file here tracked in git (via a negative-gitignore rule). The binary, .venv, scan output, dashboard, config files and the .srt/issues.json working database are all gitignored.

Writing a suppressionReason: .srt/suppressions.json is committed to a public repository, so every rationale in it is published. Write it as a statement about the code as it stands — the control that is in place, the configuration parameter or deployment parameter that governs it, or the reason the rule does not match this particular call site. Do not write it as an analysis of what would otherwise be reachable, and do not describe impact, preconditions or severity. The same applies to the rationale in # nosec, # noqa and # nosemgrep comments and in cfn-nag / Checkov reason: metadata. Claims about behaviour should be checkable against the code or the template, and references to other documents should name a section that exists.

Triage workflow:

  1. make srt-scan
  2. make srt-fix — opens the dashboard
  3. Filter to HIGH priority + Open
  4. Suppress false positives with a written rationale; fix genuine issues in code
  5. make srt-scan again to confirm Open: 0
  6. make srt-save-suppressions && git add .srt/suppressions.json && git commit

You can also edit .srt/issues.json directly — set status to suppressed or resolved and add a suppressionReason field — then run make srt-save-suppressions to fold the result into the tracked decisions file.

.gitlab-ci.yml defines a security_review stage with a single srt_security_review job that runs only on merge requests targeting develop. It is GitLab-only — the GitHub Code Checks workflow (.github/workflows/code-checks.yml) does not run SRT:

srt_security_review:
stage: security_review
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"
when: on_success
script:
- make srt-setup
- make srt-scan

The job uses tags: [size:large] (4 vCPU / 16 GB RAM) — SRT’s scanner toolchain needs more memory than a default runner. Adjust the tag to match your runner pool.

Artifacts captured on every run (1 week retention):

FileContents
srt-issues.jsonFull issue database
srt-dashboard.htmlInteractive HTML dashboard
srt-bandit-summary.jsonPython static-analysis summary
srt-semgrep-summary.jsonSAST findings summary
srt-syft-summary.jsonSBOM summary
srt-project-summary.mdProject summary

The .dsr/ scanner export is no longer tracked, so this applies only if you still have a local copy. Run the one-shot migration to port existing suppressions:

Terminal window
make srt-migrate-dsr

This reads .dsr/issues.json, drops Open entries (SRT will rediscover them), normalizes suppression_reason → suppressionReason, and writes .srt/issues.json. The .dsr/ directory is not modified.

After migrating, run make srt-scan to verify suppressions match the new findings. If a suppression doesn’t match (e.g., source moved by a few lines), SRT will report it as Open again — re-suppress via the dashboard.

Terminal window
SRT_VERSION=v1.0.2 make srt-setup

Set SRT_VERSION as a GitLab CI/CD project variable for reproducible pipeline scans.

.srt/
├── srt # Binary (gitignored, ~118 MB)
├── srt-cli-v*.tar.gz # Downloaded archive (gitignored)
├── .venv/ # Scanner toolchain (gitignored)
├── srtconfig.json # AWS profile + region (gitignored, regenerated per machine)
├── settings.json # Project ID + last-scan date (gitignored)
├── issues.json # Working database (gitignored, regenerated per scan)
├── suppressions.json # Review decisions ← committed
├── dashboard.html # Interactive dashboard (gitignored)
├── *-summary.json # Per-scanner summaries (gitignored)
└── <stack-name>/ # Per-stack analysis dirs (gitignored)

SRT not found — run make srt-setup.

Config prompts hang in CI — setup.py auto-detects CI via CI, GITLAB_CI, or GITHUB_ACTIONS. If none are set, export CI=1 before running make srt-setup.

Platform not supported — SRT supports Linux x64/arm64 and macOS x64/arm64. Windows is not yet available.

Reconfigure SRT — cd .srt && ./srt config