PatchGap tracks 1-day and n-day vulnerabilities in the major browser engines. For every disclosed CVE it locates the fixing commit, extracts the code change, links the reporter’s original bug report and proof-of-concept where the vendor makes it public, and surfaces the patch gap — the window in which a fix is visible in the source repository before the CVE is formally disclosed.

What is tracked

Vendor Fix source Bug report PoC
Chrome (Chromium, V8, ANGLE, PDFium, Skia, Dawn, WebRTC) Chromium Gerrit issues.chromium.org (opened 30–90 days after fix) Reporter attachments, when published
Firefox Mercurial (hg.mozilla.org) Bugzilla (opened after an embargo period) Reporter attachments, when published
WebKit WebKit Subversion/Git — (Apple does not publish reports) —

The goal is to cover as many years as the vendor’s public record allows, not just the current cycle.

How a CVE maps to a commit

  1. CVE → bug id. Chrome CVEs are read from the Chrome Releases blog, which lists each fixed CVE with its tracker id and reward. Firefox uses the MFSA advisories.
  2. Bug id → change. A bug:<id> (plus message: and Fixes:) search against the vendor’s code-review system returns every change that references that id — across all sub-repositories and release branches.
  3. Change → the fix. A search returns more than the fix: dependency rolls, reverts, fuzzing/flag plumbing and test-only changes all cite the same bug. PatchGap filters these out and selects the change with the largest non-test source footprint on the main branch. Release-branch changes are recorded separately as backports.
  4. Fallbacks when the main branch has no obvious fix. A single bug: search often isn’t enough, so PatchGap resolves the fix through a series of fallbacks, recording which one succeeded:
    • Backports — a fix hand-applied only to a stable release branch (refs/branch-heads/…, tagged [Merge M###]) still counts.
    • External-host rolls — components with their own code review (Skia, PDFium, Dawn, WebRTC, libaom) are fixed upstream and rolled into the browser; PatchGap follows the roll to the real change.
    • Cross-referenced fixes — when only a regression test lands under a bug and its message points at the actual fix CL (under a sibling bug), that reference is followed.
    • Manual dependency rolls — a roll that itself patches a vendored copy (e.g. an ffmpeg security roll) is treated as the fix; a pure version-bump roll instead links the upstream commit range that contains it.
    • Duplicates — a bug closed as a duplicate is mapped to the fix that landed under its canonical bug id, while keeping the CVE’s own tracker link.
  5. No public fix? When none of the above resolves a change (or the fix is still embargoed), PatchGap records no commit and the CVE page shows a “Fix not yet public” notice rather than attributing the wrong change. Nothing is guessed.

The patch gap

Because a fix usually lands in the public repository before the CVE is disclosed, the interval between the two is a window in which the vulnerability is known to anyone reading commits but not yet to defenders relying on the advisory. PatchGap makes that window explicit per-CVE and in aggregate on the Statistics page.

Field reference

Each CVE page is driven by structured metadata:

  • Severity — the vendor’s own rating (Critical / High / Medium / Low).
  • Component — the affected sub-system, taken from the vendor’s advisory text (e.g. Proxy, V8, ANGLE), not merely the repository the fix landed in.
  • Bug class — the vulnerability type (UAF, OOB write, type confusion, integer overflow, logic/authorization error, …).
  • Fix commit — the located change, its repository and the lines added/removed. A green dot on the home table means a public fix was found; a dash means embargoed / not yet public.
  • CVSS — score and vector where an advisory provides one.
  • Exploited in the wild — flagged from the advisory’s own “an exploit for CVE-… exists in the wild” statement, and independently from CISA’s KEV catalog. Shown as an ITW marker on every table.
  • Root-cause analysis — a prose explanation of the vulnerable code path and the invariant that was violated (see below).
  • Attack path — the analysis distilled into ordered stages (Entry → Controlled input → Trigger → Missing check → Primitive → Impact).
  • Original bug report — the reporter’s own write-up and attachments, linked once the vendor unseals them.

Root-cause analysis methodology

RCA is generated from the actual patch diff and the reporter’s report, never from the CVE description alone. Each analysis identifies the vulnerable code path, the violated invariant, and why the fix closes it, then structures the exploitation into a labelled attack path. An RCA is only shown when a fixing commit has been located — an embargoed CVE gets the “Fix not yet public” notice instead, so nothing on the page is speculative.

Search & filtering

  • Every table (the home page, the per-vendor and per-release pages) sorts by any column and marks, at a glance: ITW (exploited), Fix (a green dot when the fixing commit is public), and RCA (a check when a root-cause analysis is written).

  • The Search page does structured + full-text search across every tracked CVE. Operators are shown as clickable chips and combine with AND:

    Operator Matches
    vendor:chrome Browser engine (chrome / firefox / webkit)
    component:v8 Affected component (substring)
    class:uaf Bug class (substring): UAF, OOB, TypeConfusion…
    severity:critical Vendor severity
    cvss:8+ CVSS ≥ 8 (also cvss:7-9, cvss:9.8)
    status:analyzed Analysis/fix state — also status:mapped, status:embargoed
    analyzed Has a root-cause analysis (alias rca:true)
    mapped:true A fixing commit is located (alias diffed:true)
    embargoed:true No public fix located yet
    itw Exploited in the wild
    kev:true CISA KEV listed
    has_poc:true Ships a regression test / PoC
    month:2026-09 Advisory month (YYYY-MM)
    author:"Name" Credited researcher (quotes required)

    Example: vendor:chrome class:uaf severity:critical status:analyzed. The result table carries a Status column (Analyzed / Mapped / Embargoed). The top-nav vendor links open pre-filtered searches.

Triage methodology — patch-now, watch, noise

A CVSS score tells you how bad a bug is if it goes off. It does not tell you what to patch first — a 9.1 read-only info-leak and an 8.8 renderer type confusion are worlds apart in urgency, and CVSS ranks the leak higher. Worse, the bugs PatchGap cares about most — silent fixes, exploitable commits shipped with no CVE at all — have no score to sort by.

So PatchGap does not sort by CVSS. It assigns every fix an operational tier — what a defender should do about it — computed by a deterministic ruler from the fix itself.

The three tiers

Tier Meaning What it is
🔴 patch-now Drop what you’re doing. A memory-corruption primitive an attacker can reach in a default configuration, or anything with real-world exploitation evidence (in-the-wild / KEV).
🟡 watch Security-relevant, but not an emergency. A real bug that falls short of the patch-now bar — gated behind a flag, a weaker primitive, a read-only leak, a logic/policy issue, or an unproven corruption claim.
⚪ noise Ignore for triage. No security-relevant change: a refactor, an API migration, an assert/DCHECK cleanup.

Two principles

1. CVSS is impact, not priority. The tier is driven by the primitive and its reachability, not by the score. A CVSS 9.1 CRITICAL cross-origin timing side-channel is a read-only information leak — real, worth fixing, but not something you break the release train for. It lands in watch. CVSS enters the ruler only as a small tie-breaker for ordering within a tier, never to set the tier.

2. Mechanism beats the label. A bug’s recorded class (TypeConfusion, LogicError, Race, …) is a hint, not a verdict. A commit labelled LogicError can be an optimizer miscompilation that hands attacker JavaScript a wrong-typed object — a type confusion in everything but name. The ruler’s inputs are always derived from what the diff actually does, never from the stored label. This single discipline is what keeps real corruption bugs from being mis-shelved as low-priority.

The ruler: eight factors, one rule

Every fix is reduced to eight factors, each drawn from a fixed vocabulary. Nothing is free-form — that is the point. A tier assigned by hand drifts; a tier computed from named factors is reproducible and auditable.

Factor Values
primitive corruption · info-leak · logic · dos · ub · none
reachability web · file · network · local · config-gated · none
surface browser · gpu · network · utility · renderer · unknown
preconditions default · flag · uncommon · local
corroboration itw · kev · public-poc · bounty · none
verified CONFIRMED · PLAUSIBLE · WRONG (adversarial re-derivation)
confidence 0.0 – 1.0
cvss the published score, used only for ordering

The decision rule reads top to bottom; the first match wins:

  1. not a security change → noise
  2. exploited in the wild, or on the CISA KEV list → patch-now (real-world evidence overrides any diff-level analysis)
  3. a corruption primitive, attacker-reachable (web/file/network), in the default configuration, and not disproven by verification → patch-now
  4. anything else security-relevant → watch

The patch-now bar in rule 3 is deliberately narrow — corruption and reachable and default. A use-after-free behind a non-default flag is watch, not patch-now, because a defender running the stock build is not exposed. This narrowness is what makes a patch-now label mean something.

Adversarial verification

Every patch-now candidate is re-derived from the diff by a second, independent pass that has not seen the first analysis, returning CONFIRMED, PLAUSIBLE, or WRONG. A WRONG verdict pulls the candidate back down to watch. It cuts both ways: in one recent week it caught an over-stated float-cast-overflow and demoted it, and it confirmed six genuine browser-process use-after-frees.

Validation

The ruler was validated against ten real V8 CVEs spanning all three decision rules — in-the-wild zero-days, ordinary high-severity type confusions, and boundary cases. For each, the eight factors were derived blind (from the diff alone, without sight of the rule or the expected outcome), then classified. All ten matched the ground-truth tier. Three results are worth calling out, because they are the ruler earning its keep:

  • CVE-2025-10890 — CVSS 9.1 CRITICAL, labelled InfoDisclosure. The fix masks the timing of cross-origin message deserialization: a read-only side-channel. Tier: watch. The highest CVSS in the set is not the highest priority.
  • CVE-2025-12429 (LogicError) and CVE-2025-8880 (Race) — both derived to corruption from the diff (a hole-check-elision type confusion and a WebAssembly streaming-decoder TOCTOU→OOB). Tier: patch-now. Trusting the stored label would have mis-shelved both as watch.
  • The four in-the-wild bugs all sorted above the analysis-only ones — including one at only medium CVSS — because real-world exploitation dominates the ranking.

Where the tier lives

The three-tier signal is live today on the weekly silent-fix reports, where every candidate carries its tier and rank. Extending the same ruler across the full CVE corpus — so a disclosed CVE and a silent fix are read on one scale — is in progress.

Caveats

  • Component and bug-class labels follow the vendor’s own taxonomy and advisory wording; where the advisory is terse the label is a best-effort inference from the patch.
  • “Embargoed” means no public fix located, which occasionally reflects a fix that landed under a non-obvious reference rather than a true embargo.
  • Reward figures and reporter credit are shown only as published by the vendor.