Repository scans
Repository Security scans your source code for code flaws (SAST), vulnerable, malicious and look-alike dependencies (SCA), leaked secrets in the working tree and in Git history, infrastructure-as-code, Dockerfile and CI workflow mistakes, and builds a software bill of materials (SBOM) with licences. It is included in every plan.
Register a repository
Accepted remotes are HTTPS URLs on:
| Host | Notes |
|---|---|
| github.com | Always |
gitlab.com, bitbucket.org, dev.azure.com, *.visualstudio.com | Supported |
| A self-managed GitLab host | On request: contact support to have your host allowed |
Examples: https://github.com/acme/payments, https://gitlab.com/acme/payments.git.
Private repositories. Give a read-only token for the clone. It is used once and never stored, unless you choose to save it in the Secret Vault (Settings → Secret Vault, encrypted, revocable). The API field is called github_pat but it carries the clone token for any host (GitHub fine-grained token with Contents: read, GitLab read_repository, Bitbucket app password with repository read, Azure DevOps PAT with Code: read). With the GitHub App installed you need no token at all.
The token never reaches a scanner: a separate service clones the repository, and scanners run on the checkout with no network and no credential.
Run a scan from the web app
- Open Repository Security, paste the URL and a branch, tag or commit.
- Choose Detect stack & build scan plan. DVSentry clones the repository, detects technologies (with the files that prove each one) and lists every scanner as applies, not applicable, unavailable or unsupported.
- Pick a mode:
- Smart: the recommended scanners for the detected stack.
- Full: every applicable scanner.
- Custom: tick capabilities yourself. With nothing ticked there is nothing to run, and DVSentry says so rather than inventing a default set.
- Choose the Git history depth for the history secret scan (below), the environment (development, staging, production), optional scan paths for a monorepo, the time limit per scanner, and a report name.
- Choose Run. The report is created at once in state
preparingwhile the clone happens in the background; you can leave the page.
What the progress page shows
The scan page refreshes itself and shows, for each scanner: queued, running (time used out of its limit), finished with a finding count, skipped with the reason, stopped (timed out) or failed with the reason. It also shows the codebase size (files and bytes), the scanner running now, what starts next and your place in the queue. Cancel scan asks for confirmation in place and stops queued and running scanners; a stopped scanner records cancelled.
Scanners
Every scanner is an open-source engine pinned by version in the signed scanner image (scanner-manifest.yaml), plus DVSentry's own rule packs. Nothing is installed or downloaded during a scan.
| Scanner id | Engine | Finds | In Smart when |
|---|---|---|---|
trivy-filesystem | Trivy | Vulnerable dependencies and IaC/Kubernetes/Dockerfile misconfigurations | Always |
semgrep-local | Semgrep OSS with DVSentry and open-licence rules | Code flaws in many languages (shown as "semgrep") | Always |
gitleaks | Gitleaks with DVSentry's rule file | Secrets in the working tree (type and location, never the value) | Always |
gitleaks-history | Gitleaks over Git history | Secrets committed and later deleted | Always |
osv-dependencies | osv-scanner | Vulnerable dependencies from OSV, including malicious packages (MAL-) | A package ecosystem is found |
grype-vulnerabilities | Grype | A second dependency engine with its own database | A package ecosystem is found |
syft-sbom | Syft | The SBOM (components and licences) | A package ecosystem is found |
govulncheck | govulncheck (offline) | Go vulnerabilities with call reachability; vendored or dependency-free modules only | Go |
bandit-python | Bandit | Python security issues | Python |
gosec-go | gosec | Go security issues | Go |
cppcheck-c-cpp, flawfinder-c-cpp | Cppcheck, Flawfinder | C/C++ flaws | C/C++ |
hadolint-dockerfile | Hadolint | Dockerfile mistakes | Dockerfiles |
actionlint-workflows | actionlint | GitHub Actions workflow mistakes | .github/workflows |
zizmor-workflows | zizmor | GitHub Actions security: actions not pinned to a commit, pull_request_target misuse, script injection from PR data, broad permissions:, kept checkout credentials, cache poisoning | .github/workflows or action.yml |
kube-linter | KubeLinter | Kubernetes manifest risks | Kubernetes or Helm |
checkov-iac | Checkov | Terraform, Kubernetes, Helm misconfiguration (second IaC engine); GitLab CI, Bitbucket, Azure Pipelines and CircleCI pipeline checks | Terraform, Kubernetes, Helm, or one of those pipeline files |
Jenkinsfiles are read by semgrep-local with DVSentry's own rules (rules/semgrep/ci/): shell steps built from build parameters or pull request data, and scripts piped from curl/wget into a shell. Secrets in any pipeline file are Gitleaks'. | openapi-security | DVSentry | Risky OpenAPI definitions | An OpenAPI document | | license-policy | DVSentry | Licences your policy blocks | Licence metadata | | ai-bom | DVSentry | AI bill of materials: SDKs, provider hosts, model names, credential names | Always (repository root) | | dvsentry-review | DVSentry (Python ast) | Cross-file Python flows and route authentication heuristics (missing-auth, admin-route-unauthenticated, possible-idor, each "needs human confirmation") | Python, Node.js or Java |
GET /scanner-capabilities (scope org:read) lists the scanners DVSentry can run right now.
Notes:
- Semgrep runs only rules shipped in the image (DVSentry's framework rules under
rules/semgrep/framework/, owned taint rules, and MIT/LGPL GitLab SAST rules). It never fetches registry rules. It skips vendored, generated, minified and test paths, works in batches of 1,000 files, and keeps finished batches if it runs out of time (the report then lists the files it did not reach and the slowest rules). - Secrets are rated
high,mediumorlowconfidence. Generic matches in test, fixture or example paths, template files or with placeholder names are lowered (never dropped);scanner_severitykeeps the original rating. - Dependency severities come from the advisory (CVSS vector, GHSA or distro rating); a missing rating is
UNKNOWN, never a default HIGH. Each dependency finding carries CISA KEV status and EPSS likelihood. - Reachability: dependency findings say whether your code imports the vulnerable package (
packagelevel) or calls a known vulnerable function (functionlevel), and where that knowledge came from.
Git history secret scan (gitleaks-history)
Secrets that were committed and later deleted are invisible to a working-tree scan. The history scan fetches part of the branch's history and searches it.
- Depth choice. On the form, DVSentry counts the branch's commits first and offers 100 %, 75 %, 50 % or 25 % of them, each with the number of commits and an estimated time (about 90 commits a minute). The default is 25 %. The depth is never below 5,000 commits (or the whole history if shorter) and never above 50,000. The API field is
history_percent(25, 50, 75 or 100). - Fast first. The other scanners start on a shallow clone at once; the history job waits (status
fetching) for its deeper clone, then queues. - Incremental history. After a finished history scan of a branch, the next scan of the same branch fetches only the new commits (plus a safety margin) and carries forward the earlier findings by fingerprint (
metadata.carried_from). It falls back to a full history search when the old commit is no longer an ancestor (history rewritten), the scanner version changed, the last full search is older than 30 days, or you chose a larger share than last time. The form shows the previous search and whether the next one will be incremental. - A history cut short by the depth is reported as coverage-bounded, never as "no secrets in history".
Full and incremental scans
scan_strategy is full (default) or incremental.
- Full scans everything selected.
- Incremental compares the commit with the repository's baseline report and runs only the scanners that the changed files can affect. Dependency scanners run whenever a manifest, lockfile, Dockerfile, vendored tree or
.tffile changed. Findings from the baseline in unchanged areas are carried forward. The GitHub App uses incremental scans for pushes and pull requests.
Set the baseline with PUT /repositories/{id}/baseline or from the repository page.
Time limits and the queue
- Time limit per scanner. The form's default is 30 minutes. A large repository is not a failure: every scanner gets at least 30 minutes, the SBOM 30 min, the history scan 1 hour and Semgrep 4 hours (up to 12 hours). The limit only catches a hang.
- Timed out is its own state, never
failedorcompleted; a timed-out scanner keeps the findings it already produced and the report says what it did not reach. - Queue. DVSentry runs a fixed number of scanner jobs at once. Scanners of different reports take turns, fast scanners go before heavy ones (history, Semgrep, SBOM), and a heavy scanner runs alone. The report page shows how many jobs are ahead and an estimated start.
Scan statuses
| Status | Meaning |
|---|---|
preparing | The clone is in progress |
queued | Waiting for a free scan slot |
fetching | The history scan is waiting for its deeper clone |
running | A scanner is running |
completed | Every planned scanner finished |
partial | Some scanners did not complete; the coverage section says which and why |
timed_out, failed, cancelled | The scan, or a scanner, stopped; the reason is shown |
Example: run a scan through the API
export DVSENTRY_URL=https://app.dvsentry.com
export DVSENTRY_TOKEN=dvs_pat_... # scopes: repositories:write, scans:trigger, scans:read
# 1. Register the repository once and keep its id.
curl -sS -X POST "$DVSENTRY_URL/v1/repositories" \
-H "Authorization: Bearer $DVSENTRY_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"payments","remote_url":"https://github.com/acme/payments","git_ref":"main"}'
# -> {"id":"3f0c...","source_type":"remote", ...}
# 2. Start a scan.
curl -sS -X POST "$DVSENTRY_URL/v1/security-reports" \
-H "Authorization: Bearer $DVSENTRY_TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: build-1234" \
-d '{
"repository_id": "3f0c...",
"name": "payments main",
"environment": "production",
"scanners": ["trivy-filesystem","semgrep-local","gitleaks","osv-dependencies","syft-sbom"],
"git_ref": "main",
"scan_strategy": "full",
"history_percent": 25,
"timeout_seconds": 1800
}'
# -> 202 {"id":"9a1e...","status":"preparing", ...}
# 3. Poll until it finishes, then read the policy result.
curl -sS "$DVSENTRY_URL/v1/security-reports/9a1e..." -H "Authorization: Bearer $DVSENTRY_TOKEN"
curl -sS "$DVSENTRY_URL/v1/security-reports/9a1e.../ci-result" -H "Authorization: Bearer $DVSENTRY_TOKEN"
For a private repository add "github_pat": "<read-only token>" (any host), or "organization_secret_id": "<Secret Vault entry id>". The CLI does steps 2 and 3 for you. A request field scan_profile (smart or full, letting the server pick the scanners) is planned and not yet on main; until then list the scanners explicitly.
Scheduled scans
On the repository page, add a schedule: an interval (15 minutes to 30 days), the scanners, branch, paths and strategy (incremental by default). POST /repository-schedules/{id}/run runs one now. A schedule claims its occurrence before cloning, so a slow clone never runs twice. Scheduled scans of private repositories need a Secret Vault credential or the GitHub App.
Monorepos
Stack detection finds independent build units (each with its own manifest or lockfile) and recommends scanners per unit. The whole repository is scanned unless you type scan paths yourself; up to 20 paths are accepted (scan_paths).
Container images
Under Repository Security → Container images, scan an image in a registry by tag or digest:
| Registry | Access |
|---|---|
| Amazon ECR | Through a connected AWS cloud account an Admin opted in (role-based accounts only) |
| Google Artifact Registry / GCR | Through a connected Google Cloud account an Admin opted in |
| Azure Container Registry | Through a connected Azure account an Admin opted in |
| GitHub Container Registry | Through the GitHub App installation |
DVSentry brokers a short-lived, narrowed token, pulls the image by digest into a verified layout (every blob hashed) and runs Trivy on it offline. Language components join the daily SBOM re-check.
After the scan: always-on checks
- Daily SBOM re-check. Stored SBOMs are re-checked against newly published OSV advisories without a new scan. A match raises an
osv_offlinealert on the report. - Malicious and look-alike packages. A dependency listed as malicious (
MAL-advisories) raises a critical alert; a name one typo away from a popular npm or PyPI package raises a medium look-alike alert. SeeGET /security-reports/{id}/supply-chain-alerts. - Licence compliance. Each SBOM component is placed in a family (permissive, weak/strong/network copyleft, restricted, unknown). Policies can block a family. See
GET /security-reports/{id}/license-compliance. - Advisory matches. New CISA KEV entries and runtime advisories are matched against your stored inventory every hour and shown as a workspace banner.
Free public scan
/scan scans a public github.com repository with no account: secrets, dependencies, code and IaC, with a summary kept 7 days at a private link (secrets as a count only). A visitor gets a few scans per hour and one scan per repository per day.
Scan locally before you push
The CLI local command scans a folder on your machine with the scanners installed there, offline, and uploads nothing:
curl -fsSo dvsentry.py https://app.dvsentry.com/downloads/dvsentry.py
python3 dvsentry.py local . # gitleaks + trivy (+ semgrep with your own rules)
python3 dvsentry.py install-hook # run a staged-secrets check before every commit
What DVSentry never does here
It never modifies your repository during a scan, never passes your token to a scanner, never fetches rules or databases during a scan, and never treats a scanner that did not run as "no findings".
Something missing or unclear? Tell us with Feedback in the app.