From 53c9a7cc656d2e0c6d602bf2b80683856385e937 Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:51:28 +0200 Subject: [PATCH] bench(pairs): real fix-commit pairs for the short families 97 advisory-fix pairs, one repository each (309 advisories examined, 212 rejected with reasons in research-2026-10-01.md): a public fix commit distinct from its parent, the advisory-named function declared on both sides with a changed body. Pointer pairs: materialize.py reads one file per side from a shallow blobless fetch into the pair cache and verifies them with the label builder's matcher (verification.json, bytes read). learn/sources.toml lists the catalog as the advisory-fixes source. Label snapshot: negative repository groups untrusted_destination 8->25, path 3->21, output_encoding 1->22, deserialization 0->21, access_control 0->20; all five reach the per-family floor. Five pairs on reserved repositories were dropped after the reserved-population guard flagged them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo --- .../advisory-fixes-spread.json | 187 + .../labels.json | 507 +++ benchmarks/pairs/advisory-fixes/catalog.toml | 3116 +++++++++++++++++ .../pairs/advisory-fixes/materialize.py | 249 ++ .../advisory-fixes/research-2026-10-01.md | 429 +++ .../pairs/advisory-fixes/verification.json | 491 +++ src/openultrasast/learn/sources.toml | 14 + tests/test_advisory_fix_pairs.py | 88 + 8 files changed, 5081 insertions(+) create mode 100644 benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/advisory-fixes-spread.json create mode 100644 benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/labels.json create mode 100644 benchmarks/pairs/advisory-fixes/catalog.toml create mode 100644 benchmarks/pairs/advisory-fixes/materialize.py create mode 100644 benchmarks/pairs/advisory-fixes/research-2026-10-01.md create mode 100644 benchmarks/pairs/advisory-fixes/verification.json create mode 100644 tests/test_advisory_fix_pairs.py diff --git a/benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/advisory-fixes-spread.json b/benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/advisory-fixes-spread.json new file mode 100644 index 0000000..fc0853d --- /dev/null +++ b/benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/advisory-fixes-spread.json @@ -0,0 +1,187 @@ +{ + "created": "2026-10-01", + "note": "Counts only. The advisory-fix catalog (benchmarks/pairs/advisory-fixes/catalog.toml) per family: one pair = one repository group, each a real fix (fix commit != parent, the advisory-named function declared on both sides, body changed; verification.json). framework is the catalog's informational field ('none' = a library or no web framework); the label builder itself records no framework for pair rows.", + "families": { + "access_control": { + "pairs": 20, + "language": { + "go": 4, + "python": 4, + "java": 3, + "javascript/typescript": 3, + "csharp": 2, + "php": 2, + "ruby": 2 + }, + "framework": { + "none": 5, + "django": 3, + "aspnetcore": 2, + "rails": 2, + "echo": 1, + "hono": 1, + "jenkins": 1, + "payload": 1, + "spring": 1, + "starlette": 1, + "symfony": 1, + "winter": 1 + }, + "licence": { + "MIT": 8, + "Apache-2.0": 5, + "BSD-3-Clause": 4, + "AGPL-3.0": 2, + "GPL-2.0": 1 + }, + "split": { + "holdout": 10, + "train": 10 + } + }, + "deserialization": { + "pairs": 21, + "language": { + "python": 5, + "java": 4, + "php": 4, + "csharp": 3, + "ruby": 3, + "javascript/typescript": 2 + }, + "framework": { + "none": 12, + "typo3": 2, + "aiohttp": 1, + "asp.net": 1, + "jooby": 1, + "karaf": 1, + "laravel": 1, + "rails": 1, + "react-router": 1 + }, + "licence": { + "MIT": 10, + "Apache-2.0": 7, + "GPL-2.0": 1, + "GPL-3.0": 1, + "MPL-1.1": 1, + "MPL-2.0": 1 + }, + "split": { + "train": 11, + "holdout": 10 + } + }, + "output_encoding": { + "pairs": 21, + "language": { + "javascript/typescript": 4, + "php": 4, + "python": 4, + "java": 3, + "csharp": 2, + "go": 2, + "ruby": 2 + }, + "framework": { + "none": 10, + "django": 1, + "express": 1, + "fiber": 1, + "hono": 1, + "jenkins": 1, + "jquery": 1, + "jsf": 1, + "laravel": 1, + "rails": 1, + "silverstripe": 1, + "tornado": 1 + }, + "licence": { + "MIT": 13, + "Apache-2.0": 5, + "BSD-3-Clause": 3 + }, + "split": { + "train": 11, + "holdout": 10 + } + }, + "path": { + "pairs": 18, + "language": { + "java": 4, + "go": 3, + "javascript/typescript": 3, + "ruby": 3, + "php": 2, + "python": 2, + "csharp": 1 + }, + "framework": { + "none": 4, + "fastify": 2, + "spring": 2, + "aspnet": 1, + "express": 1, + "flask": 1, + "jenkins": 1, + "laravel": 1, + "rack": 1, + "rails": 1, + "tornado": 1, + "wicket": 1, + "yii": 1 + }, + "licence": { + "MIT": 11, + "Apache-2.0": 5, + "BSD-2-Clause": 1, + "BSD-3-Clause": 1 + }, + "split": { + "holdout": 9, + "train": 9 + } + }, + "untrusted_destination": { + "pairs": 17, + "language": { + "php": 4, + "python": 4, + "javascript/typescript": 3, + "go": 2, + "java": 2, + "csharp": 1, + "ruby": 1 + }, + "framework": { + "jenkins": 2, + "net/http": 2, + "none": 2, + "cakephp": 1, + "django": 1, + "feathers": 1, + "flask": 1, + "grav": 1, + "koa": 1, + "laravel": 1, + "nuxt": 1, + "rails": 1, + "symfony": 1, + "tornado": 1 + }, + "licence": { + "MIT": 13, + "Apache-2.0": 2, + "BSD-3-Clause": 2 + }, + "split": { + "train": 9, + "holdout": 8 + } + } + }, + "pairs": 97 +} diff --git a/benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/labels.json b/benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/labels.json new file mode 100644 index 0000000..a2fac6f --- /dev/null +++ b/benchmarks/measurements/2026-10-01-decision-engine-labels-advisory-fixes/labels.json @@ -0,0 +1,507 @@ +{ + "assumed_benign_history": { + "accepted": 969, + "commits_scanned": 36000, + "rejected": { + "bulk change": 278, + "dependency": 2206, + "english": 4251, + "later fix touches its files": 3168, + "near_fix": 4911, + "no changed function": 50, + "no product source": 2368, + "other_language": 2, + "protocol pin": 55, + "reference": 77 + }, + "repositories": 28 + }, + "conditional_negatives": { + "assumed_benign": { + "by_language": { + "go": 18, + "javascript": 2136, + "php": 832, + "python": 1049, + "typescript": 636 + }, + "groups": 27, + "pushes": 969, + "rows": 4671 + }, + "benign_control": { + "by_language": { + "javascript": 7, + "php": 27, + "python": 23, + "typescript": 15 + }, + "groups": 24, + "pushes": 24, + "rows": 72 + } + }, + "created": "2026-10-01", + "excluded_unread": [ + "candidates-v3-php.json", + "freeze-v3-php.json", + "population-v3-php.toml", + "protocol-v3.md", + "research-2026-09-30-php.md" + ], + "floors": { + "per_family": 20, + "pooled": 10, + "unit": "repository groups with positive and with negative pin labels" + }, + "group_merges": { + "advisory": 0, + "alias": 0, + "commit": 0 + }, + "groups": 202, + "note": "Label snapshot after adding the advisory-fixes source (benchmarks/pairs/advisory-fixes, 97 real fix-commit pairs, one repository each, none already named under benchmarks/ and none reserved). COUNTS ONLY, same builder and sources as 2026-10-01-decision-engine-labels plus that one source. Every new pair gave a fixed-side negative (pair_fixed_side.absent_fixed_side is unchanged at 108); groups 105 -> 202 with no merges. All five short families now reach the per-family floor of 20 negative groups. Language/framework spread of the new pairs: advisory-fixes-spread.json. Supersedes 2026-10-01-decision-engine-labels for the per-family floors.", + "pair_fixed_side": { + "absent_fixed_side": { + "by_family": { + "access_control": 13, + "config_secrets": 4, + "deserialization": 20, + "injection": 18, + "output_encoding": 16, + "path": 11, + "untrusted_destination": 26 + }, + "labels": 108 + }, + "fixed_side_moved": 0 + }, + "rows": 6589, + "schema": 1, + "skipped": { + "pairs: label names no function": 44, + "pairs: no fixed-side negative (fixed side is another function of the same snapshot)": 108, + "pairs: title tier (sensitivity arm)": 58, + "pairs: unscorable pair": 7, + "population: positive the fix did not change (no negative)": 22 + }, + "sources": [ + "adjudications", + "advisory-fixes", + "assumed_benign", + "dev-php", + "pairs", + "population-v1", + "population-v2" + ], + "verified_delta_labels": { + "access_control:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 32, + "positive_rows": 33 + }, + "access_control:repair": { + "negative_groups": 20, + "negative_rows": 20, + "positive_groups": 0, + "positive_rows": 0 + }, + "config_secrets:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 5, + "positive_rows": 18 + }, + "config_secrets:repair": { + "negative_groups": 2, + "negative_rows": 12, + "positive_groups": 0, + "positive_rows": 0 + }, + "deserialization:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 41, + "positive_rows": 41 + }, + "deserialization:repair": { + "negative_groups": 21, + "negative_rows": 21, + "positive_groups": 0, + "positive_rows": 0 + }, + "injection:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 42, + "positive_rows": 77 + }, + "injection:repair": { + "negative_groups": 33, + "negative_rows": 48, + "positive_groups": 0, + "positive_rows": 0 + }, + "memory:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 5, + "positive_rows": 173 + }, + "memory:repair": { + "negative_groups": 5, + "negative_rows": 173, + "positive_groups": 0, + "positive_rows": 0 + }, + "output_encoding:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 36, + "positive_rows": 77 + }, + "output_encoding:repair": { + "negative_groups": 22, + "negative_rows": 61, + "positive_groups": 0, + "positive_rows": 0 + }, + "path:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 32, + "positive_rows": 32 + }, + "path:repair": { + "negative_groups": 21, + "negative_rows": 21, + "positive_groups": 0, + "positive_rows": 0 + }, + "unknown:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 2, + "positive_rows": 2 + }, + "unknown:repair": { + "negative_groups": 2, + "negative_rows": 2, + "positive_groups": 0, + "positive_rows": 0 + }, + "untrusted_destination:introduce": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 52, + "positive_rows": 62 + }, + "untrusted_destination:repair": { + "negative_groups": 24, + "negative_rows": 27, + "positive_groups": 0, + "positive_rows": 0 + } + }, + "verified_pin_labels": { + "by_family": { + "access_control": { + "model": "per_family", + "negative_groups": 20, + "negative_rows": 20, + "positive_groups": 32, + "positive_rows": 33 + }, + "config_secrets": { + "model": "insufficient", + "negative_groups": 2, + "negative_rows": 13, + "positive_groups": 5, + "positive_rows": 18 + }, + "deserialization": { + "model": "per_family", + "negative_groups": 21, + "negative_rows": 21, + "positive_groups": 41, + "positive_rows": 41 + }, + "injection": { + "model": "per_family", + "negative_groups": 33, + "negative_rows": 71, + "positive_groups": 44, + "positive_rows": 81 + }, + "memory": { + "model": "insufficient", + "negative_groups": 5, + "negative_rows": 173, + "positive_groups": 5, + "positive_rows": 173 + }, + "output_encoding": { + "model": "per_family", + "negative_groups": 22, + "negative_rows": 61, + "positive_groups": 36, + "positive_rows": 77 + }, + "path": { + "model": "per_family", + "negative_groups": 21, + "negative_rows": 21, + "positive_groups": 33, + "positive_rows": 33 + }, + "unknown": { + "model": "insufficient", + "negative_groups": 2, + "negative_rows": 2, + "positive_groups": 2, + "positive_rows": 2 + }, + "untrusted_destination": { + "model": "per_family", + "negative_groups": 25, + "negative_rows": 43, + "positive_groups": 52, + "positive_rows": 63 + } + }, + "by_framework": { + "django": { + "negative_groups": 1, + "negative_rows": 2, + "positive_groups": 1, + "positive_rows": 2 + }, + "express": { + "negative_groups": 3, + "negative_rows": 6, + "positive_groups": 4, + "positive_rows": 12 + }, + "flask": { + "negative_groups": 2, + "negative_rows": 3, + "positive_groups": 2, + "positive_rows": 3 + }, + "httpx": { + "negative_groups": 3, + "negative_rows": 4, + "positive_groups": 3, + "positive_rows": 6 + }, + "lodash": { + "negative_groups": 5, + "negative_rows": 9, + "positive_groups": 6, + "positive_rows": 15 + }, + "node-http-clients": { + "negative_groups": 4, + "negative_rows": 8, + "positive_groups": 5, + "positive_rows": 9 + }, + "none": { + "negative_groups": 8, + "negative_rows": 25, + "positive_groups": 9, + "positive_rows": 36 + }, + "pyyaml": { + "negative_groups": 3, + "negative_rows": 5, + "positive_groups": 3, + "positive_rows": 6 + }, + "react": { + "negative_groups": 4, + "negative_rows": 4, + "positive_groups": 4, + "positive_rows": 5 + }, + "requests": { + "negative_groups": 5, + "negative_rows": 8, + "positive_groups": 5, + "positive_rows": 10 + }, + "sanitize-html": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 1, + "positive_rows": 1 + }, + "sqlalchemy": { + "negative_groups": 2, + "negative_rows": 2, + "positive_groups": 2, + "positive_rows": 3 + }, + "unknown": { + "negative_groups": 137, + "negative_rows": 372, + "positive_groups": 174, + "positive_rows": 443 + }, + "vue": { + "negative_groups": 3, + "negative_rows": 5, + "positive_groups": 4, + "positive_rows": 6 + }, + "wordpress": { + "negative_groups": 5, + "negative_rows": 8, + "positive_groups": 9, + "positive_rows": 13 + } + }, + "by_language": { + "c_cpp": { + "negative_groups": 5, + "negative_rows": 173, + "positive_groups": 5, + "positive_rows": 173 + }, + "csharp": { + "negative_groups": 9, + "negative_rows": 9, + "positive_groups": 9, + "positive_rows": 9 + }, + "go": { + "negative_groups": 11, + "negative_rows": 11, + "positive_groups": 11, + "positive_rows": 11 + }, + "java": { + "negative_groups": 17, + "negative_rows": 56, + "positive_groups": 17, + "positive_rows": 56 + }, + "javascript": { + "negative_groups": 30, + "negative_rows": 35, + "positive_groups": 27, + "positive_rows": 36 + }, + "php": { + "negative_groups": 25, + "negative_rows": 42, + "positive_groups": 29, + "positive_rows": 38 + }, + "python": { + "negative_groups": 29, + "negative_rows": 42, + "positive_groups": 78, + "positive_rows": 151 + }, + "ruby": { + "negative_groups": 11, + "negative_rows": 11, + "positive_groups": 11, + "positive_rows": 11 + }, + "typescript": { + "negative_groups": 17, + "negative_rows": 43, + "positive_groups": 17, + "positive_rows": 33 + }, + "unknown": { + "negative_groups": 1, + "negative_rows": 3, + "positive_groups": 1, + "positive_rows": 3 + } + }, + "by_source": { + "adjudications": { + "negative_groups": 15, + "negative_rows": 40, + "positive_groups": 3, + "positive_rows": 3 + }, + "dev-php": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 3, + "positive_rows": 3 + }, + "pairs": { + "negative_groups": 122, + "negative_rows": 332, + "positive_groups": 171, + "positive_rows": 440 + }, + "population-v1": { + "negative_groups": 11, + "negative_rows": 35, + "positive_groups": 11, + "positive_rows": 50 + }, + "population-v2": { + "negative_groups": 14, + "negative_rows": 18, + "positive_groups": 17, + "positive_rows": 25 + } + }, + "by_split": { + "dev-php": { + "negative_groups": 0, + "negative_rows": 0, + "positive_groups": 3, + "positive_rows": 3 + }, + "holdout": { + "negative_groups": 59, + "negative_rows": 182, + "positive_groups": 110, + "positive_rows": 236 + }, + "population-v1": { + "negative_groups": 11, + "negative_rows": 46, + "positive_groups": 11, + "positive_rows": 51 + }, + "population-v2": { + "negative_groups": 15, + "negative_rows": 47, + "positive_groups": 17, + "positive_rows": 27 + }, + "train": { + "negative_groups": 66, + "negative_rows": 150, + "positive_groups": 115, + "positive_rows": 204 + } + }, + "design_informed": { + "adjudications": { + "negative_groups": 13, + "negative_rows": 29, + "positive_groups": 2, + "positive_rows": 2 + }, + "population-v2": { + "negative_groups": 14, + "negative_rows": 18, + "positive_groups": 17, + "positive_rows": 25 + } + }, + "privileged_positives": 2 + } +} diff --git a/benchmarks/pairs/advisory-fixes/catalog.toml b/benchmarks/pairs/advisory-fixes/catalog.toml new file mode 100644 index 0000000..6f31c2d --- /dev/null +++ b/benchmarks/pairs/advisory-fixes/catalog.toml @@ -0,0 +1,3116 @@ +# Advisory-fix pairs (2026-10-01): real fix commits for the families the pair corpus left without negatives. +# +# Every pair is a public fix commit for a published advisory (CVE / GHSA), distinct from its parent, and names the +# advisory's vulnerable function, which is declared on both sides and whose body the fix changes. The positive is the +# function at `parent`, the negative the same function at `commit`. Research and every rejected advisory: +# research-2026-10-01.md. Both sides are pointer pairs (vendored = false): materialize.py reads one file per side from +# a shallow blobless fetch into the pair cache and writes the function excerpts there; nothing upstream is vendored. +# verification.json is that script's per-pair check (declared on both sides, body changed, bytes read). +# +# Schema: the pair catalog's (benchmarks/pairs/README.md). `framework` is informational (the loader ignores it); the +# label family is the expected row's `family`. One pair per repository; `split` alternates train/holdout by fix date +# within each family, so a repository never straddles. + +[[pair]] +name = "grav-redirect-2eae104" +slice = "advisory-fixes" +language = "php" +framework = "grav" +origin = "github-advisory" +license = "MIT" +repo = "getgrav/grav" +cve = "CVE-2020-11529 GHSA-wrxc-mr2w-cjpv" +cwe = "CWE-601" +commit = "2eae104c7a4bf32bc26cb8073d5c40464bfda3f7" +parent = "6f2be2a2d225355c50362f716ffc154d711358f6" +commit_url = "https://github.com/getgrav/grav/commit/2eae104c7a4bf32bc26cb8073d5c40464bfda3f7" +vendored = false +host = "github" +path = "system/src/Grav/Common/Grav.php" +mode = "name" +relpath = "system/src/Grav/Common/Grav.php" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2020-03-18" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "system/src/Grav/Common/Grav.php" +function = "redirect" +mechanism = "validation_strength" +family = "untrusted_destination" +evidence = "GHSA-wrxc-mr2w-cjpv: redirect() now collapses leading slash/backslash runs in the route so //host routes can no longer become protocol-relative external redirects" + +[[pair]] +name = "cas-plugin-determinetargeturl-8ac536a" +slice = "advisory-fixes" +language = "java" +framework = "jenkins" +origin = "github-advisory" +license = "MIT" +repo = "jenkinsci/cas-plugin" +cve = "CVE-2021-21673 GHSA-2vvr-5757-qp87" +cwe = "CWE-601" +commit = "8ac536a953156160dbabb61bdb3b9bc75c3d0ef9" +parent = "13c6100f3561715cfd2a9b0d7d24fc4661570b1d" +commit_url = "https://github.com/jenkinsci/cas-plugin/commit/8ac536a953156160dbabb61bdb3b9bc75c3d0ef9" +vendored = false +host = "github" +path = "src/main/java/org/jenkinsci/plugins/cas/spring/security/SessionUrlAuthenticationSuccessHandler.java" +mode = "name" +relpath = "src/main/java/org/jenkinsci/plugins/cas/spring/security/SessionUrlAuthenticationSuccessHandler.java" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2021-06-29" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "src/main/java/org/jenkinsci/plugins/cas/spring/security/SessionUrlAuthenticationSuccessHandler.java" +function = "determineTargetUrl" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-2vvr-5757-qp87: determineTargetUrl returned the session-stored target URL unchecked; fix falls back to the default target unless Util.isSafeToRedirectTo holds" + +[[pair]] +name = "spirit-custom-login-8f32f89" +slice = "advisory-fixes" +language = "python" +framework = "django" +origin = "github-advisory" +license = "MIT" +repo = "nitely/spirit" +cve = "CVE-2022-0869 GHSA-5p9j-w2wx-qx4c" +cwe = "CWE-601" +commit = "8f32f89654d6c30d56e0dd167059d32146fb32ef" +parent = "8b48d18e44f1dbb4b0f0a0a975d0dc14b88f0f41" +commit_url = "https://github.com/nitely/spirit/commit/8f32f89654d6c30d56e0dd167059d32146fb32ef" +vendored = false +host = "github" +path = "spirit/user/auth/views.py" +mode = "name" +relpath = "spirit/user/auth/views.py" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2022-02-23" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "spirit/user/auth/views.py" +function = "custom_login" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-5p9j-w2wx-qx4c: custom_login replaces redirect(request.GET next) with safe_redirect (url_has_allowed_host_and_scheme check)" + +[[pair]] +name = "archivy-login-2d8cb29" +slice = "advisory-fixes" +language = "python" +framework = "flask" +origin = "github-advisory" +license = "MIT" +repo = "archivy/archivy" +cve = "CVE-2022-0697 GHSA-28mg-98xm-q493" +cwe = "CWE-601" +commit = "2d8cb29853190d42572b36deb61127e68d6be574" +parent = "fa389e7d59f91980693965830839d6de1f1db45f" +commit_url = "https://github.com/archivy/archivy/commit/2d8cb29853190d42572b36deb61127e68d6be574" +vendored = false +host = "github" +path = "archivy/routes.py" +mode = "name" +relpath = "archivy/routes.py" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2022-02-24" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "archivy/routes.py" +function = "login" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-28mg-98xm-q493: login redirects to next only when is_safe_redirect_url (same host, http/https) holds" + +[[pair]] +name = "google-login-plugin-docommencelogin-532d714" +slice = "advisory-fixes" +language = "java" +framework = "jenkins" +origin = "github-advisory" +license = "Apache-2.0" +repo = "jenkinsci/google-login-plugin" +cve = "CVE-2022-46683 GHSA-v93c-cxj5-c398" +cwe = "CWE-601" +commit = "532d714943ff8ae8dc862427d39a4b78b7f6a375" +parent = "6b616f7709037a9bde1ee1646b18811a45e0b1fd" +commit_url = "https://github.com/jenkinsci/google-login-plugin/commit/532d714943ff8ae8dc862427d39a4b78b7f6a375" +vendored = false +host = "github" +path = "src/main/java/org/jenkinsci/plugins/googlelogin/GoogleOAuth2SecurityRealm.java" +mode = "name" +relpath = "src/main/java/org/jenkinsci/plugins/googlelogin/GoogleOAuth2SecurityRealm.java" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2022-12-05" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "src/main/java/org/jenkinsci/plugins/googlelogin/GoogleOAuth2SecurityRealm.java" +function = "doCommenceLogin" +mechanism = "validation_strength" +family = "untrusted_destination" +evidence = "GHSA-v93c-cxj5-c398: doCommenceLogin used from/Referer only when Util.isSafeToRedirectTo was FALSE (inverted); fix routes through a correct safe-redirect check" + +[[pair]] +name = "truelayer-dotnet-getpayment-75e436e" +slice = "advisory-fixes" +language = "csharp" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "truelayer/truelayer-dotnet" +cve = "CVE-2024-23838 GHSA-67m4-qxp3-j6hh" +cwe = "CWE-918" +commit = "75e436ed5360faa73d6e7ce3a9903a3c49505e3e" +parent = "9bf4120a355b1da8f2622b5c9bbe6b084b06cb91" +commit_url = "https://github.com/truelayer/truelayer-dotnet/commit/75e436ed5360faa73d6e7ce3a9903a3c49505e3e" +vendored = false +host = "github" +path = "src/TrueLayer/Payments/PaymentsApi.cs" +mode = "name" +relpath = "src/TrueLayer/Payments/PaymentsApi.cs" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2024-01-22" + +[[pair.expected]] +cwe = "CWE-918" +class = "ssrf" +path = "src/TrueLayer/Payments/PaymentsApi.cs" +function = "GetPayment" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-67m4-qxp3-j6hh: GetPayment built new Uri(_baseUri, id) so an absolute-URL id redirected the authenticated request off-host; fix rejects URL ids (NotAUrl) and appends the id as a path segment" + +[[pair]] +name = "koa-back-422c551" +slice = "advisory-fixes" +language = "javascript" +framework = "koa" +origin = "github-advisory" +license = "MIT" +repo = "koajs/koa" +cve = "CVE-2025-8129 GHSA-jgmv-j7ww-jx2x" +cwe = "CWE-601" +commit = "422c551c63d00f24e2bbbdf492f262a5935bb1f0" +parent = "6e51eb15169598be1130e8bbe06e67b1d4907275" +commit_url = "https://github.com/koajs/koa/commit/422c551c63d00f24e2bbbdf492f262a5935bb1f0" +vendored = false +host = "github" +path = "lib/response.js" +mode = "name" +relpath = "lib/response.js" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2025-07-27" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "lib/response.js" +function = "back" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-jgmv-j7ww-jx2x: back() no longer redirects to the raw Referrer header; it accepts only relative paths or same-host absolute URLs" + +[[pair]] +name = "miniflux-checklogin-76df99f" +slice = "advisory-fixes" +language = "go" +framework = "net/http" +origin = "github-advisory" +license = "Apache-2.0" +repo = "miniflux/v2" +cve = "CVE-2025-67713 GHSA-wqv2-4wpg-8hc9" +cwe = "CWE-601" +commit = "76df99f3a3db234cf6b312be5e771485213d03c7" +parent = "57e3eaecd9bd2d9e70c7bda8fe20b2cc90badd9d" +commit_url = "https://github.com/miniflux/v2/commit/76df99f3a3db234cf6b312be5e771485213d03c7" +vendored = false +host = "github" +path = "internal/ui/login_check.go" +mode = "name" +relpath = "internal/ui/login_check.go" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2025-10-26" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "internal/ui/login_check.go" +function = "checkLogin" +mechanism = "validation_strength" +family = "untrusted_destination" +evidence = "GHSA-wqv2-4wpg-8hc9: checkLogin accepted any non-absolute redirect_url (including //host); fix requires urllib.IsRelativePath (no host, no scheme) before redirecting" + +[[pair]] +name = "taguette-go-to-next-67de2d2" +slice = "advisory-fixes" +language = "python" +framework = "tornado" +origin = "github-advisory" +license = "BSD-3-Clause" +repo = "remram44/taguette" +cve = "CVE-2025-67502 GHSA-5923-r76v-mprm" +cwe = "CWE-601" +commit = "67de2d2612e7e2572c61cd9627f89c2bfd0f2a36" +parent = "b34e2156a49a313c8af13885d9b0ae6fe3b1b93e" +commit_url = "https://github.com/remram44/taguette/commit/67de2d2612e7e2572c61cd9627f89c2bfd0f2a36" +vendored = false +host = "github" +path = "taguette/web/views.py" +mode = "name" +relpath = "taguette/web/views.py" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2025-12-08" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "taguette/web/views.py" +function = "_go_to_next" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-5923-r76v-mprm: _go_to_next now passes the next argument through sanitize_next_url (same-base-path allow-list) before redirect" + +[[pair]] +name = "weasyprint-default-url-fetcher-b6a14f0" +slice = "advisory-fixes" +language = "python" +framework = "" +origin = "github-advisory" +license = "BSD-3-Clause" +repo = "kozea/weasyprint" +cve = "CVE-2025-68616 GHSA-983w-rhvv-gwmv" +cwe = "CWE-918" +commit = "b6a14f0f3f4ce9c0c75c1a2d73cb1c5d43f0e565" +parent = "1140eba630ce1c5194872e90122df0e154790d65" +commit_url = "https://github.com/kozea/weasyprint/commit/b6a14f0f3f4ce9c0c75c1a2d73cb1c5d43f0e565" +vendored = false +host = "github" +path = "weasyprint/urls.py" +mode = "name" +relpath = "weasyprint/urls.py" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-01-19" + +[[pair.expected]] +cwe = "CWE-918" +class = "ssrf" +path = "weasyprint/urls.py" +function = "default_url_fetcher" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-983w-rhvv-gwmv: default_url_fetcher followed HTTP redirects past custom-fetcher allow-lists; fix builds its URLFetcher with allow_redirects=False" + +[[pair]] +name = "feathers-getredirect-ee19a0a" +slice = "advisory-fixes" +language = "typescript" +framework = "feathers" +origin = "github-advisory" +license = "MIT" +repo = "feathersjs/feathers" +cve = "CVE-2026-27191 GHSA-ppf9-4ffw-hh4p" +cwe = "CWE-601" +commit = "ee19a0ae9bc2ebf23b1fe598a1f7361981b65401" +parent = "e4c0dac68b00615c7bc4724871375a14863a2447" +commit_url = "https://github.com/feathersjs/feathers/commit/ee19a0ae9bc2ebf23b1fe598a1f7361981b65401" +vendored = false +host = "github" +path = "packages/authentication-oauth/src/strategy.ts" +mode = "name" +relpath = "packages/authentication-oauth/src/strategy.ts" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-02-03" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "packages/authentication-oauth/src/strategy.ts" +function = "getRedirect" +mechanism = "validation_strength" +family = "untrusted_destination" +evidence = "GHSA-ppf9-4ffw-hh4p: getRedirect now rejects redirect query values containing @, backslash or // before appending them to the allowed origin" + +[[pair]] +name = "mailpit-dohead-10ad4df" +slice = "advisory-fixes" +language = "go" +framework = "net/http" +origin = "github-advisory" +license = "MIT" +repo = "axllent/mailpit" +cve = "CVE-2026-27808 GHSA-mpf7-p9x7-96r3" +cwe = "CWE-918" +commit = "10ad4df8cc0cd9e51dea1b4410009545eef7fbf5" +parent = "632113fcc5c953e282d45896c63479a291579fe3" +commit_url = "https://github.com/axllent/mailpit/commit/10ad4df8cc0cd9e51dea1b4410009545eef7fbf5" +vendored = false +host = "github" +path = "internal/linkcheck/status.go" +mode = "name" +relpath = "internal/linkcheck/status.go" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-02-24" + +[[pair.expected]] +cwe = "CWE-918" +class = "ssrf" +path = "internal/linkcheck/status.go" +function = "doHead" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-mpf7-p9x7-96r3: doHead now validates the link URL, dials through safeDialContext (blocks internal IPs) and re-validates redirect targets" + +[[pair]] +name = "devise-redirect-url-025fe21" +slice = "advisory-fixes" +language = "ruby" +framework = "rails" +origin = "github-advisory" +license = "MIT" +repo = "heartcombo/devise" +cve = "CVE-2026-40295 GHSA-jp94-3292-c3xv" +cwe = "CWE-601" +commit = "025fe2124f9928766fc46520e999633b598d0360" +parent = "7ca7ed9c174525a4d36167441b35af4a0991b6af" +commit_url = "https://github.com/heartcombo/devise/commit/025fe2124f9928766fc46520e999633b598d0360" +vendored = false +host = "github" +path = "lib/devise/failure_app.rb" +mode = "name" +relpath = "lib/devise/failure_app.rb" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-05-08" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "lib/devise/failure_app.rb" +function = "redirect_url" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-jp94-3292-c3xv: FailureApp#redirect_url returned request.referrer verbatim on non-GET timeouts; fix reduces it to a same-site path via extract_path_from_location" + +[[pair]] +name = "koel-createforepisode-8708f07" +slice = "advisory-fixes" +language = "php" +framework = "laravel" +origin = "github-advisory" +license = "MIT" +repo = "koel/koel" +cve = "CVE-2026-47260 GHSA-7j2f-6h2r-6cqc" +cwe = "CWE-918" +commit = "8708f077efd7d8a332b32e954d65bc837f3a413a" +parent = "1f1700743ef5fde0d8d74e1ee2d98fb0b134aae1" +commit_url = "https://github.com/koel/koel/commit/8708f077efd7d8a332b32e954d65bc837f3a413a" +vendored = false +host = "github" +path = "app/Values/Podcast/EpisodePlayable.php" +mode = "name" +relpath = "app/Values/Podcast/EpisodePlayable.php" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-05-18" + +[[pair.expected]] +cwe = "CWE-918" +class = "ssrf" +path = "app/Values/Podcast/EpisodePlayable.php" +function = "createForEpisode" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-7j2f-6h2r-6cqc: createForEpisode refuses to Http::sink()->get() an episode enclosure URL unless Network::isSafeUrl (public host, http/https)" + +[[pair]] +name = "authentication-getloginredirect-1c1e29c" +slice = "advisory-fixes" +language = "php" +framework = "cakephp" +origin = "github-advisory" +license = "MIT" +repo = "cakephp/authentication" +cve = "CVE-2026-55590 GHSA-hhpq-7wg4-36jm" +cwe = "CWE-601" +commit = "1c1e29c7e8129cfbcae74558316ecd3ea50a8273" +parent = "3f7cb4a7a0313139d5d3ce2bdd7854c19bae2491" +commit_url = "https://github.com/cakephp/authentication/commit/1c1e29c7e8129cfbcae74558316ecd3ea50a8273" +vendored = false +host = "github" +path = "src/AuthenticationService.php" +mode = "name" +relpath = "src/AuthenticationService.php" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-06-24" + +[[pair.expected]] +cwe = "CWE-601" +class = "open_redirect" +path = "src/AuthenticationService.php" +function = "getLoginRedirect" +mechanism = "validation_strength" +family = "untrusted_destination" +evidence = "GHSA-hhpq-7wg4-36jm: getLoginRedirect normalises backslashes to slashes and rejects values starting with // before the host/scheme check" + +[[pair]] +name = "og-image-resolve-243cac2" +slice = "advisory-fixes" +language = "typescript" +framework = "nuxt" +origin = "github-advisory" +license = "MIT" +repo = "nuxt-modules/og-image" +cve = "CVE-2026-61793 GHSA-q8hw-4fvp-9rwv" +cwe = "CWE-918" +commit = "243cac2228671d3711c2bd65e300c278fcdf5a4e" +parent = "5aa6dbf0b3848b578a87144032cfb6b734454b56" +commit_url = "https://github.com/nuxt-modules/og-image/commit/243cac2228671d3711c2bd65e300c278fcdf5a4e" +vendored = false +host = "github" +path = "src/runtime/server/og-image/bindings/font-assets/node.ts" +mode = "name" +relpath = "src/runtime/server/og-image/bindings/font-assets/node.ts" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-06-24" + +[[pair.expected]] +cwe = "CWE-918" +class = "ssrf" +path = "src/runtime/server/og-image/bindings/font-assets/node.ts" +function = "resolve" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-q8hw-4fvp-9rwv: resolve() fetched new URL(font path, origin) for attacker-supplied fonts[].path so absolute or //host paths reached arbitrary hosts; fix classifies data:/external URLs and fetches through fetchWithRedirectValidation" + +[[pair]] +name = "koillection-fetchwithhttpclient-4d445e2" +slice = "advisory-fixes" +language = "php" +framework = "symfony" +origin = "github-advisory" +license = "MIT" +repo = "benjaminjonard/koillection" +cve = "CVE-2026-50888 GHSA-gmxh-hjfv-qc2w" +cwe = "CWE-918" +commit = "4d445e21c631c26070f19fe8ec086a2939767ae0" +parent = "d2a244068046c65001d429d0d27e58262255cc00" +commit_url = "https://github.com/benjaminjonard/koillection/commit/4d445e21c631c26070f19fe8ec086a2939767ae0" +vendored = false +host = "github" +path = "src/Service/Scraper/HtmlScraper.php" +mode = "name" +relpath = "src/Service/Scraper/HtmlScraper.php" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-08-05" + +[[pair.expected]] +cwe = "CWE-918" +class = "ssrf" +path = "src/Service/Scraper/HtmlScraper.php" +function = "fetchWithHttpClient" +mechanism = "source_reaches_sink" +family = "untrusted_destination" +evidence = "GHSA-gmxh-hjfv-qc2w: fetchWithHttpClient now wraps CurlHttpClient in NoPrivateNetworkHttpClient so user-supplied scrape URLs cannot reach private networks" + +[[pair]] +name = "elfinderaspnet-extractasync-675049b" +slice = "advisory-fixes" +language = "csharp" +framework = "aspnet" +origin = "github-advisory" +license = "BSD-2-Clause" +repo = "mguinness/elFinder.AspNet" +cve = "CVE-2021-23415 GHSA-pjxv-w3qj-j8m3" +cwe = "CWE-22" +commit = "675049b39284a9e84f0915c71d688da8ebc7d720" +parent = "8f2690712bd6cf417d897d70f734d6e972cf0071" +commit_url = "https://github.com/mguinness/elFinder.AspNet/commit/675049b39284a9e84f0915c71d688da8ebc7d720" +vendored = false +host = "github" +path = "elFinder.AspNet/Drivers/FileSystem/FileSystemDriver.cs" +mode = "name" +relpath = "elFinder.AspNet/Drivers/FileSystem/FileSystemDriver.cs" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2021-07-10" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "elFinder.AspNet/Drivers/FileSystem/FileSystemDriver.cs" +function = "ExtractAsync" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-pjxv-w3qj-j8m3: FileSystemDriver.ExtractAsync resolves each archive entry with Path.GetFullPath and rejects entries outside the destination root (zip slip)" + +[[pair]] +name = "hsweb-framework-createstaticsavepath-8009845" +slice = "advisory-fixes" +language = "java" +framework = "spring" +origin = "github-advisory" +license = "Apache-2.0" +repo = "hs-web/hsweb-framework" +cve = "CVE-2026-11470 GHSA-vjmr-f5fc-vr2w" +cwe = "CWE-22" +commit = "8009845b577d8a2c4bbf4fdd8e8913799a714be6" +parent = "76cb89f56e871c25721a79f2b1eb3b37f4d48570" +commit_url = "https://github.com/hs-web/hsweb-framework/commit/8009845b577d8a2c4bbf4fdd8e8913799a714be6" +vendored = false +host = "github" +path = "hsweb-system/hsweb-system-file/src/main/java/org/hswebframework/web/file/FileUploadProperties.java" +mode = "name" +relpath = "hsweb-system/hsweb-system-file/src/main/java/org/hswebframework/web/file/FileUploadProperties.java" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-01-26" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "hsweb-system/hsweb-system-file/src/main/java/org/hswebframework/web/file/FileUploadProperties.java" +function = "createStaticSavePath" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-vjmr-f5fc-vr2w: createStaticSavePath reduces the upload name to its NFKC-normalised base name, allows original names only when [a-zA-Z0-9._-]+, and normalises the save path" + +[[pair]] +name = "changedetectionio-static-content-9d38b45" +slice = "advisory-fixes" +language = "python" +framework = "flask" +origin = "github-advisory" +license = "Apache-2.0" +repo = "dgtlmoon/changedetection.io" +cve = "CVE-2026-25527 GHSA-9jj8-v89v-xjvw" +cwe = "CWE-22" +commit = "9d38b4517364831889b5b0d7b3465fd060403fd4" +parent = "3558e9ee10cfe19cfe9209e469e43e9327c26735" +commit_url = "https://github.com/dgtlmoon/changedetection.io/commit/9d38b4517364831889b5b0d7b3465fd060403fd4" +vendored = false +host = "github" +path = "changedetectionio/flask_app.py" +mode = "name" +relpath = "changedetectionio/flask_app.py" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-02-16" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "changedetectionio/flask_app.py" +function = "static_content" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-9jj8-v89v-xjvw: static_content tightens group/filename sanitisation to [a-z0-9_] (no dots) and 404s on empty names" + +[[pair]] +name = "rack-check-forbidden-75c5745" +slice = "advisory-fixes" +language = "ruby" +framework = "rack" +origin = "github-advisory" +license = "MIT" +repo = "rack/rack" +cve = "CVE-2026-22860 GHSA-mxw3-3hh2-x2mh" +cwe = "CWE-22" +commit = "75c5745c286637a8f049a33790c71237762069e7" +parent = "f2f225f297b99fbee3d9f51255d41f601fc40aff" +commit_url = "https://github.com/rack/rack/commit/75c5745c286637a8f049a33790c71237762069e7" +vendored = false +host = "github" +path = "lib/rack/directory.rb" +mode = "name" +relpath = "lib/rack/directory.rb" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-02-16" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "lib/rack/directory.rb" +function = "check_forbidden" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-mxw3-3hh2-x2mh: Rack::Directory#check_forbidden compares the expanded path against root plus a trailing separator, closing the sibling-prefix bypass" + +[[pair]] +name = "camaleon-cms-fetch-file-f54a77e" +slice = "advisory-fixes" +language = "ruby" +framework = "rails" +origin = "github-advisory" +license = "MIT" +repo = "owen2345/camaleon-cms" +cve = "CVE-2026-1776 GHSA-jw5g-f64p-6x78" +cwe = "CWE-22" +commit = "f54a77e2a7be601215ea1b396038c589a0cab9af" +parent = "df576a2f80539b847e84768b7cc36f776f1b8dfb" +commit_url = "https://github.com/owen2345/camaleon-cms/commit/f54a77e2a7be601215ea1b396038c589a0cab9af" +vendored = false +host = "github" +path = "app/uploaders/camaleon_cms_aws_uploader.rb" +mode = "name" +relpath = "app/uploaders/camaleon_cms_aws_uploader.rb" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-03-08" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "app/uploaders/camaleon_cms_aws_uploader.rb" +function = "fetch_file" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-jw5g-f64p-6x78: CamaleonCmsAwsUploader#fetch_file now rejects file names failing valid_folder_path? before reading/downloading to the local path" + +[[pair]] +name = "wicket-save-7247098" +slice = "advisory-fixes" +language = "java" +framework = "wicket" +origin = "github-advisory" +license = "Apache-2.0" +repo = "apache/wicket" +cve = "CVE-2026-43975 GHSA-3gmf-p6r4-q8m6" +cwe = "CWE-22" +commit = "72470983f689c61e6a6c0b7388ef955f23bb1e16" +parent = "db0a3dc6e38948f9e87346ddf71f9337fb1b6dbc" +commit_url = "https://github.com/apache/wicket/commit/72470983f689c61e6a6c0b7388ef955f23bb1e16" +vendored = false +host = "github" +path = "wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java" +mode = "name" +relpath = "wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-04-27" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java" +function = "save" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-3gmf-p6r4-q8m6: FolderUploadsFileManager.save resolves the client file name through a containment-checked resolveTargetFile instead of new File(folder, clientFileName)" + +[[pair]] +name = "yii2-renderphpfile-109878b" +slice = "advisory-fixes" +language = "php" +framework = "yii" +origin = "github-advisory" +license = "BSD-3-Clause" +repo = "yiisoft/yii2" +cve = "CVE-2026-39850 GHSA-5vpg-rj7q-qpw2" +cwe = "CWE-98" +commit = "109878b491dbffa541032bc99fb5e26d12cd0375" +parent = "f635d552241c348be8a0020f531a3e278e89b6d9" +commit_url = "https://github.com/yiisoft/yii2/commit/109878b491dbffa541032bc99fb5e26d12cd0375" +vendored = false +host = "github" +path = "framework/base/View.php" +mode = "name" +relpath = "framework/base/View.php" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-05-09" + +[[pair.expected]] +cwe = "CWE-98" +class = "file-inclusion" +path = "framework/base/View.php" +function = "renderPhpFile" +mechanism = "source_reaches_sink" +family = "path" +evidence = "GHSA-5vpg-rj7q-qpw2: renderPhpFile moves extract()+require into an isolated closure so view params can no longer overwrite the included file path" + +[[pair]] +name = "yard-check-static-cache-f78c19f" +slice = "advisory-fixes" +language = "ruby" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "lsegal/yard" +cve = "CVE-2026-49342 GHSA-pxcc-8665-phx8" +cwe = "CWE-22" +commit = "f78c19f0dd33a407085b4ed181bb60c0aa0078b4" +parent = "d29a77cd5fe7ea2be352e4079c93c5dc216ccefe" +commit_url = "https://github.com/lsegal/yard/commit/f78c19f0dd33a407085b4ed181bb60c0aa0078b4" +vendored = false +host = "github" +path = "lib/yard/server/static_caching.rb" +mode = "name" +relpath = "lib/yard/server/static_caching.rb" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-05-25" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "lib/yard/server/static_caching.rb" +function = "check_static_cache" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-pxcc-8665-phx8: check_static_cache builds the cache path through cache_path, which rejects .. segments and drive prefixes and cleans the path, instead of joining request.path raw" + +[[pair]] +name = "thumbor-load-3b986d1" +slice = "advisory-fixes" +language = "python" +framework = "tornado" +origin = "github-advisory" +license = "MIT" +repo = "thumbor/thumbor" +cve = "CVE-2026-53502 GHSA-cj54-hpcc-gj6h" +cwe = "CWE-22" +commit = "3b986d13677b30fe6651c8c72ebb25957ac0a40d" +parent = "471ef279700e9f79941d451acf88abe1601df875" +commit_url = "https://github.com/thumbor/thumbor/commit/3b986d13677b30fe6651c8c72ebb25957ac0a40d" +vendored = false +host = "github" +path = "thumbor/loaders/file_loader.py" +mode = "name" +relpath = "thumbor/loaders/file_loader.py" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-05-30" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "thumbor/loaders/file_loader.py" +function = "load" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-cj54-hpcc-gj6h: load decodes the path before joining under FILE_LOADER_ROOT_PATH and re-checks root containment (commonpath) for the literal fallback" + +[[pair]] +name = "filebrowser-subtitleshandler-f3f4bbe" +slice = "advisory-fixes" +language = "go" +framework = "" +origin = "github-advisory" +license = "Apache-2.0" +repo = "gtsteffaniak/filebrowser" +cve = "CVE-2026-54910 GHSA-vvp7-h4fj-m28w" +cwe = "CWE-22" +commit = "f3f4bbe80cb569d664174aea874d7bfa008c3b5a" +parent = "863f8f64a3d055ac8fb712c0e7bfcf3ac7f173f0" +commit_url = "https://github.com/gtsteffaniak/filebrowser/commit/f3f4bbe80cb569d664174aea874d7bfa008c3b5a" +vendored = false +host = "github" +path = "backend/http/media.go" +mode = "name" +relpath = "backend/http/media.go" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-06-08" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "backend/http/media.go" +function = "subtitlesHandler" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-vvp7-h4fj-m28w: subtitlesHandler only serves a subtitle track found for the video and joins filepath.Base(track.Name) instead of the raw name parameter" + +[[pair]] +name = "laravel-mediable-sanitizepath-6d1e7fb" +slice = "advisory-fixes" +language = "php" +framework = "laravel" +origin = "github-advisory" +license = "MIT" +repo = "plank/laravel-mediable" +cve = "CVE-2026-49970 GHSA-xv8g-76mx-2rxc" +cwe = "CWE-22" +commit = "6d1e7fb39922fdfb3b2d120e13f4eb2e653ae082" +parent = "7e9e3000fa05fe16e678f15bfb51a091e60c2cb8" +commit_url = "https://github.com/plank/laravel-mediable/commit/6d1e7fb39922fdfb3b2d120e13f4eb2e653ae082" +vendored = false +host = "github" +path = "src/Helpers/File.php" +mode = "name" +relpath = "src/Helpers/File.php" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-06-14" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "src/Helpers/File.php" +function = "sanitizePath" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-xv8g-76mx-2rxc: sanitizePath now normalises backslashes and drops . and .. segments instead of only stripping characters" + +[[pair]] +name = "external-workspace-manager-plugin-computecustompath-2fccbbd" +slice = "advisory-fixes" +language = "java" +framework = "jenkins" +origin = "github-advisory" +license = "MIT" +repo = "jenkinsci/external-workspace-manager-plugin" +cve = "CVE-2026-57296 GHSA-hrmj-8fm9-cg8x" +cwe = "CWE-22" +commit = "2fccbbd398cf209f5931b48e006659dbee86ddf2" +parent = "265ef4f624a8fefc976b1656d83d9c1ab1a493ae" +commit_url = "https://github.com/jenkinsci/external-workspace-manager-plugin/commit/2fccbbd398cf209f5931b48e006659dbee86ddf2" +vendored = false +host = "github" +path = "src/main/java/org/jenkinsci/plugins/ewm/steps/ExwsAllocateExecution.java" +mode = "name" +relpath = "src/main/java/org/jenkinsci/plugins/ewm/steps/ExwsAllocateExecution.java" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-06-16" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "src/main/java/org/jenkinsci/plugins/ewm/steps/ExwsAllocateExecution.java" +function = "computeCustomPath" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-hrmj-8fm9-cg8x: computeCustomPath rejects '..' segments in the user-supplied custom workspace path before building the FilePath" + +[[pair]] +name = "openhop-filepath-c8190fb" +slice = "advisory-fixes" +language = "typescript" +framework = "fastify" +origin = "github-advisory" +license = "MIT" +repo = "naorsabag/openhop" +cve = "CVE-2026-59179 GHSA-g72f-jw3w-mgh7" +cwe = "CWE-22" +commit = "c8190fbefa3a50e7b0c16c001d2e05b0e920cfb4" +parent = "6bde78bf88aafa09a39dfaf45978c73d1c5fedc3" +commit_url = "https://github.com/naorsabag/openhop/commit/c8190fbefa3a50e7b0c16c001d2e05b0e920cfb4" +vendored = false +host = "github" +path = "packages/server/src/store.ts" +mode = "name" +relpath = "packages/server/src/store.ts" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-06-20" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "packages/server/src/store.ts" +function = "filePath" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-g72f-jw3w-mgh7: FlowStore.filePath asserts the flow id is valid (no separators or dot segments) before joining it under the flows directory" + +[[pair]] +name = "handlebarsjava-getresource-61f4342" +slice = "advisory-fixes" +language = "java" +framework = "spring" +origin = "github-advisory" +license = "Apache-2.0" +repo = "jknack/handlebars.java" +cve = "CVE-2026-63490 GHSA-g29j-rwfv-h99w" +cwe = "CWE-22" +commit = "61f43423a337b87db5fec1fe59f0725aaaa38df5" +parent = "c95855064f48f1a66af2a5da5a9c1522d7f57264" +commit_url = "https://github.com/jknack/handlebars.java/commit/61f43423a337b87db5fec1fe59f0725aaaa38df5" +vendored = false +host = "github" +path = "handlebars-springmvc/src/main/java/com/github/jknack/handlebars/springmvc/SpringTemplateLoader.java" +mode = "name" +relpath = "handlebars-springmvc/src/main/java/com/github/jknack/handlebars/springmvc/SpringTemplateLoader.java" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-06-28" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "handlebars-springmvc/src/main/java/com/github/jknack/handlebars/springmvc/SpringTemplateLoader.java" +function = "getResource" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-g29j-rwfv-h99w: SpringTemplateLoader.getResource normalises the location and rejects paths escaping the configured prefix, then rejects URLs with query/fragment components" + +[[pair]] +name = "fastify-static-pumpsendtoreply-db4276f" +slice = "advisory-fixes" +language = "javascript" +framework = "fastify" +origin = "github-advisory" +license = "MIT" +repo = "fastify/fastify-static" +cve = "CVE-2026-15074 GHSA-83w8-p2f5-377r" +cwe = "CWE-22" +commit = "db4276f846ba56b21f93768cd6636ee5e2fc58b1" +parent = "c68224e3067e18ee4c676cc9db66aae6b30ffb02" +commit_url = "https://github.com/fastify/fastify-static/commit/db4276f846ba56b21f93768cd6636ee5e2fc58b1" +vendored = false +host = "github" +path = "index.js" +mode = "name" +relpath = "index.js" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-07-22" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "index.js" +function = "pumpSendToReply" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-83w8-p2f5-377r: pumpSendToReply now rejects non-leading .. segments with 403 before the allowedPath route guard and send()" + +[[pair]] +name = "goshs-upload-f3ef599" +slice = "advisory-fixes" +language = "go" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "goshs-labs/goshs" +cve = "CVE-2026-66064 GHSA-964w-f6gj-5236" +cwe = "CWE-22" +commit = "f3ef599e409151d1380866e47de8b1afb0bb54fa" +parent = "ddd2eb9470237e68f0d6c828dd75173708e60277" +commit_url = "https://github.com/goshs-labs/goshs/commit/f3ef599e409151d1380866e47de8b1afb0bb54fa" +vendored = false +host = "github" +path = "httpserver/updown.go" +mode = "name" +relpath = "httpserver/updown.go" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-07-27" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "httpserver/updown.go" +function = "upload" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-964w-f6gj-5236: FileServer.upload rejects empty/./.. filenames and checks the joined destination stays inside targetDir via sanitizePath" + +[[pair]] +name = "knowns-get-09c5a96" +slice = "advisory-fixes" +language = "go" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "knowns-dev/knowns" +cve = "CVE-2026-86439 GHSA-9gfj-28hw-jchp" +cwe = "CWE-22" +commit = "09c5a96fd5817b941dc86669278c1a17db10ed4e" +parent = "d3989829fb5095666d23d005b2f78a082832a396" +commit_url = "https://github.com/knowns-dev/knowns/commit/09c5a96fd5817b941dc86669278c1a17db10ed4e" +vendored = false +host = "github" +path = "internal/storage/doc_store.go" +mode = "name" +relpath = "internal/storage/doc_store.go" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2026-08-15" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "internal/storage/doc_store.go" +function = "Get" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-9gfj-28hw-jchp: DocStore.Get resolves the doc path through resolveDocPath/safepath.Resolve (containment) instead of a raw filepath.Join" + +[[pair]] +name = "webpack-dev-middleware-getfilenamefromurl-13344e7" +slice = "advisory-fixes" +language = "javascript" +framework = "express" +origin = "github-advisory" +license = "MIT" +repo = "webpack/webpack-dev-middleware" +cve = "CVE-2026-76844 GHSA-g84c-rxfj-3j2c" +cwe = "CWE-22" +commit = "13344e78b26d88fe9b7c8381a3860e248a52359b" +parent = "e58a70ba9c36f6acc10617f338b73269f951de35" +commit_url = "https://github.com/webpack/webpack-dev-middleware/commit/13344e78b26d88fe9b7c8381a3860e248a52359b" +vendored = false +host = "github" +path = "src/middleware.js" +mode = "name" +relpath = "src/middleware.js" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-09-03" + +[[pair.expected]] +cwe = "CWE-22" +class = "path-traversal" +path = "src/middleware.js" +function = "getFilenameFromUrl" +mechanism = "path_join_user_input" +family = "path" +evidence = "GHSA-g84c-rxfj-3j2c: getFilenameFromUrl verifies path.relative(outputPath, filename) stays inside outputPath after the publicPath prefix is stripped" + +[[pair]] +name = "template-load-28cff3a" +slice = "advisory-fixes" +language = "go" +framework = "fiber" +origin = "github-advisory" +license = "MIT" +repo = "gofiber/template" +cve = "CVE-2024-22199 GHSA-4mq2-gc4j-cmw6" +cwe = "CWE-79" +commit = "28cff3ac4d4c117ab25b5396954676d624b6cb46" +parent = "fd5a1ab847ebbf537447561827f310ba70fd0998" +commit_url = "https://github.com/gofiber/template/commit/28cff3ac4d4c117ab25b5396954676d624b6cb46" +vendored = false +host = "github" +path = "django/django.go" +mode = "name" +relpath = "django/django.go" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2024-01-11" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "django/django.go" +function = "Load" +mechanism = "other" +family = "output_encoding" +evidence = "GHSA-4mq2-gc4j-cmw6: the Django engine Load() stops forcing pongo2.SetAutoescape(false) and enables autoescaping by default, so template variables are HTML-escaped" + +[[pair]] +name = "zeppelin-fromjson-8368579" +slice = "advisory-fixes" +language = "java" +framework = "" +origin = "github-advisory" +license = "Apache-2.0" +repo = "apache/zeppelin" +cve = "CVE-2024-31868 GHSA-rrvf-5w4r-3x7v" +cwe = "CWE-79" +commit = "83685795e0ec8d3059fd7a3dbcae5c0532b63b79" +parent = "ccf1a992da3556262b0a2d571f8b823a3b33c242" +commit_url = "https://github.com/apache/zeppelin/commit/83685795e0ec8d3059fd7a3dbcae5c0532b63b79" +vendored = false +host = "github" +path = "zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java" +mode = "name" +relpath = "zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2024-03-10" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java" +function = "fromJson" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-rrvf-5w4r-3x7v: fromJson now HTML-escapes every string field of the deserialised Helium package (escapeHtml4 via preventXss) before it reaches the UI" + +[[pair]] +name = "moonshine-resolvepreview-f108f4e" +slice = "advisory-fixes" +language = "php" +framework = "laravel" +origin = "github-advisory" +license = "MIT" +repo = "moonshine-software/moonshine" +cve = "CVE-2025-51487 GHSA-p632-58pp-c9xg" +cwe = "CWE-79" +commit = "f108f4ea5c0d66e8172c2dc397c1d9a6f466d9c6" +parent = "959e99397fbd3c681cb27afdddcd7fa338248b03" +commit_url = "https://github.com/moonshine-software/moonshine/commit/f108f4ea5c0d66e8172c2dc397c1d9a6f466d9c6" +vendored = false +host = "github" +path = "src/Laravel/src/Fields/Relationships/BelongsTo.php" +mode = "name" +relpath = "src/Laravel/src/Fields/Relationships/BelongsTo.php" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2025-05-14" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "src/Laravel/src/Fields/Relationships/BelongsTo.php" +function = "resolvePreview" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-p632-58pp-c9xg: BelongsTo::resolvePreview now passes the related record preview through escapeValue unless the field is explicitly unescaped" + +[[pair]] +name = "chrome-construct-34b2b8d" +slice = "advisory-fixes" +language = "php" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "chrome-php/chrome" +cve = "CVE-2025-48883 GHSA-3432-fmrf-7vmh" +cwe = "CWE-79" +commit = "34b2b8d1691f4e3940b1e1e95d388fffe81169c8" +parent = "e3bd5857914a885e768293fdda135298f866034b" +commit_url = "https://github.com/chrome-php/chrome/commit/34b2b8d1691f4e3940b1e1e95d388fffe81169c8" +vendored = false +host = "github" +path = "src/Dom/Selector/CssSelector.php" +mode = "name" +relpath = "src/Dom/Selector/CssSelector.php" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2025-05-28" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "src/Dom/Selector/CssSelector.php" +function = "__construct" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-3432-fmrf-7vmh: the CssSelector constructor now json_encodes the selector expression so it is a safe JS string literal before it is formatted into querySelectorAll scripts" + +[[pair]] +name = "prosemirror-to-html-render-opening-tag-4d59f94" +slice = "advisory-fixes" +language = "ruby" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "etaminstudio/prosemirror_to_html" +cve = "CVE-2025-64501 GHSA-52c5-vh7f-26fx" +cwe = "CWE-79" +commit = "4d59f94f550bcabeec30d298791bbdd883298ad8" +parent = "0797c1555aee010606c82f873489716a001903f0" +commit_url = "https://github.com/etaminstudio/prosemirror_to_html/commit/4d59f94f550bcabeec30d298791bbdd883298ad8" +vendored = false +host = "github" +path = "lib/prosemirror_to_html.rb" +mode = "name" +relpath = "lib/prosemirror_to_html.rb" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2025-11-05" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "lib/prosemirror_to_html.rb" +function = "render_opening_tag" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-52c5-vh7f-26fx: render_opening_tag now CGI.escapeHTML-escapes each node attribute value before writing it into the opening tag" + +[[pair]] +name = "algernon-htmllink-cd88320" +slice = "advisory-fixes" +language = "go" +framework = "" +origin = "github-advisory" +license = "BSD-3-Clause" +repo = "xyproto/algernon" +cve = "CVE-2025-65754 GHSA-8jqm-8qm3-qgqm" +cwe = "CWE-79" +commit = "cd8832014a624a9aeab60566434c3344135e23f8" +parent = "ae7772017187d65ddb026c562853013d47de3acc" +commit_url = "https://github.com/xyproto/algernon/commit/cd8832014a624a9aeab60566434c3344135e23f8" +vendored = false +host = "github" +path = "themes/html.go" +mode = "name" +relpath = "themes/html.go" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2025-11-13" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "themes/html.go" +function = "HTMLLink" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-8jqm-8qm3-qgqm: HTMLLink now sanitizes the filename-derived link text and URL with a bluemonday UGC policy before building the directory-listing anchor" + +[[pair]] +name = "tornado-write-error-9c163ae" +slice = "advisory-fixes" +language = "python" +framework = "tornado" +origin = "github-advisory" +license = "Apache-2.0" +repo = "tornadoweb/tornado" +cve = "CVE-2025-67724 GHSA-pr2v-jx2c-wg9f" +cwe = "CWE-79" +commit = "9c163aebeaad9e6e7d28bac1f33580eb00b0e421" +parent = "771472cfdaeebc0d89a9cc46e249f8891a6b29cd" +commit_url = "https://github.com/tornadoweb/tornado/commit/9c163aebeaad9e6e7d28bac1f33580eb00b0e421" +vendored = false +host = "github" +path = "tornado/web.py" +mode = "name" +relpath = "tornado/web.py" +provenance = "human" +split = "train" +review_tier = "advisory" +fix_date = "2025-12-10" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "tornado/web.py" +function = "write_error" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-pr2v-jx2c-wg9f: write_error now passes the attacker-influenced reason phrase through escape.xhtml_escape before formatting it into the HTML error page" + +[[pair]] +name = "htmlsanitizer-dosanitize-0ac53dc" +slice = "advisory-fixes" +language = "csharp" +framework = "" +origin = "github-advisory" +license = "MIT" +repo = "mganss/HtmlSanitizer" +cve = "CVE-2026-25543 GHSA-j92c-7v7g-gj3f" +cwe = "CWE-79" +commit = "0ac53dca30ddad963f2b243669a5066933d82b81" +parent = "ca3931ae6ef305ece676846b99cdc4dd1cae377c" +commit_url = "https://github.com/mganss/HtmlSanitizer/commit/0ac53dca30ddad963f2b243669a5066933d82b81" +vendored = false +host = "github" +path = "src/HtmlSanitizer/HtmlSanitizer.cs" +mode = "name" +relpath = "src/HtmlSanitizer/HtmlSanitizer.cs" +provenance = "human" +split = "holdout" +review_tier = "advisory" +fix_date = "2026-02-02" + +[[pair.expected]] +cwe = "CWE-79" +class = "xss" +path = "src/HtmlSanitizer/HtmlSanitizer.cs" +function = "DoSanitize" +mechanism = "source_reaches_sink" +family = "output_encoding" +evidence = "GHSA-j92c-7v7g-gj3f: DoSanitize now recurses into