Skip to content

Priorities and deadlines

MopokeHQ matches published security advisories against the packages and runtimes in every SBOM your pipelines upload (see Scanners). Each match gets a priority, a deadline and a confidence, and lands on the Now list. This page explains how each is decided.

Matching covers npm and Composer (Packagist) packages, and the PHP and Node.js runtimes. Packages from other ecosystems appear in the SBOM but are not matched against advisories yet.

A few words used throughout:

  • An advisory is a published security notice: a CVE, a GitHub or OSV advisory, a malicious package report, or a php.net or Node.js security release. Each advisory that matches carries its own priority and deadline.
  • An exposure is one place an advisory matches: a repository, at a version, on a branch or in a deployed environment.
  • An action is one fix on the Now list, such as “Upgrade axios to 1.8.2”. It holds every advisory that fix closes, across every repository that needs it.
Signal Source Used for
Severity The advisory’s own rating, or its CVSS 3 base score when it has none (9.0 and up critical, 7.0 high, 4.0 medium, below that low). A malicious package is always critical. P1 and P2
Known exploited CISA Known Exploited Vulnerabilities (KEV) catalogue P1
Likelihood of exploitation FIRST EPSS: the probability a CVE is exploited in the next 30 days P2
Malicious package OSV malicious package reports P1
Runtime security release php.net and Node.js security releases P1
Confidence Where the vulnerable version is seen: Confirmed live in a deployed environment, Likely live on the default branch, or Unconfirmed on another branch P1, and the order of the list

MopokeHQ re-matches every stored SBOM whenever a new advisory lands, so a CVE published today is matched against last month’s build without a rescan. Priorities are recomputed after every KEV and EPSS update: when CISA adds a CVE to KEV, every open match of it becomes P1 within hours.

The rule is deterministic: the same signals always give the same priority, and every advisory lists the reasons for its priority.

Priority When Deadline Who hears
P1 Any of: known exploited (in CISA KEV), a malicious package, a PHP or Node.js runtime security release, or critical severity and confirmed live in a deployed environment 48 hours Instant email, then escalation after 4 hours if nobody acknowledges it
P2 Critical or high severity, or an EPSS score of 0.1 (10%) or more 14 days Daily digest
P3 Everything else (“No exploitation signal”) 30 days Weekly digest

The reasons shown with each advisory read like “Exploited in the wild (CISA KEV)”, “Critical and verified live”, “Severity high” or “EPSS 34% chance of exploitation within 30 days”.

A priority can rise at any time: a new KEV entry, a higher EPSS score, or a critical exposure confirmed live. The deadline starts when MopokeHQ matched the advisory to your code, or when it moved up to its current priority, whichever is later. A vendor’s release date can be earlier than that; evidence packs say so.

Secrets are ranked by the secrets scanner rather than by advisories: a real, provider-specific secret outside test and example files is critical and goes out as a P1 alert. See Rotating a leaked secret.

Every exposure says how sure MopokeHQ is that the vulnerable version is running.

Confidence Means
Confirmed live Seen in a deployed environment: a deploy report named the commit, or a live server’s runtime check-in reported the version
Likely live On the default branch but not reported deployed, or in an environment whose servers have stopped checking in
Unconfirmed Only on another branch, behind a floating base image tag such as php:8.3-fpm, or read from a website’s response headers

The API and webhooks use the values verified, likely and possible for these.

MopokeHQ learns what is deployed from three places:

  • the mopoke deploy --env <name> step at the end of a deploy job (any CI; see Any CI),
  • GitLab deployment webhooks (see GitLab),
  • runtime check-ins: a live server reporting the PHP or Node.js version it runs.

On Now, “Live only” (on by default) hides actions seen only on branches nobody deploys. A P1 always shows.

MopokeHQ closes an advisory on its own in one case only: the fixed version is seen deployed, reported by a deploy, in every repository it affected. The advisory resolves and the evidence log records that the fix was seen live.

Everything else waits for a person:

  • A fix merged on a branch but not deployed moves the advisory to “Waiting to confirm”. It closes when a deploy shows the fix, or when someone confirms it by hand, which is logged with their name.
  • A fix only a runtime check-in shows also waits. A check-in is a server’s own word about itself. It can raise an alarm (a server saying it runs an old PHP is believed), but it can never close one, so a compromised or misconfigured server, or a leaked runtime token, can only cause false alarms, never hide a real one.
  • A fix only a declared runtime shows (a --runtime flag, or a Dockerfile FROM php:8.3.12 line rather than the version measured from the built image) waits the same way.

A check-in counts for seven days. After that MopokeHQ falls back to what the deploy reported, or keeps the environment in scope as Likely live. A server going quiet never reads as a fix.

If a fixed version later disappears (a rollback, a regression), the advisory opens again as new: it alerts and needs acknowledging again.

Who may confirm a fix: owners and admins, and with teams, a Lead who leads every project the advisory touches.

Email When Goes to
P1 alert As soon as a P1 opens Without teams, owners and admins get every P1, and each project owner gets the ones that touch their projects. With teams, each fix goes to its projects’ owners and the Leads of the teams that have them, each listing only their own projects; owners and admins get what no team answers for.
Escalation A P1 nobody has acknowledged after 4 hours Without teams, everyone in the organisation. With teams, every owner and admin, plus everyone on the teams that have the affected projects (Leads, Developers and project owners), each with their own part. Never the whole organisation.
Daily digest 8am in the organisation’s time zone, when there are new P2 fixes or overdue ones Owners and admins
Weekly digest Mondays at 8am, when there are new P3 fixes or overdue ones Owners and admins

Alerts are grouped by fix: a burst of advisories that one upgrade closes arrives as one email, not one per CVE. Each advisory alerts once.

Acknowledging a P1 says someone is on it and stops the escalation for everyone. Anyone who can see one of its projects can acknowledge it, except a Viewer, from the alert, from Now, through the API or through an AI assistant connected over MCP.

Now is the work screen. It lists actions, not advisories, because that is how the work gets done: one upgrade that closes twelve CVEs across five repositories is one line.

Actions sit in three tiers:

Tier Holds
Now P1s, and anything due within 48 hours
This week P2s, and anything due within 7 days
Later The rest

Inside each tier the order is fixed: priority, then the earliest deadline, then confidence (Confirmed live first), then reach (how many advisories the fix closes), then name. The Overview shows the first five in the same order under “Do these first”.

Filters above the list narrow it by project, ecosystem and priority. Each tier shows its first 8 rows; Later starts folded.

Not every match needs a fix. On an action, Not affected and Accept risk record a decision with a reason. A decided advisory is dismissed: it leaves Now, the alerts, the escalations and the digests, and is listed under “Dismissed” until someone reopens it or the decision stops holding.

Not affected needs a reason from a fixed list, plus optional details. The reasons are OpenVEX justifications, so every decision exports as a VEX statement:

Shown as VEX justification
Component not used component_not_present
Vulnerable code not reachable vulnerable_code_not_in_execute_path
Not exploitable in this configuration vulnerable_code_cannot_be_controlled_by_adversary
Mitigated elsewhere inline_mitigations_already_exist
False positive (version misread) false_positive

Accept risk needs a written reason and a review date, at most a year away.

When an action holds several advisories, the dialog lists them all ticked, so you can leave some open.

Every decision goes into the evidence log with the person, the reason, the priority it was decided at and the repositories exposed at the time. That is what an auditor reads when they ask why a known vulnerability was left in place.

Decision Reopens when
Accepted risk Its review date arrives, or its priority rises above the one that was accepted (a new KEV entry, a higher EPSS score, a critical exposure confirmed live)
Not affected A repository that was not exposed when it was decided becomes exposed to the same advisory
Either Someone clicks “Reopen” under Dismissed

A decision that reopens on its own is treated as new: it alerts and needs acknowledging again, and if its deadline passed while it was dismissed, the clock restarts from the reopen. A person who reopens a decision keeps the original deadline, so dismissing and reopening never buys time. Every reopen is logged with its cause.

  • A P1 always needs an owner or admin.
  • A P2 or P3 needs an owner or admin, or with teams, a Lead who leads every project the advisory touches.
  • Viewers can see decisions but not make them.

When AI is on, Lilly adds a plain note to each advisory and an estimate of whether it applies to you. For a P2 or P3 she is confident does not apply (90% by default), she marks it “Lilly suggests not affected” and keeps it out of the digests. It stays on Now until a person clicks Confirm not affected, which records the decision, or Keep it, which puts it back in the digests and stops her suggesting on it. Lilly never holds back a P1, never dismisses anything herself and never changes a priority.