Security & Trust

Secure Scanning: SAST, Dependencies, Secrets, DAST

How Versus runs recurring automated security scanning across six categories — SAST, software-composition, secret detection, container images, IaC, and DAST — with Semgrep, Trivy, Gitleaks, Bandit, and OWASP ZAP wired into pre-commit, CI, and a local runner.

Versus is built for financial-sector and enterprise buyers whose procurement teams run formal vendor security assessments — SIG/CAIQ questionnaires, "show us your SAST/DAST evidence," SOC 2. To answer those from a position of strength, the platform's own source is put through a recurring, evidence-producing scanning program: automated checks that run before code is committed, on every push, and on a schedule.

This page describes what that program scans, the tools behind each category, and how those same scanners run at three layers — a developer's laptop, a shared CI pipeline, and a one-command local runner. It is about how Versus secures its own codebase; for scanning your network devices for vulnerabilities, that is a separate device-side capability.


The six scan categories

Every category has a job to do and an open-source tool that does it. The set below is deliberately OSS-first — it is fully sufficient to answer a security questionnaire without a commercial platform.

#CategoryWhat it catchesTool
1SAST (static app security)Injection, authz flaws, unsafe deserialization, crypto misuse in our codeSemgrep (Python/TS/React/Django rulesets) + Bandit (Django/Python)
2SCA / dependenciesKnown CVEs in PyPI / npm / Go dependenciesTrivy + pip-audit + npm audit (+ govulncheck for the Go collector)
3Secret scanningCommitted tokens, keys, and private materialGitleaks (pre-commit and CI, full history)
4Container / imageVulnerable OS packages and app deps in the shipped imageTrivy image
5IaCMisconfig in Dockerfiles, compose, Helm, PackerTrivy config (misconfig scanner)
6DAST (dynamic)Runtime issues on the live app — headers, TLS, auth, injectionOWASP ZAP (baseline scan)

A seventh control sits outside the automated tooling: an independent penetration test, run annually and on major architecture changes, to catch the business-logic and chained flaws that scanners miss — the multi-tenant isolation boundary in particular.


Where the scans run

The same tools run at three points in the software lifecycle, each catching problems earlier than the last.

1. Pre-commit — stop secrets before they reach history

The highest-value hook is the one that runs on git commit, before anything is shared. A leaked token in git history is expensive to purge, so the program catches it at the moment of writing.

Install the hooks once:

pip install pre-commit && pre-commit install

The configured hooks are:

  • Gitleaks — secret detection on staged changes.
  • detect-private-key — blocks committed private keys outright.
  • check-added-large-files — rejects files over 2 MB (a common way blobs and dumps sneak in).
  • Bandit — Django/Python SAST across backend, at medium confidence and above (-ll).
  • Plus end-of-file-fixer and trailing-whitespace hygiene.

2. CI — the shared gate on every push

The recurring program lives in a CI workflow (.github/workflows/security.yml) that runs on push and pull request to the default branch, on a schedule, and on manual dispatch. It requests security-events: write so every tool's findings upload as SARIF — a standardized findings format — to the repository's security view, giving one consolidated, dated evidence trail across all tools.

The workflow is split into parallel jobs, one per category:

JobRunsNotes
secretsGitleaksChecks out full history (fetch-depth: 0) so it scans every commit, not just the diff
sastSemgrep + BanditSemgrep configs: p/python p/javascript p/react p/django p/secrets p/owasp-top-ten; Bandit emits SARIF
dependenciesTrivy (fs) + pip-audit + npm auditTrivy scans vuln,secret,misconfig at CRITICAL,HIGH; pip-audit on backend/requirements.txt; npm audit --omit=dev --audit-level=high
containerTrivy imageBuilds the image, then scans it at CRITICAL,HIGH
dastOWASP ZAP baselineRuns only on the scheduled or manual triggers, against a staging URL

The schedule has two cadences: a nightly SCA re-scan (new CVEs land on dependencies you never touched) and a weekly ZAP baseline against staging. The DAST job is gated to those triggers so a dynamic scan never fires against production on an ordinary code push — its target is a non-prod staging instance.

3. Local runner — the whole suite, one command

You do not need CI to run the program. scripts/security-scan.sh mirrors what CI does, using whatever OSS tools you have installed and skipping the rest with a note:

# scan and fail on Critical/High findings
scripts/security-scan.sh

# scan and write SARIF/JSON reports to ./security-reports without failing
scripts/security-scan.sh --report

It runs Gitleaks, Semgrep, Bandit, pip-audit, npm audit, and Trivy (filesystem and, if a versus:scan image is present, container) in sequence, writes each tool's report into security-reports/, and exits non-zero if anything lands at or above the fail threshold — CRITICAL,HIGH by default, overridable with the FAIL_ON environment variable. This is the same threshold and the same tools as CI, so a developer can reproduce a pipeline result locally before pushing.


Severity, SLAs, and gating

Findings are triaged by CVSS severity, adjusted for real exploitability — a vulnerability in an unreachable path is deprioritized. Each severity carries a published remediation SLA, which is itself an answer to a vendor assessment:

SeverityFix SLAGate
Critical7 daysBlocks release; hotfix if in production
High30 daysBlocks release of the affected area
Medium90 daysTracked and batched
Low / infoBest effortBacklog

New Critical/High findings are meant to block a merge or deploy unless an explicit, time-boxed, signed-off exception is filed in an accepted-risk register (with an owner and an expiry — assessors always ask how you handle findings you don't fix). The local runner enforces the threshold today by exiting non-zero; the CI jobs currently surface every finding as SARIF for review and dated retention, with hard-fail gating enabled as the branch's required-check policy is turned on.


What ships, and what gets shared

Raw scan output is never shared with customers — a list of your findings is an attacker's to-do list. On request, buyers receive a one-page Application Security Summary attesting to the secure-SDLC practice (automated scanning on every change plus scheduled re-scans, the six-category coverage, the remediation SLAs, and the annual pentest), with the pentest letter and SOC 2 report available under NDA.

The scanning program also reaches the shipped artifact, not just source. The on-premise image (Dockerfile.onprem) ships sourceless .pyc with no build toolchain, and its base image and OS packages are scanned by Trivy — so the same coverage that protects the cloud service protects a self-hosted appliance. See the On-Premise Hardening Guide.


How it fits the platform

Secure scanning is one control family in a broader posture. It supplies the secure-SDLC evidence that SOC 2's vulnerability-management criterion (CC7.1) asks for, sitting alongside the platform's runtime controls — mandatory MFA, per-tenant Postgres row-level security, envelope-encrypted secrets, and a hash-chained audit trail — described in Security & Compliance and Platform Architecture.

Because the tooling is OSS and runs at every layer from pre-commit to CI to a local script, the same guarantees hold whether a change is made by a Versus engineer or reproduced by a reviewer on their own machine — the scan is part of writing the code, not a step bolted on afterward.


Related