Suppression is how a team says “we know, and we have decided not to act on this now”. It is the one action in TRUSTIVAN whose misuse is invisible by design: it makes a real, confirmed vulnerability stop appearing in the backlog, in the severity counts, and eventually in the gate that blocks a release. Everything about its shape follows from that.

Suppressing a finding

The suppression also records who applied it and when. An acceptance nobody owns is not an acceptance.

The reason is mandatory

It is not paperwork. It is the entire audit value of the action, and it is what the person reviewing this in a year has instead of the person who made the decision. A suppression with no reason is refused with a 400 naming the missing reason specifically, rather than a bare “invalid transition” that gives a user nothing to act on.

Expiry is optional, and bounded at both ends

An explicit expiry must be at least one minute away and at most two years away. The lower bound exists because below it the suppression expires before the user has finished reading the confirmation, which reads as the feature being broken rather than as the input being silly. The upper bound is the interesting one. Without a ceiling, “expires 31 December 9999” is permanent suppression wearing a costume: it satisfies every policy that requires an expiry while guaranteeing that nobody ever revisits the decision. Two years is long enough for a genuine multi-year remediation plan and short enough that the decision outlives neither the person who made it nor the context they made it in.

Permanent suppression is allowed, and usually wrong

The distinction is surfaced in the product rather than enforced, because “this detection is wrong” is a legitimate permanent statement. Refusing it would push users into entering a fake far-future date instead, which is worse: it produces a record that looks like a review date and is not one.

Amending a suppression

suppressed → suppressed is a legal transition. Extending an expiry, shortening it, or restating the justification is a decision, and it writes an event like any other. A request whose terms are byte-for-byte identical to the current suppression is a no-op: it writes nothing, including no event. A retried request must not produce a second suppression event for one decision.

Expiry is a sweep, not a query-time rule

The tempting alternative is to treat status = 'suppressed' AND expires_at < NOW() as open wherever findings are read. It reads well and is wrong in a way that surfaces late: the status column would then disagree with what the product shows, every future query would have to remember the rule, and the finding would silently rejoin the backlog with no event marking when. A state that changes has to change in the table, once, with a record. So a worker sweeps on its own timer, moving due suppressions back to open. The sweep is bounded and skip-locked, so several workers can run it concurrently and a tenant who suppressed a hundred thousand findings on one day cannot produce a single enormous transaction. It runs on the worker’s own schedule rather than on a scan, because an accepted risk must come back when the acceptance ends whether or not anything has been scanned since — and a tenant who suppressed a finding and then stopped scanning is exactly the case where leaving it suppressed is worst.

An expiry is not a change of mind

Two different facts, and only one of them means a human has looked: They are recorded as distinct actions in the timeline for that reason. Only the second means the finding is back in the backlog with no human judgement behind it, and a queue of those is a signal a team needs to see. The expiry event carries no actor at all. Nobody performed it, and a column asserting otherwise would be a small permanent lie. The lapsed reason is copied onto the event, because the finding no longer carries it — without that, the trail would record that an acceptance ended without recording what had been accepted.

It returns to open, not to what it was before

The status a finding held before it was suppressed is not stored, and inventing a column to hold it would encode a claim the history already makes better: the timeline shows exactly what it was and when. open also has the right effect. An expired acceptance belongs back in the triage queue, not filed as already assessed.

Suppression does not buy a green build

By default, suppressing a finding does not make a policy pass. If it did, the fastest route to a green build would be to accept the risk, and TRUSTIVAN would be selling a button that turns off security. A policy’s default warn mode still evaluates suppressed findings: a failing rule that fires only because of suppressed findings is downgraded to a warning, so the build is not blocked by a risk somebody already accepted — but the acceptance stays visible in every verdict that touches it. The downgrade requires that every matching finding be suppressed; one unsuppressed critical alongside ten suppressed ones is still a failure. A team that wants suppression to mean silence has to say so explicitly, in a policy version with an author and a timestamp. See Policies and verdicts.

Who may suppress

The asymmetry is the point. For machine credentials the equivalent scope is findings:suppress, kept separate from findings:write for the same reason. Folding it in would hand every CI key the ability to silence its own failures.

Where to go next