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.
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.
| # | Category | What it catches | Tool |
|---|---|---|---|
| 1 | SAST (static app security) | Injection, authz flaws, unsafe deserialization, crypto misuse in our code | Semgrep (Python/TS/React/Django rulesets) + Bandit (Django/Python) |
| 2 | SCA / dependencies | Known CVEs in PyPI / npm / Go dependencies | Trivy + pip-audit + npm audit (+ govulncheck for the Go collector) |
| 3 | Secret scanning | Committed tokens, keys, and private material | Gitleaks (pre-commit and CI, full history) |
| 4 | Container / image | Vulnerable OS packages and app deps in the shipped image | Trivy image |
| 5 | IaC | Misconfig in Dockerfiles, compose, Helm, Packer | Trivy config (misconfig scanner) |
| 6 | DAST (dynamic) | Runtime issues on the live app — headers, TLS, auth, injection | OWASP 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.
The same tools run at three points in the software lifecycle, each catching problems earlier than the last.
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:
backend, at medium confidence and above (-ll).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:
| Job | Runs | Notes |
|---|---|---|
secrets | Gitleaks | Checks out full history (fetch-depth: 0) so it scans every commit, not just the diff |
sast | Semgrep + Bandit | Semgrep configs: p/python p/javascript p/react p/django p/secrets p/owasp-top-ten; Bandit emits SARIF |
dependencies | Trivy (fs) + pip-audit + npm audit | Trivy scans vuln,secret,misconfig at CRITICAL,HIGH; pip-audit on backend/requirements.txt; npm audit --omit=dev --audit-level=high |
container | Trivy image | Builds the image, then scans it at CRITICAL,HIGH |
dast | OWASP ZAP baseline | Runs 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.
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.
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:
| Severity | Fix SLA | Gate |
|---|---|---|
| Critical | 7 days | Blocks release; hotfix if in production |
| High | 30 days | Blocks release of the affected area |
| Medium | 90 days | Tracked and batched |
| Low / info | Best effort | Backlog |
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.
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.
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.