The approach
How AllowGate decides
AllowGate gives the same answer every time, for the same input. The same decision your auditor will see is the one your CI pipeline blocks on. This page walks through how that decision is produced.
Section 1
The question
Every scanner answers the same question: "is this CVE present?" That is rarely the question security teams need to answer.
AllowGate answers a different one: "can this CVE realistically harm this service, in this environment, with this exposure, given this team's policy?" The framing matters. A CVSS 9.8 in a service that is on no network, behind no listener, with no reachable code path, is not the same risk as the same CVE on an internet-facing payments API. Triage that flattens those two cases drowns the team and misleads the audit.
Every decision AllowGate emits names a specific service, a specific artifact, a specific policy revision, and a specific environment. That is the unit of evaluation. There is no global "is this CVE bad?"; there is only "is this CVE acceptable here, right now."
Same input. Same output. Always.
Section 2
Two-phase evaluation
Evaluation runs in two distinct phases. Keeping them separate is what lets the decision be both flexible (per-service, per-tenant, per-tag) and auditable (one effective policy per CVE, fixed precedence).
Phase 1 is policy resolution. Every rule whose scope matches the service, environment, project, and tags is collected, validated, and merged into a single effective policy. Rules are non-hierarchical. A rule applies only when all of its declared scope fields match; nothing is inherited implicitly.
Phase 2 is CVE evaluation. The raw CVSS is risk-adjusted by the context that was captured at scan time (exposure, reachability, runtime privileges, mitigations, temporal signals) into an adjusted severity. That adjusted severity is then evaluated against the effective policy from Phase 1, using a fixed precedence chain. The output is one of allow, deny, undetermined, or filtered (when a trusted VEX statement says the CVE does not apply to this artifact).
The separation is load-bearing. Phase 1 is pure policy logic and does not depend on any specific CVE. Phase 2 is the only place contextual signals enter the decision. If a future signal type is added, it slots into Phase 2 without disturbing the rule composition logic.
Section 3
Decision precedence
The Phase 2 precedence chain is fixed and ordered. It is not a scoring function and not a soft majority. The first matching step wins and short-circuits the rest.
- Step 1 - Global Hard Deny. Platform-level absolute blocks: critical severity on an internet-facing service, KEV-listed vulnerabilities, high severity with reachable code. These cannot be overridden, not even by an executive-level acknowledgment.
- Step 2 - Deny Override. An explicit allow, scoped to a service or artifact, with an attached risk acknowledgment. Bypasses scoped denies. Does not bypass Step 1.
- Step 3 - Scoped Deny. A rule that says: this specific service, tag, or CVE pattern is denied here, full stop.
- Step 4 - Threshold. If the adjusted severity is at or below the effective threshold for this scope, allow. The threshold is the most-specific applicable base threshold, capped by the tenant's elected tier.
- Step 5 - Threshold Override. Bypasses the threshold check (and only the threshold check) for a specific CVE or pattern, with an attached rationale and expiry. Cannot bypass any deny step.
- Step 6 - Undetermined. If no step has resolved the decision, emit undetermined. The tenant's environment policy then resolves it. The mapping is configured per environment by the tenant, not hard-coded by AllowGate; a common configuration is production resolves undetermined to deny and development resolves undetermined to allow.
Undetermined is a signal back to the team, not a verdict. It means no rule applies to this CVE in this scope, so the engine is deferring to the environment default. The actionable response is to update the policy (add a scoped deny, raise the threshold), to add a scoped exception with the right acknowledgment level, or to supply the missing context (reachability evidence, exposure tag) and rerun. The engine will never silently allow a CVE it cannot reason about; it tells you so you can decide.
Worked example. The payments-api service in production has a base threshold of 5.0 (payments handles card data), with an explicit scoped deny pinning CVE-2024-51736 because reachability analysis is not yet complete. A scan finds CVE-2024-51736 (CVSS 9.8) on that service. The engine walks the chain: no system rule fires at Step 1; no deny override applies at Step 2; at Step 3 the scoped deny matches and the chain short-circuits.
{
"cve_id": "CVE-2024-51736",
"service": "payments-api",
"environment": "prod",
"adjusted_score": 9.8,
"decision": "deny",
"decision_confidence": "high",
"winning_rule": {
"type": "scoped_deny",
"scope": { "service": "payments-api" }
},
"justification": [
"step1: no system rule or global_hard_deny matched",
"step2: no exception or deny_override matched",
"step3: scoped_deny matched (service=payments-api) -> deny"
]
}
Section 4
Confidence-aware risk
Every contextual signal carries a confidence level. An auto-derived exposure tag from the runtime manifest is high confidence. A static heuristic from a manifest scan at build time is medium. A manual operator assertion is low. Confidence is derived from the source, not asserted by the rule author.
Modifiers come in two classes. Hard modifiers (exposure, evidence-anchored reachability, sandbox mitigation, usage_context, exploit-intel above the tier threshold) shift the adjusted severity directly. Soft modifiers (PoC availability, WAF mitigation, temporal age, residual heuristics) ride a clamped contribution. The clamp is supplied by the tenant's elected tier, not by individual rules.
Two protections matter here. First, soft modifiers can never pull the adjusted severity below the severity floor for its raw class; the only way to authorise a CVE above the floor is an explicit deny override. Second, if any contributing signal was low confidence and removing it would have flipped the decision, the engine emits undetermined rather than silently allowing. Environment policy then decides what to do.
The upshot: a low-confidence signal can never quietly turn a deny into an allow. It either does not move the needle, or it forces the decision into the open.
Section 5
Exceptions, properly constrained
Exception fatigue is real. Today, most teams track exceptions in a spreadsheet of CVEs that someone, once, agreed were "fine." Three years later no one remembers why, and the auditor wants to know.
AllowGate replaces that spreadsheet. Exceptions are first-class policy objects, not comments in a ticket. Every exception must name a CVE, must be narrowly scoped (service or artifact id, never just a tag), must carry an expiry, and must carry a risk acknowledgment at a level commensurate with the raw CVSS of the underlying CVE: team for below 7.0, security for 7.0 to 9.0, executive for 9.0 and above. The level is enforced at policy load.
Expired exceptions are excluded from evaluation. No grace period. To make the pending regression visible before the gate flips, AllowGate emits a next_actions hint on any decision that would have differed had the expired exception still applied. The team sees the change coming, weeks before the audit does.
exceptions:
- cve: CVE-2024-51736
scope:
service: payments-api
expires_at: 2026-06-01
risk_acknowledgment:
level: security
by: ops@example.com
reason: vulnerable code path is unreachable from public traffic
Section 6
Output: a justification chain
Every decision is paired with a justification chain. This is not a log line. It is a structured record that names the winning rule, the rules that matched but were overridden, the severity before and after risk adjustment, the policy and tenant revisions that produced the decision, the snapshot of the CVE feed and the KEV catalog that were in force, the context signature, and a list of recommended next actions.
This is the evidence reviewers ask for. SOC 2 CC7.1 expects documented vulnerability evaluation criteria and treatment decisions. ISO 27001 A.8.8 expects a traceable management of technical vulnerabilities. PCI DSS 6.3 expects ranked findings and applied corrective actions. DORA's RTS on ICT risk management expects identifiable, traceable risk decisions. The justification chain answers all four without requiring a separate evidence pipeline.
{
"decision": "deny",
"decision_confidence": "high",
"winning_rule": {
"type": "scoped_deny",
"scope": { "service": "payments-api" }
},
"matched_rules": [
{ "type": "threshold", "scope": { "service": "payments-api" }, "value": 5.0 },
{ "type": "threshold_override", "scope": { "service": "payments-api" } },
{ "type": "scoped_deny", "scope": { "service": "payments-api" } }
],
"overridden_rules": [],
"justification": [
"severity: raw=9.8, sum_hard=0, sum_soft=0, adjusted=9.8",
"floor: floor=7 not applied (pre_floor=9.8)",
"step3: scoped_deny matched -> deny"
],
"next_actions": [
"exception_invalid_ack_level: required=executive, actual=security"
]
}
Section 7
Determinism
Same input, same output, always. This is not a slogan; it is a property the engine enforces, and the property auditors test for.
To make replay possible, every decision pins three independent revisions: the platform-layer policy commit, the tenant-layer policy commit, and the parameter envelope (the tier revision) that supplied the thresholds and confidence weights. It also pins the CVE feed digest, the KEV catalog snapshot, and the full evaluation context (including the signing identity of the scanner that produced it). A replay months later, given the same three revisions and the same snapshots, must produce the same decision.
Context is snapshotted at decision time, not re-derived later. Exposure tags, runtime privileges, and reachability evidence are captured as data on the decision record. A service that changes from internal to internet-facing tomorrow does not retroactively change yesterday's decisions, and a CISA KEV addition does not retroactively flip decisions made before the update landed.
A decision that cannot be reproduced is not a decision. It is an opinion that happened to be in the audit log.
Section 8
Delivered as a multi-tenant SaaS
AllowGate is a hosted service. The engine, the system rule pack, and the tier definitions are run and curated by us; your team is a tenant on the platform.
Rules live in two layers. The platform layer (system rules, global hard denies, tier definitions) is owned by AllowGate and applies across all tenants by design. The tenant layer (your base thresholds, scoped denies, exceptions, tier election) is owned by your team and carries your tenant id on every rule. The engine enforces write isolation at the policy boundary: no tenant can author a rule that fires for another tenant, no tenant can author a global hard deny, and no tenant can reclassify a modifier to escape its tier's soft cap.
Tiers are AllowGate-curated parameter envelopes - the max-threshold cap, the soft-modifier cap, the confidence weights, the severity floor. You elect a tier; you do not define one. This is what makes a regulated tier actually regulated, and what lets a platform improvement (a new system rule, a tightened cap) reach tenant at once without each team rewriting its policy.
Want to see this on your stack?
If CVE triage is part of your week, we'd like to hear from you. Email only. We respond within 1 week.
Selecting 15-30 security leaders for design conversations.