Skip to main content

Suppressing findings

A finding you have reviewed and accepted can be silenced two ways: an in-source comment next to the code, or a repo-wide entry in vulkro.toml. Both keep the reason visible in version control, and a [team_policy] block can require that reason and cap how long a suppression may live.

In-source comments

Add a comment in the source instead of editing a config file. This lives next to the code, so reviewers see why a finding was waived without leaving the file.

Supported shapes:

// vulkro:disable next-line <rule-id>[, <rule-id>, ...]
// vulkro:disable-line <rule-id>[, <rule-id>, ...]
// vulkro:disable-file <rule-id>[, <rule-id>, ...]
// vulkro:disable-file (no rule ID = every rule in the file)
/* vulkro:disable next-line <rule-id> */ (block-comment form)
<!-- vulkro:disable next-line <rule-id> --> (XML / HTML)

Examples:

# vulkro:disable next-line API2
@router.get("/health")
def health(): ...
// vulkro:disable next-line cors-wildcard
app.use(cors({ origin: "*" }));

/* vulkro:disable next-line API1 */
db.query(`SELECT * FROM ${table}`);

The <rule-id> is the kebab-case detector signal (for example cors-wildcard) or an OWASP API code (for example API1). Comma- or whitespace-separated lists are accepted; the wildcard * suppresses every rule on the target line. Matching is case-insensitive.

Two guardrails keep the directive from misfiring:

  • A directive whose text appears inside a string literal is ignored, so security tests can embed the syntax as test data without disabling rules.
  • The directive must be the first non-whitespace content after the comment marker, so narrative doc-comment mentions (for example * The vulkro:disable-line annotation hides X) are ignored.

When findings are suppressed the CLI prints a one-line summary so you can audit them:

Note: N finding(s) suppressed by // vulkro:disable annotations

To confirm what would otherwise be hidden, run with suppressions off: VULKRO_NO_SUPPRESS=1 disables the filter for the engine and the LSP / MCP consumers.

Repo-wide suppressions in vulkro.toml

Suppressions that should apply across the repo live in vulkro.toml instead of the source:

[[suppress]]
rule = "API2" # OWASP category prefix or message substring
path = "tests/**" # glob matched against the finding's file
reason = "test fixtures"
expires = "2026-12-31" # optional YYYY-MM-DD review deadline

expires gives a suppression a review deadline. Once the date has passed the entry stops matching, the findings it hid are reported again, and the scan prints a one-line stderr warning naming each expired entry so it gets re-reviewed (update the date) or removed. An unparseable expires value keeps the entry active (a typo must not silently unhide a triaged finding) and is warned about so the date gets fixed.

Team policy (org-wide enforcement)

A [team_policy] section in the repo-committed vulkro.toml declares requirements the scanner enforces for everyone who runs it in that repo, on any machine and any license tier:

[team_policy]
require_suppression_reason = true # every [[suppress]] needs a reason
require_suppression_expiry = true # ... and an expires date
max_suppression_days = 90 # expiry at most 90 days out

[team_policy.quality_gate] # locked gate floors
max_critical_findings = 0
fail_on_new_high = true

Semantics:

  • A [[suppress]] entry that violates the policy is not applied. The scan prints a loud [team_policy] suppression IGNORED (...) warning naming the entry and the exact fix, and the finding stays visible.
  • [team_policy.quality_gate] thresholds are merged with the user's [quality_gate] and the stricter value wins per threshold. When floors are configured, the gate is evaluated on every scan in the repo even without --gate; a breach exits 1 like any findings failure.
  • There is no way to quietly loosen the policy: it lives in version control, so any change is a visible vulkro.toml diff that goes through code review. VULKRO_NO_SUPPRESS=1 still works and disables suppressions entirely, which is stricter than any policy, never weaker.