Security EngineeringMarch 15, 2026 · 8 min read

Snyk + DORA: How to Track Security Debt as an Engineering Metric

Security debt is invisible in most DORA dashboards. Snyk surfaces CVEs and license violations in your repos and containers, but none of that feeds into your deployment risk score or Change Failure Rate. Koalr changes that — by treating unresolved vulnerabilities as a first-class engineering signal, not a separate security team concern.

Security posture belongs in DORA

NIST SP 800-218 and CISA's Secure Software Development Framework both identify unresolved critical vulnerabilities in production as a leading indicator of security incidents — incidents that are categorized as deployment failures in your Change Failure Rate. Security debt and engineering velocity are not separate problems. They share the same denominator.

Why security posture is a DORA signal

Change Failure Rate (CFR) measures the percentage of deployments that result in a degraded service or require a hotfix. Most engineering teams track CFR against incidents caught by their observability stack — an SLO breach, an on-call page, a P1 ticket. But a material percentage of incidents that show up in your CFR have a security vulnerability at their root, and that vulnerability was detectable before the deployment occurred.

The link between CVE severity and post-deployment incident rate is not hypothetical. CISA's Known Exploited Vulnerabilities catalog documents thousands of CVEs actively exploited in the wild — the majority of which had public fixes available for weeks or months before exploitation. NIST's National Vulnerability Database shows that critical-severity vulnerabilities (CVSS 9.0+) have a median time-to-exploit of 15 days after public disclosure. When your engineering team merges a dependency update that introduces a critical CVE, the clock starts immediately.

The mechanism connecting vulnerability severity to Change Failure Rate is straightforward: an exploited vulnerability triggers an incident, that incident is counted as a deployment failure (either the original deployment that introduced the vulnerable dependency, or the emergency patch deployment that follows), and your CFR goes up. The engineering team experiences this as an incident. The security team experiences it as a CVE. They are the same event.

This is why security posture belongs in your DORA data. Not as a parallel report from a separate security tool, but as a signal that feeds directly into the same metrics your engineering team already tracks. Koalr pulls three specific signals from Snyk and incorporates them into deploy risk scoring and the CFR trend visible in your DORA dashboard.

Snyk's data model

Before explaining what Koalr pulls from Snyk, it is worth being precise about Snyk's data model — because understanding the shape of the data clarifies why these specific signals are useful.

Projects

In Snyk's model, a project is a single monitored artifact: a repository, a container image, an IaC configuration file, or a package manifest (package.json, requirements.txt, go.mod, and so on). Each project has a unique ID, a name, a type (npm, pip, docker, terraform, etc.), and a test frequency. A single GitHub repository often maps to multiple Snyk projects — one per manifest file, plus one per container image if you're scanning images.

The Snyk REST API endpoint for listing projects is:

GET /orgs/{orgId}/projects
Authorization: token {snykApiToken}
Content-Type: application/vnd.api+json

# Response includes:
# data[].id           — project UUID
# data[].attributes.name
# data[].attributes.type    — "npm", "pip", "docker", etc.
# data[].attributes.status  — "active" | "inactive"
# data[].relationships.target.data.id  — maps to GitHub repo

Issues

An issue in Snyk represents an individual vulnerability finding — typically a CVE or a license violation — within a specific project. Each issue has a severity level (critical, high, medium, low), a CVSS score, a fixability status (upgradeable, patchable, or not patchable), and a first-seen timestamp. The severity level exposed via the REST API is the effective_severity_level — Snyk computes this by adjusting the base CVSS score with contextual factors like whether the vulnerable code path is reachable.

The primary issues endpoint:

GET /orgs/{orgId}/issues
Authorization: token {snykApiToken}
Content-Type: application/vnd.api+json

# Key query parameters:
# ?filter[severity][]=critical&filter[severity][]=high
# ?filter[status]=open
# ?limit=100&starting_after={cursor}

# Response shape:
{
  "data": [
    {
      "id": "uuid",
      "type": "issue",
      "attributes": {
        "title": "Remote Code Execution",
        "effective_severity_level": "critical",
        "status": "open",
        "ignored": false,
        "created_at": "2026-02-14T08:12:00Z",
        "updated_at": "2026-03-01T14:33:00Z",
        "problems": [
          {
            "id": "CVE-2025-12345",
            "source": "NVD",
            "type": "vulnerability",
            "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-12345",
            "updated_at": "2026-02-10T00:00:00Z"
          }
        ],
        "coordinates": [
          {
            "remedies": [
              {
                "type": "indirectUpgrade",
                "description": "Upgrade lodash to 4.17.21"
              }
            ]
          }
        ]
      },
      "relationships": {
        "scan_item": {
          "data": { "id": "project-uuid", "type": "project" }
        }
      }
    }
  ],
  "links": { "next": "..." }
}

Dependencies and the vulnerability graph

Snyk resolves your full dependency graph — both direct dependencies declared in your manifest and transitive dependencies pulled in by those packages. Each issue is associated with the dependency path from your root package to the vulnerable package. This distinction matters for Koalr's signal calculation: a critical CVE in a direct dependency is more actionable (one npm install to fix) than the same CVE nested five levels deep in a transitive dependency (may require coordination with an upstream package author). Koalr uses the dependency depth in its vulnerability delta scoring to weight the risk appropriately.

PR checks

When Snyk is connected to GitHub, it creates a GitHub Check Run on each pull request that runs a fresh vulnerability scan of the PR's changed dependencies. If the PR introduces any net-new high or critical CVEs that were not present in the base branch, Snyk can be configured to block the merge. This is the "gate" mechanism — but it is binary (pass/fail) and does not flow into your engineering metrics. Koalr consumes the Check Run result and the underlying diff to compute a continuous signal rather than a binary gate.

The 3 signals Koalr pulls from Snyk

Three signals from the Snyk data model have proven most predictive of deployment-related security incidents. They are computed per PR, not per project snapshot, which is what makes them actionable in the deploy risk workflow.

Signal 1: Vulnerability delta per PR

The vulnerability delta compares the Snyk issue set for the affected packages in the PR head branch against the same packages in the base branch. If the PR introduces net-new CVEs — dependencies added or upgraded in a way that brings in new vulnerabilities — the delta is positive. If the PR resolves CVEs (by upgrading a vulnerable package to a patched version), the delta is negative.

Koalr's scoring uses a severity-weighted formula:

vuln_delta_score =
  (new_critical × 25) +
  (new_high     × 10) +
  (new_medium   ×  3) +
  (new_low      ×  1) -
  (fixed_critical × 20) -
  (fixed_high     ×  8)

# A PR that adds 2 critical CVEs: +50 deploy risk points
# A PR that fixes 3 high CVEs: -24 deploy risk points
# A PR that adds 1 critical and fixes 2 high: +25 - 16 = +9 points

The asymmetry between the introduction penalty and the fix credit is intentional. Introducing a critical CVE is a harder-to-reverse action than fixing one: the vulnerable version may be required by other dependencies, the fix may break API compatibility, or the "fix" may not yet be available for the transitive chain. The scoring reflects this asymmetry.

Signal 2: Unresolved critical count at merge time

Independent of what a specific PR changes, the absolute count of unresolved critical and high-severity issues in the affected packages at merge time is a steady-state signal. This represents the accumulated vulnerability debt that the PR is being deployed on top of. A PR that changes a package with 0 open critical issues is a different risk profile from an identical PR touching a package with 7 open critical issues — even if neither PR introduces or resolves any vulnerabilities itself.

Koalr applies a threshold ladder to this signal:

0 open criticals
Green

No vulnerability debt in affected packages. No additional risk contribution from this signal.

1–2 open criticals
Amber

Elevated background risk. Koalr surfaces the CVE IDs in the deploy risk panel. +8 deploy risk points.

3+ open criticals
Red

Significant accumulated vulnerability debt. Process breakdown signal — see fix lag below. +20 deploy risk points.

Signal 3: Fix lag (days since first seen)

The age of an unresolved critical or high vulnerability is a signal about engineering process, not just technical debt. A critical CVE that was first seen by Snyk yesterday may simply be awaiting triage. The same CVE open for 45 days signals something different: a prioritization failure, a missing owner, or a remediation path that the team has not found.

Koalr uses 30 days as the threshold for process-breakdown classification. A critical CVE open longer than 30 days in any package modified by a PR adds a fix-lag penalty to the deploy risk score, independent of the other two signals. This threshold aligns with CISA's Known Exploited Vulnerabilities remediation timelines, which mandate remediation of KEV-listed vulnerabilities within 15 days for government systems — 30 days is the industry-standard analog for commercial engineering teams.

The fix lag signal is designed to make a specific pattern visible: teams that ship new features continuously while unresolved critical vulnerabilities accumulate in their dependencies. This pattern is common and rarely intentional — it emerges from backlogs where security debt competes for priority against product work and consistently loses. Surfacing it explicitly in the deploy risk score puts it in the same dashboard as velocity metrics, which is where the engineering manager has leverage to change the prioritization.

Setting up Snyk in Koalr

Connecting Snyk to Koalr takes under five minutes. You need a Snyk API token with read access to your organization's issues and projects.

Step 1: Generate a Snyk API token

In Snyk, navigate to Settings → Service accounts. Create a new service account with the "Viewer" role — Koalr only needs read access, never write. Copy the generated API token; you will not be able to view it again after leaving this page.

If your organization uses Snyk Groups (a Group containing multiple Snyk organizations), generate the token at the Group level if you want Koalr to see all child organizations, or at an individual organization level if you want to scope the integration to a single Snyk org.

Step 2: Add the token to Koalr

In Koalr, go to Settings → Integrations → Snyk. Paste your API token and click Connect. Koalr will immediately validate the token against the Snyk API and enumerate the organizations your token has access to.

Step 3: Select your Snyk org

After the token is validated, Koalr presents a dropdown of accessible Snyk organizations. Select the org (or orgs) that contain the projects corresponding to your GitHub repositories. If your Snyk org names do not obviously map to your GitHub org, Koalr performs fuzzy matching against your connected GitHub repos to suggest the most likely mapping.

Step 4: Initial sync

The initial sync fetches all projects and their current issue sets from the Snyk API. For organizations with up to 50 projects, this typically completes in under 2 minutes. Larger organizations (200+ projects) may take 5–10 minutes as Koalr paginates through the issues endpoint. You can monitor sync progress in the Integrations page — a progress indicator shows projects synced vs. total.

Once the sync completes, your Snyk projects appear in Koalr's Coverage page alongside test coverage data. The deploy risk signal activates automatically for all GitHub repositories that have a matching Snyk project. No additional configuration is required — Koalr maps Snyk projects to GitHub repos via the repository target in Snyk's project metadata.

No Snyk plan upgrade required

The Snyk REST API endpoints Koalr uses — /orgs/{orgId}/issues and /orgs/{orgId}/projects — are available on Snyk's Free and Team plans. You do not need Snyk Business or Enterprise for the Koalr integration. Container scanning and IaC scanning results are available if those features are enabled on your Snyk plan.

Reading your Snyk DORA data in Koalr

Once Snyk is connected, vulnerability data surfaces in three places in Koalr.

Coverage page: vulnerability trend per repo

The Coverage page shows a vulnerability count trend chart per repository, broken down by severity. The chart uses the same date range selector as the rest of Koalr — you can compare your open critical count today against 30 or 90 days ago. This makes vulnerability debt directionally visible: is your team reducing its open critical count over time, or is it growing? The trend line is the signal; the absolute number is less meaningful than the direction.

The Coverage page also shows a severity breakdown table — criticals, highs, mediums, and lows — per repository, sortable by severity. This gives your security lead (or whoever owns Snyk triage) a prioritized view of where vulnerability debt is highest across your engineering org.

Deploy Risk panel: per-PR vulnerability score

On every open PR in Koalr's PR list, the deploy risk panel shows a breakdown of the signals contributing to the risk score. When Snyk data is available for the affected packages, the panel displays the vulnerability contribution explicitly:

Deploy Risk Score: 61  ▲ Elevated

Signal breakdown:
  Change size          +12  (847 lines, 3 packages)
  Author expertise     +8   (2 files outside normal territory)
  Review coverage      +5   (single reviewer, same team)
  ✦ Vuln delta         +18  (adds 1 critical CVE: CVE-2025-44821)
  ✦ Unresolved criticals +8  (2 open criticals in lodash@4.17.19)
  Timing               +10  (Thursday 4:45pm)
  ─────────────────────────
  Total                61

The marker on vulnerability signals distinguishes Snyk-sourced signals from code-structure signals. Clicking either signal expands a detail panel showing the specific CVE IDs, their CVSS scores, and the Snyk-provided remediation path (upgrade version, patch, or no fix available).

AI Chat: natural-language vulnerability queries

Koalr's AI Chat panel can answer questions about your Snyk data in plain English, joined against your PR and deployment data. Example queries your team can ask:

"Which of our open PRs introduce new critical CVEs?"
→ Returns a list of PRs with CVE details, severity, and fixability

"Which repos have the most unresolved critical vulnerabilities?"
→ Returns a ranked table with CVE counts by severity

"How long has CVE-2025-44821 been open in our codebase?"
→ Returns first-seen date, affected projects, and which teams own them

"Show me PRs in the last 30 days that fixed critical CVEs"
→ Returns merged PRs that reduced the open critical count

The AI Chat joins Snyk data against your GitHub PR history and CODEOWNERS team assignments, which means it can answer questions that neither Snyk nor GitHub can answer individually: "Which team has the most critical CVE debt in their owned repositories?"

The DORA math: how vulnerability debt degrades Change Failure Rate

The quantitative relationship between unresolved critical vulnerabilities and Change Failure Rate is worth making explicit. The mechanism has two paths.

Path 1: Direct exploitation. A critical CVE in a production dependency is exploited. The result is a security incident that is categorized as a deployment failure — the deployment that introduced (or failed to remediate) the vulnerable dependency is the failure event. This contributes directly to CFR.

Path 2: Emergency patch churn. The security team identifies a critical CVE before exploitation and requires an emergency patch. This generates a hotfix PR, a fast-tracked deployment outside normal release cadence, and often a rollback if the patch introduces a regression. Emergency patches have higher change failure rates than planned deployments — they are rushed, reviewed under pressure, and often skip the normal testing pipeline. Each emergency patch increases CFR by both the patch deployment itself (elevated failure probability) and the downstream reactive deployments it may trigger.

Koalr models the expected CFR contribution of outstanding vulnerability debt using the following approximation, calibrated against NIST NVD exploitation rate data:

expected_cfr_impact =
  P(exploitation | critical, age_days) × incident_weight
  + P(emergency_patch_required | critical, age_days) × patch_failure_rate

Where:
  P(exploitation | critical, age_days=30) ≈ 0.08  (8% base rate, NIST KEV data)
  P(exploitation | critical, age_days=90) ≈ 0.22  (22%, exploitation probability grows nonlinearly)
  incident_weight                         ≈ 1.0   (security incidents count as CFR events)
  P(emergency_patch | critical, age=30)   ≈ 0.35  (35% of critical CVEs trigger emergency patches)
  patch_failure_rate                      ≈ 0.18  (18% of emergency patches cause a regression)

# A single critical CVE at 30 days old contributes approximately:
# (0.08 × 1.0) + (0.35 × 0.18) = 0.08 + 0.063 = 0.143 expected CFR events

This is not a precise prediction — it is a directional model. The practical takeaway is that each unresolved critical CVE older than 30 days carries a non-trivial expected CFR cost, and that cost grows nonlinearly with time. Remediating three critical CVEs before they age past 30 days is a meaningfully better engineering outcome than remediating them at 90 days, even if the end state (zero open criticals) looks identical in a Snyk dashboard.

Team-level vulnerability ownership

One of the gaps in Snyk's default reporting is team attribution. Snyk shows you vulnerabilities per project, but it does not tell you which engineering team owns that project or how to route the remediation task. Security teams often end up manually triaging CVEs against a list of service owners — a process that does not scale past a few dozen services.

Koalr solves this by mapping Snyk projects to CODEOWNERS file assignments. The mapping works in three steps: Snyk projects are matched to GitHub repositories via the repository target in Snyk's project metadata. Each repository's CODEOWNERS file defines which Koalr teams own which paths within the repo. Koalr combines these two mappings to produce a team-level vulnerability ownership view: each team sees their own open critical and high count, severity distribution, oldest unresolved CVE, and fix lag trend.

This team-level view appears in Koalr's Team page alongside the team's DORA metrics. A team lead can see in a single dashboard: their deployment frequency, change failure rate, MTTR, and their open critical CVE count — all scoped to the repositories their team owns. Security debt becomes a peer metric to engineering velocity, not a separate concern managed in a different tool by a different team.

For organizations with dedicated platform or security engineering teams, Koalr can additionally show a cross-team comparison: which team has the highest vulnerability debt relative to their deployment frequency? This framing is useful for prioritization conversations — a team shipping 20 deployments per week with 5 open criticals is a different risk profile from a team shipping 2 deployments per week with the same 5 open criticals.

CODEOWNERS as the source of truth

Koalr uses your existing GitHub CODEOWNERS file as the authoritative team-to-repo mapping. You do not need to maintain a separate ownership registry in Snyk or in Koalr. Changes to your CODEOWNERS file are picked up on the next sync — if the Payments team takes ownership of a new service, their vulnerability count in Koalr updates automatically.

Putting it all together

The pattern Koalr is solving for is simple but common: security tooling and engineering tooling live in separate dashboards, owned by separate teams, with separate review cadences. Snyk finds the vulnerabilities. The security team triages them and creates Jira tickets. The engineering team processes those tickets when they reach the top of the backlog. The connection between vulnerability age and deployment risk is never made explicit — until an incident makes it explicit for you.

By pulling Snyk signals into the deploy risk score and the DORA dashboard, Koalr puts security debt in the same place as the engineering team's other performance metrics. The deploy risk score on a PR becomes a unified signal: large change, low review coverage, introduces a critical CVE, and touches a package with 4 existing open criticals. The engineer can see all of that in one panel, before they click merge, and make a better-informed decision.

The same data informs retrospectives and planning conversations. Instead of the security team presenting a separate vulnerability report at the quarterly review, the engineering manager opens Koalr's DORA dashboard and the vulnerability trend is already there — directionally improving or not, attributed to specific teams, with the oldest unresolved CVEs identified and ready for prioritization.

Connect Snyk to Koalr and make security debt visible in DORA

Set up in under 5 minutes. Koalr syncs your Snyk projects and open issues, maps them to your GitHub repos and CODEOWNERS teams, and immediately incorporates vulnerability signals into every PR's deploy risk score. No Snyk plan upgrade required.