DVSentry Documentation API referenceOpen the app

Policies, exceptions and release gates

A policy turns findings into a pass/fail answer. That answer drives the CI result, the GitHub pull request check and the release gate. Without a policy there is no policy-based protection: the CI result passes with no policies evaluated and the report's Policy tab shows the policy gate as Not set.

Policies

Open Governance → Policies (Admin) and create a policy. Fields:

FieldValuesMeaning
nametextShown in results
repository_ida repository or emptyEmpty = every asset
source_typerepository, web, cloud, ai, resilience or emptyLimit the policy to one pillar; empty = all pillars
environmentall, development, staging, productionWhich reports it applies to
severity_thresholdNONE, INFO, LOW, MEDIUM, HIGH, CRITICALFail when any active finding is at or above this
block_severitieslistFail on exactly these severities
license_allow_list, license_block_listSPDX ids, upper-caseFail on a blocked licence, or on any licence outside the allow list. The form offers "block a family" buttons (for example all strong copyleft)
posture_rulesobject, belowOrganization posture a release must meet

Example: block criticals and highs in production and GPL licences everywhere (Admin, web session):

POST /policies
{
  "name": "Production release bar",
  "environment": "production",
  "severity_threshold": "HIGH",
  "license_block_list": ["GPL-2.0-ONLY", "GPL-3.0-ONLY", "AGPL-3.0-ONLY"]
}

Only active findings count: open and reopened ones. Fixed findings and findings under an approved exception do not.

Posture rules

A policy for no single repository may also carry posture_rules, read across the whole organization:

"posture_rules": {
  "cloud_severity_threshold": "CRITICAL",   // fail while any cloud account's newest report has an open CRITICAL
  "block_cloud_drift": true,                // fail while a cloud account drifted from its baseline
  "block_open_kev": true,                   // fail while any asset has an open CISA KEV issue
  "resilience": "no_failure",               // off | no_failure | proven
  "resilience_gameday_days": 0              // N: a fully passed GameDay within N days
}

Posture rules apply only where a release is gated (the CI result, the GitHub check and the release gate), never to a frozen report's own policy answer.

Exceptions

Accepting a risk or suppressing a finding is a governed decision, not a deletion. On a finding choose Request exception:

POST /security-reports/{report_id}/exceptions
{
  "finding_key": "<the finding's dedupe key>",
  "decision": "accepted_risk",                 // or "suppressed"
  "owner": "payments-team",
  "justification": "Only reachable from the admin VPN; fix planned for Q1 with the auth rewrite.",
  "compensating_controls": ["VPN-only access", "WAF rule 9031"],
  "expires_at": "2027-01-31T00:00:00Z"
}

A different Admin or Owner approves or rejects it (approved, rejected, later revoked) with a note. The approval history is kept. An expired, rejected or revoked exception stops suppressing the finding at once.

The CI result

GET /v1/security-reports/{id}/ci-result (scope gates:read) is what the CLI and CI pipelines read:

{
  "report_id": "9a1e...",
  "conclusion": "fail",           // pass | fail | pending | error
  "passed": false,
  "exit_code": 1,                 // 0 pass, 1 fail, 2 pending or error
  "summary": "FAIL: 2 policy rule(s) were violated.",
  "policy": {
    "outcome": "failed",
    "evaluated_policy_ids": ["..."],
    "violations": [
      {"policy_name": "Production release bar", "rule_type": "severity_threshold",
       "reason": "3 active finding(s) meet or exceed the HIGH threshold.",
       "matching_values": ["CRITICAL", "HIGH"], "finding_ids": ["..."]}
    ]
  }
}

A report that is still running answers pending; one that ended in any state other than completed answers error. A scan that assessed nothing never passes silently. With a repository baseline set, findings already present in the baseline are ignored for the gate (baseline_ignored says how many), so a pull request is judged on what it introduced.

The GitHub pull request check

With the GitHub App, each pull request gets a DVSentry security scan check: success (no policy violations), failure (violations in findings new since the baseline, with up to 50 line annotations) or neutral (some scanners did not finish, or nothing was assessed).

Release gates

A release gate compares a candidate report with an approved baseline report of the same asset.

  1. Mark a known-good report as a release baseline: the report's Release Gate tab, or POST /security-reports/{id}/release-baseline.
  2. Evaluate a newer report against it: Evaluate on the tab, or POST /v1/security-reports/{candidate_id}/release-gate/evaluate with {"baseline_report_id": "..."} (scope gates:evaluate).
  3. Read the recommendation:
RecommendationWhen
BlockNew or resurfaced CRITICAL or HIGH findings, a blocked licence, a new high-risk attack path, the threat model regressed to critical, or the policy gate failed
ConditionalNew or resurfaced MEDIUM findings, unapproved licences, or unexpected new SBOM packages
PassNone of the above

A Conditional release can be approved or rejected once, with a justification, by an Admin or Owner other than the person who asked for the evaluation (POST /release-gates/{id}/approval). The decision re-signs the evidence. Download it as GET /v1/release-gates/{id}/evidence.json (Ed25519 signed) or evidence.md.

If no baseline exists yet, the tab says "No prior release baselines found" and how to create one, instead of showing a blank panel.

Regression versus release gate

Remediation SLAs

Open issues carry a due date by severity: CRITICAL 1 day, HIGH 7 days, MEDIUM 30 days, LOW 90 days (app/services/remediation_sla.py). The risk inbox and scorecards use them to show what is overdue.

Something missing or unclear? Tell us with Feedback in the app.