mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
spec(pre-push-safety-net): reserve the independent population v2 (17 cases, unscanned)
v1 was evaluated and then used to retune the analyzer. A retune can only be qualified on a population that did not inform it. The candidates came from web research alone (research-2026-09-26.md): - advisories were taken newest-first per language under fixed criteria, never by how easy they look; - 261 advisories were examined and 224 rejected, each with a reason; - every SHA pair was verified through the GitHub API; - no clone and no scan. The maintainer selected the rule-based "balanced" set: 5 Python, 6 JS/TS, and 6 PHP of which 4 are WordPress plugins; 10 injection, 6 destination and 1 config. Each case declares the advisory's sink sites before any scan, for protocol v2. freeze.py takes --population and writes freeze-vN.json. The reservation tests now cover every population-v*.toml. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d14fd5e960
commit
c64e37edbd
@@ -10,8 +10,8 @@ The benign change is a PROPOSAL for review, chosen mechanically: the first commi
|
||||
about security in its message, and stays small. A reviewer confirms or replaces it before the status becomes
|
||||
`frozen`.
|
||||
|
||||
Usage: python benchmarks/independent/freeze.py [--cache DIR] [--only ID,...]
|
||||
Writes: benchmarks/independent/freeze-v1.json
|
||||
Usage: python benchmarks/independent/freeze.py [--population population-vN.toml] [--cache DIR] [--only ID,...]
|
||||
Writes: benchmarks/independent/freeze-vN.json (v1 by default)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -26,7 +26,13 @@ from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
POPULATION = HERE / "population-v1.toml"
|
||||
OUT = HERE / "freeze-v1.json"
|
||||
|
||||
|
||||
def freeze_record(population: Path) -> Path:
|
||||
"""`population-v2.toml` -> `freeze-v2.json`, next to it."""
|
||||
return population.with_name("freeze-" + population.stem.removeprefix("population-") + ".json")
|
||||
|
||||
|
||||
EXTENSIONS = {
|
||||
"php": (".php",),
|
||||
"javascript": (".js", ".mjs", ".cjs", ".jsx"),
|
||||
@@ -145,8 +151,10 @@ def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("--cache", type=Path, default=Path.home() / ".cache" / "openultrasast" / "independent")
|
||||
parser.add_argument("--only", default="")
|
||||
parser.add_argument("--population", type=Path, default=POPULATION, help="a population-vN.toml in this directory")
|
||||
args = parser.parse_args()
|
||||
cases = tomllib.loads(POPULATION.read_text())["case"]
|
||||
population = args.population if args.population.is_absolute() else HERE / args.population.name
|
||||
cases = tomllib.loads(population.read_text())["case"]
|
||||
only = {c.strip() for c in args.only.split(",") if c.strip()}
|
||||
records = []
|
||||
for case in cases:
|
||||
@@ -163,7 +171,9 @@ def main() -> int:
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
OUT.write_text(json.dumps({"schema_version": 1, "population": POPULATION.name, "cases": records}, indent=2) + "\n")
|
||||
freeze_record(population).write_text(
|
||||
json.dumps({"schema_version": 1, "population": population.name, "cases": records}, indent=2) + "\n"
|
||||
)
|
||||
return 0 if all(not r["problems"] for r in records) else 1
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
# Independent evaluation population v2 (milestone M4). Reserved 2026-09-26.
|
||||
#
|
||||
# RESERVED, NOT SCANNED. Chosen by web research alone (research-2026-09-26.md): advisories taken newest-first per
|
||||
# language under fixed criteria, never by how easy a case looks for this analyzer; 261 advisories examined, 224
|
||||
# rejected with reasons. No repository was cloned, no scanner or project tooling run on any of them, and none is
|
||||
# referenced anywhere in this project's development tree (checked against every github.com URL under benchmarks/,
|
||||
# src/, tests/ and .kiro/, which includes the v1 population).
|
||||
#
|
||||
# Why a v2: v1 (2026-09-24) was evaluated and then used to retune the analyzer (file-owned gaps, Node vm sinks,
|
||||
# Django sources, quoted-only SQL escapes, guards, .gitignore semantics). A retune cannot be qualified on the
|
||||
# population that informed it.
|
||||
#
|
||||
# Rules for this file are v1's: no scan, trace, probe or census of these repositories before the population is
|
||||
# FROZEN and protocol-v2.md is committed; no rule, fact or query change may cite these cases before their
|
||||
# evaluation is recorded.
|
||||
#
|
||||
# `sites` are the sink or execution locations the ADVISORY names, declared here before any scan. Protocol v2
|
||||
# credits a finding at one of them as well as one matching the fix's changed lines.
|
||||
#
|
||||
# Selected by the maintainer on 2026-09-26 ("balanced" rule-based selection, 17 cases -- the menu called it 16; the
|
||||
# list it showed held 17). Licences marked "verify" must be checked before any result is published.
|
||||
|
||||
schema_version = 1
|
||||
status = "reserved-unscanned"
|
||||
reserved_on = "2026-09-26"
|
||||
selected_by = "maintainer"
|
||||
research = "research-2026-09-26.md"
|
||||
protocol = "protocol-v2.md"
|
||||
multi_language_members = ["wp-gopay-log-filter-sqli", "wp-user-registration-login-redirect", "wp-directorist-avatar-ssrf", "wp-groundhogg-confirm-redirect"]
|
||||
also_reserved = ["population-v1.toml", "../push/ghost-reserved.toml"]
|
||||
|
||||
[[case]]
|
||||
id = "pgadmin-maintenance-sqli"
|
||||
repo = "https://github.com/pgadmin-org/pgadmin4"
|
||||
language = "python"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-7815", "GHSA-hp84-p2gq-6fvr"]
|
||||
vulnerable = "d112dc3b962218efc3613fa727d54f0fc5b1d527"
|
||||
fixed = "cf53953d9ad66c182a08e662c1ba29c5831b486b"
|
||||
ancestry = "parent"
|
||||
sink = "web/pgadmin/tools/maintenance/templates/maintenance/sql/command.sql (string concatenation of vacuum_index_cleanup, vacuum_parallel, buffer_usage_limit, reindex_tablespace) rendered in web/pgadmin/tools/maintenance/__init__.py :: create_maintenance_job() → render_template(...) → BatchProcess(cmd=psql, args=[..., '--command', query])"
|
||||
source = "JSON body of POST /maintenance/job/<sid>/<did>"
|
||||
sites = ["web/pgadmin/tools/maintenance/__init__.py::create_maintenance_job"]
|
||||
privilege = "authenticated (tools_maintenance permission)"
|
||||
license = "PostgreSQL License (OSI; GitHub: NOASSERTION) [verify before publishing]"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "qwed-sympy-eval"
|
||||
repo = "https://github.com/QWED-AI/qwed-verification"
|
||||
language = "python"
|
||||
also = []
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-55585", "GHSA-q27q-98j4-9pfv"]
|
||||
vulnerable = "06801f68cbf9c9b5e54cc17f2a61cb2792da7a26"
|
||||
fixed = "6066b68c0c4f4cc2c3771824822aaa864d082ef8"
|
||||
ancestry = "first parent (fixed is the merge of PR #200; the PR commit `dc9d4db72ca4b4ae3f96d0e6a0c27a9e38a06f61` is a direct child of the vulnerable SHA)"
|
||||
sink = "src/qwed_new/api/main.py :: verify_math() → sympy.parsing.sympy_parser.parse_expr(expression_normalized) (also left_str/right_str); second path src/qwed_new/core/batch.py :: _verify_item() → parse_expr(item.query)"
|
||||
source = "POST /verify/math JSON expression; POST /verify/batch item query"
|
||||
sites = ["src/qwed_new/api/main.py::verify_math", "src/qwed_new/core/batch.py::_verify_item"]
|
||||
privilege = "authenticated tenant (API key; self-signup /auth/signup is on by default)"
|
||||
license = "Apache-2.0"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "contextforge-prompt-ssti"
|
||||
repo = "https://github.com/IBM/mcp-context-forge"
|
||||
language = "python"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["GHSA-vwf3-4xxj-qg6h"]
|
||||
vulnerable = "0f8e0ad679f1fff685f486e17f642ac863c8ca9e"
|
||||
fixed = "4d31004661858a2e99055b3c0c9f14218b8f7120"
|
||||
ancestry = "parent"
|
||||
sink = "mcpgateway/services/prompt_service.py :: PromptService._render_template() → _compile_jinja_template() → _get_jinja_env().from_string(template).render() (plain jinja2.Environment)"
|
||||
source = "prompt template stored through register_prompt (prompts create/update API), rendered on prompt get"
|
||||
sites = ["mcpgateway/services/prompt_service.py::_render_template", "mcpgateway/services/prompt_service.py::_compile_jinja_template"]
|
||||
privilege = "authenticated (permission to create or update prompts)"
|
||||
license = "Apache-2.0"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "lmdeploy-media-url-ssrf"
|
||||
repo = "https://github.com/InternLM/lmdeploy"
|
||||
language = "python"
|
||||
also = []
|
||||
family = "untrusted_destination"
|
||||
advisory = ["GHSA-39wr-7q6h-cf68"]
|
||||
vulnerable = "71648d239c978452720a6d6692fe914efc8a7b94"
|
||||
fixed = "03c313006d17cc3feae86b633c44206a997c44db"
|
||||
ancestry = "parent"
|
||||
sink = "lmdeploy/vl/media/connection.py :: _load_http_url() → requests.Session().get(url_spec.geturl(), allow_redirects=True), gated by _is_safe_url() (the urlparse host differs from the requests host)"
|
||||
source = "image_url/video/audio URLs in POST /v1/chat/completions → serve/processors/multimodal.py :: MultimodalProcessor._parse_multimodal_item() → load_from_url()"
|
||||
sites = ["lmdeploy/vl/media/connection.py::_load_http_url"]
|
||||
privilege = "unauthenticated by default (api_keys is optional, default None)"
|
||||
license = "Apache-2.0"
|
||||
confidence = "medium"
|
||||
|
||||
[[case]]
|
||||
id = "winml-cli-serve-cors"
|
||||
repo = "https://github.com/microsoft/winml-cli"
|
||||
language = "python"
|
||||
also = []
|
||||
family = "config_secrets"
|
||||
advisory = ["CVE-2026-84452", "GHSA-96p9-rh4f-92cf"]
|
||||
vulnerable = "02179d185f0e3ac23518236b5ca1f61fd37ef680"
|
||||
fixed = "f4073e0ef4700a25b623487e7e45c421ca0b9993"
|
||||
ancestry = "parent"
|
||||
sink = "src/winml/modelkit/serve/cli_api.py :: module level app.add_middleware(CORSMiddleware, allow_origins=[\"*\"], ...) (same in serve/app.py :: create_app())"
|
||||
source = "any web page in the victim's browser calling POST /v1/cli/{command} on 127.0.0.1"
|
||||
sites = ["src/winml/modelkit/serve/cli_api.py::<global>", "src/winml/modelkit/serve/app.py::create_app"]
|
||||
privilege = "unauthenticated (cross-origin from any website)"
|
||||
license = "MIT"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "reactpress-findall-column-sqli"
|
||||
repo = "https://github.com/fecommunity/reactpress"
|
||||
language = "typescript"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-61685", "GHSA-wmw4-mw6x-6vfm"]
|
||||
vulnerable = "c3dd10681b3d2b5e224f7ac647b3769557603975"
|
||||
fixed = "78ecb70af1c021455c05fdcbe137212c70e310d6"
|
||||
ancestry = "parent"
|
||||
sink = "server/src/modules/article/article.service.ts :: ArticleService.findAll() (query-string key interpolated as article.${key} into andWhere); same pattern in comment/file/page/knowledge services"
|
||||
source = "@Query() queryParams in ArticleController.findAll() (GET /api/article)"
|
||||
sites = ["server/src/modules/article/article.service.ts::findAll"]
|
||||
privilege = "unauthenticated"
|
||||
license = "MIT"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "fuxa-tdengine-escape-sqli"
|
||||
repo = "https://github.com/frangoteam/FUXA"
|
||||
language = "javascript"
|
||||
also = ["typescript"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-47720", "GHSA-h9fj-c2qr-76g2"]
|
||||
vulnerable = "193a888f8e3f805325072829b1644a2c768c948b"
|
||||
fixed = "b78f6159d02a56ce5ff48207feb936afb3534fc8"
|
||||
ancestry = "parent"
|
||||
sink = "server/runtime/storage/tdengine/index.js :: TDengine.getDaqValue() (escapeTdString(tagid) doubles quotes but not backslashes, then SQL string for cursor.query)"
|
||||
source = "sids of GET /api/daq (server/api/daq/index.js) or Socket.IO DAQ_QUERY"
|
||||
sites = ["server/runtime/storage/tdengine/index.js::getDaqValue"]
|
||||
privilege = "unauthenticated (guest level; socket handler has no auth check)"
|
||||
license = "MIT"
|
||||
confidence = "medium"
|
||||
|
||||
[[case]]
|
||||
id = "budibase-mongo-template-nosqli"
|
||||
repo = "https://github.com/Budibase/budibase"
|
||||
language = "typescript"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["GHSA-pmpg-2mxq-6xwr", "GHSA-qw6m-8fw2-2v64"]
|
||||
vulnerable = "b7e63df737eede85d951aa9665b0c0778ece7ccb"
|
||||
fixed = "dd8c0654b35cd89ce3645f2355f4bf2ff9a5dd80"
|
||||
ancestry = "parent"
|
||||
sink = "packages/server/src/sdk/workspace/queries/queries.ts :: enrichContext() (handlebars processStringSync(..., {noEscaping: true}) into the json template, then JSON.parse) → packages/server/src/integrations/mongodb.ts :: MongoIntegration.read() (collection.find(json))"
|
||||
source = "query parameters / bindings (e.g. currentUser.email, request parameters) of POST /api/v2/queries/:queryId (packages/server/src/api/controllers/query/index.ts :: executeV2)"
|
||||
sites = ["packages/server/src/integrations/mongodb.ts::read", "packages/server/src/sdk/workspace/queries/queries.ts::enrichContext"]
|
||||
privilege = "authenticated app user (BASIC role)"
|
||||
license = "GPL-3.0 (root LICENSE says \"GPLv3 overall\"; server package.json GPL-3.0; GitHub NOASSERTION)"
|
||||
confidence = "medium"
|
||||
|
||||
[[case]]
|
||||
id = "unleash-addon-webhook-ssrf"
|
||||
repo = "https://github.com/Unleash/unleash"
|
||||
language = "typescript"
|
||||
also = ["javascript"]
|
||||
family = "untrusted_destination"
|
||||
advisory = ["CVE-2026-63004", "GHSA-5vf6-jrqr-78fj"]
|
||||
vulnerable = "b9f3217d327f24094b4352130baa30387f85cffe"
|
||||
fixed = "2100db76af3473f13e6fb40096cf17a9c2b741a1"
|
||||
ancestry = "parent"
|
||||
sink = "src/lib/addons/addon.ts :: Addon.fetchRetry() (ky(url, ...)), called from webhook.ts / slack / teams / datadog / new-relic addons"
|
||||
source = "addon parameters.url saved through the admin addons API (CREATE_ADDON/UPDATE_ADDON), fired on feature events"
|
||||
sites = ["src/lib/addons/addon.ts::fetchRetry"]
|
||||
privilege = "authenticated (CREATE_ADDON/UPDATE_ADDON root permission)"
|
||||
license = "AGPL-3.0"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "medplum-external-auth-open-redirect"
|
||||
repo = "https://github.com/medplum/medplum"
|
||||
language = "typescript"
|
||||
also = ["javascript"]
|
||||
family = "untrusted_destination"
|
||||
advisory = ["CVE-2026-53728", "GHSA-m44r-7c5h-m6mj"]
|
||||
vulnerable = "5d4a8654b02aa67c444f96ae1eb5663c09854006"
|
||||
fixed = "7ae10035ddadde4dba7b18d3156553940465b3a1"
|
||||
ancestry = "parent"
|
||||
sink = "packages/server/src/auth/external.ts :: externalCallbackHandler() (res.redirect(redirectUrl) with URI accepted by getClientRedirectUri(client, body.redirectUri, true) prefix match in packages/server/src/oauth/clients.ts)"
|
||||
source = "redirectUri inside JSON state query parameter of GET /auth/external"
|
||||
sites = ["packages/server/src/auth/external.ts::externalCallbackHandler"]
|
||||
privilege = "unauthenticated (victim completes external IdP login)"
|
||||
license = "Apache-2.0"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "cct-studio-server-cmdi"
|
||||
repo = "https://github.com/davila7/claude-code-templates"
|
||||
language = "javascript"
|
||||
also = ["typescript", "python"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-73222", "GHSA-79wm-x847-7cvg"]
|
||||
vulnerable = "acdb5893412a81eaec9862ee24a4613bf296ab45"
|
||||
fixed = "bc4618b07232633c1c0aac12a43e436268d31783"
|
||||
ancestry = "parent"
|
||||
sink = "cli-tool/src/sandbox-server.js :: executeLocalTask() (spawn('claude', [finalPrompt], {shell: true})), route handler POST /api/install-agent and checkAndInstallAgent() (spawn('npx', [..., agentName, ...], {shell: true}))"
|
||||
source = "req.body.prompt, req.body.agentName, req.body.agent"
|
||||
sites = ["cli-tool/src/sandbox-server.js::executeLocalTask", "cli-tool/src/sandbox-server.js::checkAndInstallAgent"]
|
||||
privilege = "unauthenticated (server on 0.0.0.0, wildcard CORS)"
|
||||
license = "MIT"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "wp-gopay-log-filter-sqli"
|
||||
repo = "https://github.com/gopaycommunity/gopay-woocommerce-integration"
|
||||
language = "php"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-75959", "GHSA-88c4-2f34-57h5"]
|
||||
vulnerable = "626bc8b5f8d33de6eefd921e3a05ed52ac320bcd"
|
||||
fixed = "7190c985370f9758f226c2f2addd79532a2f93d5"
|
||||
ancestry = "parent"
|
||||
sink = "admin/views/log.php :: file scope (included by admin/class-gopay-gateway-admin.php), two $wpdb->get_results(sprintf(\"... REGEXP '[\\w\\W]*%s[\\w\\W]*' ...\", ..., strtoupper($log_table_filter)))"
|
||||
source = "filter_input(INPUT_GET, 'log_table_filter')"
|
||||
sites = ["admin/views/log.php::<global>"]
|
||||
privilege = "admin (shop manager, manage_woocommerce)"
|
||||
license = "GitHub: none; plugin header GPLv2 or later [verify before publishing]"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "wp-user-registration-login-redirect"
|
||||
repo = "https://github.com/themegrill/user-registration"
|
||||
language = "php"
|
||||
also = ["javascript"]
|
||||
family = "untrusted_destination"
|
||||
advisory = ["CVE-2026-80072", "GHSA-w78c-fx7q-9x8m"]
|
||||
vulnerable = "e52cf151cb229852e6209fb3a627aa51485139c1"
|
||||
fixed = "75c940dab15fd54d5a3fbb9f2213767b89467b08"
|
||||
ancestry = "parent"
|
||||
sink = "includes/functions-ur-core.php :: ur_process_login() (wp_redirect(wp_validate_redirect($redirect, $redirect)), and wp_send_json_success(['message'=>$redirect]) on the AJAX path)"
|
||||
source = "$post['redirect'] (login form POST) or wp_get_raw_referer()"
|
||||
sites = ["includes/functions-ur-core.php::ur_process_login"]
|
||||
privilege = "unauthenticated attacker, victim logs in"
|
||||
license = "GitHub: NOASSERTION; repo has license.txt, plugin header GPLv3 [verify before publishing]"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "wp-directorist-avatar-ssrf"
|
||||
repo = "https://github.com/sovware/directorist"
|
||||
language = "php"
|
||||
also = ["javascript"]
|
||||
family = "untrusted_destination"
|
||||
advisory = ["CVE-2026-84046", "GHSA-6q63-3w56-m722"]
|
||||
vulnerable = "77cfc9bc8b453f97ab7e720330706fa4ef056867"
|
||||
fixed = "1a67825d5cf6e7c768c3a692da1d18bae732bea9"
|
||||
ancestry = "parent"
|
||||
sink = "includes/rest-api/functions.php :: directorist_rest_upload_image_from_url() (download_url($image_url))"
|
||||
source = "$request['avater']['src'] in includes/rest-api/Version1/class-users-controller.php :: update_user_meta_fields() (REST user update)"
|
||||
sites = ["includes/rest-api/functions.php::directorist_rest_upload_image_from_url"]
|
||||
privilege = "authenticated (subscriber)"
|
||||
license = "GitHub: NOASSERTION; plugin header GPLv3 [verify before publishing]"
|
||||
confidence = "medium"
|
||||
|
||||
[[case]]
|
||||
id = "wp-groundhogg-confirm-redirect"
|
||||
repo = "https://github.com/groundhoggwp/groundhogg"
|
||||
language = "php"
|
||||
also = ["javascript"]
|
||||
family = "untrusted_destination"
|
||||
advisory = ["CVE-2026-81741", "GHSA-m96f-c5vh-h7cm"]
|
||||
vulnerable = "9472a1dd79d4556c40eece501a61930793f170d3"
|
||||
fixed = "028247d4b8ec9ba051d3eb48f1466a759bc16bfd"
|
||||
ancestry = "parent (follow-up is the direct child of the fix)"
|
||||
sink = "templates/preferences.php :: file scope, case 'confirm': (wp_redirect($redirect_to))"
|
||||
source = "get_url_var('redirect_to') (query string, only through esc_url_raw(sanitize_text_field(...)))"
|
||||
sites = ["templates/preferences.php::<global>"]
|
||||
privilege = "unauthenticated (crafted preferences link)"
|
||||
license = "GitHub: none, no LICENSE file; plugin header GPLv3 [verify before publishing]"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "froxlor-admin-ip-second-order-sqli"
|
||||
repo = "https://github.com/froxlor/froxlor"
|
||||
language = "php"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-54348", "GHSA-w27m-rmmf-g5w4"]
|
||||
vulnerable = "9bf03de643a2edee4642bfc6c94db1f05253da02"
|
||||
fixed = "a1eaca5a1601c8a30e00814a4fc73ad0c185f89e"
|
||||
ancestry = "parent (ancestor of tag 2.3.8)"
|
||||
sink = "lib/Froxlor/Api/Commands/IpsAndPorts.php :: IpsAndPorts::listing() / listingCount() (implode(\", \", json_decode($this->getUserDetail('ip'), true)) into WHERE id IN (...)); also lib/Froxlor/Api/Commands/Domains.php :: validateIpAddresses()"
|
||||
source = "ipaddress API parameter of Admins.add / Admins.update (lib/Froxlor/Api/Commands/Admins.php), stored as JSON in panel_admins.ip, read back via getUserDetail('ip')"
|
||||
sites = ["lib/Froxlor/Api/Commands/IpsAndPorts.php::listing", "lib/Froxlor/Api/Commands/IpsAndPorts.php::listingCount", "lib/Froxlor/Api/Commands/Domains.php::validateIpAddresses"]
|
||||
privilege = "admin (API key with change_serversettings)"
|
||||
license = "LGPL-2.1 (per GitHub)"
|
||||
confidence = "high"
|
||||
|
||||
[[case]]
|
||||
id = "sulu-smart-content-sql2-injection"
|
||||
repo = "https://github.com/sulu/sulu"
|
||||
language = "php"
|
||||
also = ["javascript"]
|
||||
family = "injection"
|
||||
advisory = ["CVE-2026-92692", "GHSA-jg26-q8hg-3pq4"]
|
||||
vulnerable = "d061094f5b7bb1d5e974544fce30bede9c7adf8e"
|
||||
fixed = "d19c01487af8c3de2fb3aa145856707a6367392c"
|
||||
ancestry = "parent (2.6 branch; ancestor of tag 2.6.25)"
|
||||
sink = "src/Sulu/Component/Content/SmartContent/QueryBuilder.php :: QueryBuilder::buildCategoriesWhere() (also buildTagsWhere(), buildAudienceTargeting()), string-concatenated into the SQL2 WHERE clause built by buildWhere()"
|
||||
source = "?categories= query parameter, read in src/Sulu/Component/Category/Request/CategoryRequestHandler.php :: getCategories() (explode + trim only), passed by src/Sulu/Component/SmartContent/ContentType.php as websiteCategories"
|
||||
sites = ["src/Sulu/Component/Content/SmartContent/QueryBuilder.php::buildCategoriesWhere", "src/Sulu/Component/Content/SmartContent/QueryBuilder.php::buildWhere"]
|
||||
privilege = "unauthenticated (any public page with a category-filtered Smart Content block)"
|
||||
license = "MIT"
|
||||
confidence = "high"
|
||||
@@ -0,0 +1,658 @@
|
||||
# Independent evaluation population v2: candidate real-world cases
|
||||
|
||||
Research date: 2026-09-26. Method: web research only. I read GitHub advisories (GHSA/CVE), commit pages, compare views and diffs through the GitHub REST API (`gh api`, read-only). I did not clone any candidate repository or run any scanner or project tooling on one. The work ran in four parallel lanes (Python, JS/TS, PHP apps, WordPress plugins) under one shared rule sheet, reproduced below. Every candidate was then re-verified from the merged list.
|
||||
|
||||
## Selection method (anti-bias)
|
||||
- **Enumeration.** Reviewed GitHub advisories per ecosystem (pip, npm, composer): `gh api "/advisories?ecosystem=<eco>&type=reviewed&cwes=<C>&sort=published&direction=desc"` for CWE 89, 78, 77, 94, 95, 1336, 943, 90, 918, 601, 489, 215, 942, 295, 798, 321, 259, 1188, 16, 346, 1004. The results were merged, deduplicated, limited to publications on or after 2023-01-01 and sorted newest first.
|
||||
- WordPress plugins: the newest 300 *unreviewed* advisories per CWE (89, 78, 94, 918, 601, 798, 942), kept only when the description mentions a WordPress plugin, then worked newest first. Each plugin's slug was searched on GitHub for a real development repository; SVN mirrors do not count.
|
||||
- The CWE filter is the only prefilter. It removes out-of-family advisories before anyone sees them, so those are not counted.
|
||||
- **Order.** Every lane took each advisory that met the criteria, strictly newest first, until its quota was filled:
|
||||
- Python 7, JS/TS 7, PHP 4, WordPress 5 (rows marked `quota`).
|
||||
- After the quota, each lane continued newest first for `untrusted_destination` and `config_secrets` only, until it had one extra of each or had examined 40 more advisories (rows marked `extra`).
|
||||
- The quota phase came out heavy on SSRF and redirects (about half the reviewed advisories of 2026 are SSRF in AI and agent servers). So the Python, JS and PHP lanes ran a third pass, `continuation`: newest first, injection only (Python) or CWE-89/943 only (JS, PHP), until 2 more were taken. Python then ran a CWE-89 pass until 1 SQLi was taken.
|
||||
- At no point was a case chosen or skipped for how easy it would be to detect.
|
||||
- **Shared rules:**
|
||||
- In scope means a web application or service. For Python that is Flask, Django or FastAPI (Starlette counts). For JS/TS it is a Node server (Express, Koa, NestJS, Next API routes, plain http). For PHP it is any web application or plugin. Libraries, SDKs, CLIs, MCP stdio tool servers and other frameworks (Zope/Plone, aiohttp, WsgiDAV) were rejected.
|
||||
- Guard-bypass and incomplete-fix advisories are allowed, with the caveat "vulnerable pin already has a guard".
|
||||
- A fix spread over up to 3 commits is allowed. A fix inside a large unrelated squash or release commit is rejected.
|
||||
- One case per repository: the newest qualifying advisory wins.
|
||||
- **Exclusion check.** I compared every candidate repo against `used-repos-v2.txt` (case-insensitive, `github.com/owner/repo`), which covers the v1 population and all development subjects. There were no hits. No candidate is a training app, CTF, benchmark suite or toy fork.
|
||||
- **Verification (merged list, re-run independently of the lanes).** For all 37 pairs:
|
||||
- `gh api repos/O/R/commits/<SHA> --jq .sha` returned both SHAs.
|
||||
- For 36 pairs, the vulnerable SHA is listed in the fixed commit's `parents`. For qwed, the fixed SHA is a merge and the vulnerable SHA is its first parent.
|
||||
- For LightRAG, `compare` reports the fixed SHA 3 commits ahead of the vulnerable SHA and 0 behind, so the vulnerable SHA is an ancestor.
|
||||
|
||||
"Diff seen" means the patch hunks of the named fix commit were read through the API, and the sink was read at the vulnerable pin (`contents/<path>?ref=<vuln>`).
|
||||
|
||||
## Summary table
|
||||
|
||||
| # | id | phase | repo | lang (+also) | family | advisory | vulnerable SHA | fixed SHA | ancestry | sink file :: function | source | privilege | license | diff seen | confidence |
|
||||
|---|----|-------|------|--------------|--------|----------|----------------|-----------|----------|-----------------------|--------|-----------|---------|-----------|------------|
|
||||
| 1 | lightrag-md-image-ssrf | quota | https://github.com/HKUDS/LightRAG | Python/FastAPI (+TS web UI) | untrusted_destination (SSRF, guard bypass) | CVE-2026-85740 / GHSA-vv3m-f8x4-7377 | `480f4a01741ec1d246842af68e09555a51ebf78f` | `c598545aa0084a427bcb19a84ade5e0ec31fa673` | ancestor (3 commits: `a2586217` → `9207e7fd` → `c598545a`, linear) | lightrag/parser/markdown/parser.py :: `_MarkdownImageResolver._download()` → `_build_guarded_opener().open(req)`, gated by the module function `_validated_addresses()` (image `src` URL from an uploaded markdown/textpack document) | document upload through the FastAPI API (image URLs inside the uploaded markdown) | authenticated (API key through `combined_auth`) | MIT | yes | medium |
|
||||
| 2 | lmdeploy-media-url-ssrf | quota | https://github.com/InternLM/lmdeploy | Python/FastAPI (+C++/CUDA) | untrusted_destination (SSRF, guard bypass) | GHSA-39wr-7q6h-cf68 (no CVE) | `71648d239c978452720a6d6692fe914efc8a7b94` | `03c313006d17cc3feae86b633c44206a997c44db` | parent | lmdeploy/vl/media/connection.py :: `_load_http_url()` → `requests.Session().get(url_spec.geturl(), allow_redirects=True)`, gated by `_is_safe_url()` (the `urlparse` host differs from the `requests` host) | `image_url`/video/audio URLs in `POST /v1/chat/completions` → serve/processors/multimodal.py :: `MultimodalProcessor._parse_multimodal_item()` → `load_from_url()` | unauthenticated by default (`api_keys` is optional, default None) | Apache-2.0 | yes | medium |
|
||||
| 3 | litellm-user-config-ssrf | quota | https://github.com/BerriAI/litellm | Python/FastAPI (+TS admin UI) | untrusted_destination (SSRF) | CVE-2026-59823 / GHSA-hx8v-g79f-8w5f | `93faf321df159b1ef65a98cc29a70958a96d184a` | `47214be317f45a2d2ac08258362e0481411eb427` | parent | litellm/proxy/route_llm_request.py :: `_route_user_config_request()` (`litellm.Router(**user_config)` then the provider call to the attacker's `api_base`); the only guard is litellm/proxy/auth/auth_utils.py :: `is_request_body_safe()` (`banned_params`) | JSON body of `POST /chat/completions` etc., key `user_config` | authenticated (any valid virtual key) | NOASSERTION (MIT core + enterprise dir) | yes | medium |
|
||||
| 4 | open-webui-playwright-ssrf | quota | https://github.com/open-webui/open-webui | Python/FastAPI (+Svelte/TS/JS) | untrusted_destination (SSRF, DNS rebinding) | CVE-2026-87996 / GHSA-4v28-j6q3-5m4r | `73c1f5806aeb6345dad5de8f5aa26d1f3d0bef80` | `27402ff210bfa253445720920dfb86b15a00327b` | parent | backend/open_webui/retrieval/web/utils.py :: `SafePlaywrightURLLoader._intercept_navigation_sync()` / `_intercept_navigation()` (`validate_url(req.url)` then `route.fetch()`, where the browser re-resolves the host) | URL submitted for RAG ingestion or web search | authenticated (any verified user) | Open WebUI License (BSD-3 plus branding clause, not OSI; GitHub: NOASSERTION) | yes | medium |
|
||||
| 5 | winml-cli-serve-cors | quota | https://github.com/microsoft/winml-cli | Python/FastAPI (+HTML) | config_secrets (wildcard CORS on an unauthenticated local API) | CVE-2026-84452 / GHSA-96p9-rh4f-92cf | `02179d185f0e3ac23518236b5ca1f61fd37ef680` | `f4073e0ef4700a25b623487e7e45c421ca0b9993` | parent | src/winml/modelkit/serve/cli_api.py :: module level `app.add_middleware(CORSMiddleware, allow_origins=["*"], ...)` (same in serve/app.py :: `create_app()`) | any web page in the victim's browser calling `POST /v1/cli/{command}` on 127.0.0.1 | unauthenticated (cross-origin from any website) | MIT | yes | high |
|
||||
| 6 | chainlit-mcp-ssrf | quota | https://github.com/Chainlit/chainlit | Python/FastAPI (+TS) | untrusted_destination (SSRF) | CVE-2026-45019 / GHSA-hvfh-5mj3-5f3j | `b8506a47fa59d7eaba40ad6d27496c2605a64cb9` | `0565fd0eccb915fce159929598b053ed79f6e0c9` | parent | backend/chainlit/server.py :: `connect_mcp()` → nested `_mcp_session_runner()` → `sse_client(url=...)` / `streamablehttp_client(url=..., headers=...)` (`payload.url`, `payload.headers`) | `POST /mcp` JSON body (`ConnectMCPRequest`) | unauthenticated (needs non-default `features.mcp.enabled = true`) | Apache-2.0 | yes | high |
|
||||
| 7 | contextforge-prompt-ssti | quota | https://github.com/IBM/mcp-context-forge | Python/FastAPI (+JS, Rust, Go) | injection (SSTI, Jinja2) | GHSA-vwf3-4xxj-qg6h (no CVE) | `0f8e0ad679f1fff685f486e17f642ac863c8ca9e` | `4d31004661858a2e99055b3c0c9f14218b8f7120` | parent | mcpgateway/services/prompt_service.py :: `PromptService._render_template()` → `_compile_jinja_template()` → `_get_jinja_env().from_string(template).render()` (plain `jinja2.Environment`) | prompt template stored through `register_prompt` (prompts create/update API), rendered on prompt get | authenticated (permission to create or update prompts) | Apache-2.0 | yes | high |
|
||||
| 8 | lemur-acme-update-ssrf | extra | https://github.com/Netflix/lemur | Python/Flask (+JS) | untrusted_destination (SSRF, stored) | CVE-2026-71303 / GHSA-v5rc-cpwc-cfpr | `e9ade7dad5c9bc1369a831554ed6df3277c9cbae` | `edca0390f930344d65ff4ca37a669c2320e3dfad` | parent | lemur/plugins/lemur_acme/acme_handlers.py :: `AcmeHandler.setup_acme_client_no_retry()` → `ClientV2.get_directory(directory_url, net)` (`acme_url` from the stored authority options) | `PUT /api/1/authorities/<id>` `options` → lemur/authorities/service.py :: `update()` stores it without validation | authenticated (user with a role on the ACME authority) | Apache-2.0 | yes | high |
|
||||
| 9 | glances-restful-cors-credentials | extra | https://github.com/nicolargo/glances | Python/FastAPI (+Vue/JS) | config_secrets (wildcard CORS with credentials) | CVE-2026-68517 / GHSA-fp27-88fp-2phg | `a14fb302a772bf8341b00c3ab8c2b0e95cffc197` | `890858944ab9d03730ec6b1ba42d4015e6d85db5` | parent | glances/outputs/glances_restful_api.py :: `GlancesRestfulApi.__init__()` (`add_middleware(CORSMiddleware, allow_origins=cors_origins, allow_credentials=cors_credentials)` after a guard that only matches `cors_origins == ["*"]`) | configuration file (`[outputs] cors_origins=*,https://x`, `cors_credentials=true`); exploited by any website through the victim's cached Basic auth | unauthenticated cross-origin reader (config-dependent) | LGPL-3.0 per COPYING (GitHub: NOASSERTION) | yes | medium |
|
||||
| 10 | qwed-sympy-eval | continuation | https://github.com/QWED-AI/qwed-verification | Python/FastAPI (+HTML, small Go/TS/Rust SDKs) | injection (code/eval injection through SymPy `parse_expr`) | CVE-2026-55585 / GHSA-q27q-98j4-9pfv | `06801f68cbf9c9b5e54cc17f2a61cb2792da7a26` | `6066b68c0c4f4cc2c3771824822aaa864d082ef8` | first parent (fixed is the merge of PR #200; the PR commit `dc9d4db72ca4b4ae3f96d0e6a0c27a9e38a06f61` is a direct child of the vulnerable SHA) | src/qwed_new/api/main.py :: `verify_math()` → `sympy.parsing.sympy_parser.parse_expr(expression_normalized)` (also `left_str`/`right_str`); second path src/qwed_new/core/batch.py :: `_verify_item()` → `parse_expr(item.query)` | `POST /verify/math` JSON `expression`; `POST /verify/batch` item `query` | authenticated tenant (API key; self-signup `/auth/signup` is on by default) | Apache-2.0 | yes | high |
|
||||
| 11 | xinference-llama3-tool-eval | continuation | https://github.com/xorbitsai/inference | Python/FastAPI (+TS/JS web UI) | injection (code/eval injection) | CVE-2026-61539 / GHSA-x2rj-828p-hx9m | `4cefcaa1e63d7e29d26bcd4be96cb267da113871` | `1b3d220f342ce68d34cec4586d9409d457dadc42` | parent | xinference/model/llm/tool_parsers/llama3_tool_parser.py :: `Llama3ToolParser.extract_tool_calls()` → `eval(model_output, {}, {})`; second site xinference/model/llm/utils.py :: `ChatModelMixin._eval_llama3_chat_arguments()` → `eval(text, {}, {})` | model output steered by the prompt in `POST /v1/chat/completions` with `tools` (xinference/api/restful_api.py) | unauthenticated by default (auth not enabled in the default deployment, per the advisory) | Apache-2.0 | yes | medium |
|
||||
| 12 | pgadmin-maintenance-sqli | continuation | https://github.com/pgadmin-org/pgadmin4 | Python/Flask (+JS/React) | injection (SQLi) | CVE-2026-7815 / GHSA-hp84-p2gq-6fvr | `d112dc3b962218efc3613fa727d54f0fc5b1d527` | `cf53953d9ad66c182a08e662c1ba29c5831b486b` | parent | web/pgadmin/tools/maintenance/templates/maintenance/sql/command.sql (string concatenation of `vacuum_index_cleanup`, `vacuum_parallel`, `buffer_usage_limit`, `reindex_tablespace`) rendered in web/pgadmin/tools/maintenance/__init__.py :: `create_maintenance_job()` → `render_template(...)` → `BatchProcess(cmd=psql, args=[..., '--command', query])` | JSON body of `POST /maintenance/job/<sid>/<did>` | authenticated (`tools_maintenance` permission) | PostgreSQL License (OSI; GitHub: NOASSERTION) | yes | high |
|
||||
| 13 | reactpress-findall-column-sqli | quota | https://github.com/fecommunity/reactpress | TypeScript, NestJS/TypeORM (+JS, CSS, HTML) | injection (SQLi) | CVE-2026-61685 / GHSA-wmw4-mw6x-6vfm | `c3dd10681b3d2b5e224f7ac647b3769557603975` | `78ecb70af1c021455c05fdcbe137212c70e310d6` | parent | server/src/modules/article/article.service.ts :: `ArticleService.findAll()` (query-string key interpolated as `article.${key}` into `andWhere`); same pattern in comment/file/page/knowledge services | `@Query() queryParams` in `ArticleController.findAll()` (`GET /api/article`) | unauthenticated | MIT | yes | high |
|
||||
| 14 | 9router-kiro-region-ssrf | quota | https://github.com/decolua/9router | JavaScript, Next.js API routes (+CSS, Shell) | untrusted_destination (SSRF) | CVE-2026-56678 / GHSA-6mwv-4mrm-5p3m | `f2a7ae20309b4af55023eb11d1c02f63be1b80d1` | `126aa244c5b51b74ab8c7594e3418fcf4437bf6f` | parent | src/lib/oauth/services/kiro.js :: `KiroService.listAvailableProfiles()` (`fetch(\`https://codewhisperer.${region}.amazonaws.com\`)` with the API key as Bearer), reached via `validateApiKey()` | `region` in JSON body of `POST /api/oauth/kiro/api-key` (src/app/api/oauth/kiro/api-key/route.js) | authenticated (dashboard session) | MIT | yes | high |
|
||||
| 15 | omniroute-acp-agent-rce | quota | https://github.com/diegosouzapw/OmniRoute | TypeScript, Next.js API routes (+JS, Python, Shell) | injection (OS command / code) | CVE-2026-88062 / GHSA-hf57-cqmx-p4gr | `c130f2aa1ccc7aaddd7a7685bd6a0e08136dccf1` | `60829241fd64d0317aa6a0dd8cd7a445a5287fed` | parent | src/lib/acp/registry.ts :: `detectAgent()` → `execFileSync(probe.command, probe.args)` with probe from `resolveVersionProbe(binary, versionCommand, true)` | `binary` / `versionCommand` in body of `POST /api/acp/agents` (src/app/api/acp/agents/route.ts → `setCustomAgents` → `refreshAgentCache`) | unauthenticated when `requireLogin=false` or before a management password is set; management session otherwise | MIT | yes | medium |
|
||||
| 16 | cct-studio-server-cmdi | quota | https://github.com/davila7/claude-code-templates | JavaScript, Express (+Python, TS, HTML, Shell, many template languages) | injection (OS command) | CVE-2026-73222 / GHSA-79wm-x847-7cvg | `acdb5893412a81eaec9862ee24a4613bf296ab45` | `bc4618b07232633c1c0aac12a43e436268d31783` | parent | cli-tool/src/sandbox-server.js :: `executeLocalTask()` (`spawn('claude', [finalPrompt], {shell: true})`), route handler `POST /api/install-agent` and `checkAndInstallAgent()` (`spawn('npx', [..., agentName, ...], {shell: true})`) | `req.body.prompt`, `req.body.agentName`, `req.body.agent` | unauthenticated (server on 0.0.0.0, wildcard CORS) | MIT | yes | high |
|
||||
| 17 | unleash-addon-webhook-ssrf | quota | https://github.com/Unleash/unleash | TypeScript, Express (+JS, PLpgSQL, SCSS) | untrusted_destination (SSRF) | CVE-2026-63004 / GHSA-5vf6-jrqr-78fj | `b9f3217d327f24094b4352130baa30387f85cffe` | `2100db76af3473f13e6fb40096cf17a9c2b741a1` | parent | src/lib/addons/addon.ts :: `Addon.fetchRetry()` (`ky(url, ...)`), called from webhook.ts / slack / teams / datadog / new-relic addons | addon `parameters.url` saved through the admin addons API (`CREATE_ADDON`/`UPDATE_ADDON`), fired on feature events | authenticated (CREATE_ADDON/UPDATE_ADDON root permission) | AGPL-3.0 | yes | high |
|
||||
| 18 | nocobase-backup-restore-cmdi | quota | https://github.com/nocobase/nocobase | TypeScript, Koa (+JS, Shell) | injection (OS command) | CVE-2026-55410 / GHSA-p853-83gj-wjj3 | `a89b1ea0218b0d758543734e86f595f98f654314` | `aecc6758810b46dc13790d35ec4a15eea29357cc` | parent | packages/plugins/@nocobase/plugin-backups/src/server/adapters/database.ts :: `PostgresAdapter.restore()` (`-n ${srcSchema}` in pg_restore command string) → module `run()` → `child_process.exec` | `database.schema` in `_metadata.json` of an uploaded backup archive, restore action with `force` (resourcers/backup-cli.ts → managers/restore.ts) | authenticated, backup-management role | repo: NocoBase License (non-OSI, GitHub NOASSERTION); plugin package.json says Apache-2.0 | yes | medium |
|
||||
| 19 | medplum-external-auth-open-redirect | quota | https://github.com/medplum/medplum | TypeScript, Express (+JS, Python, HCL) | untrusted_destination (open redirect) | CVE-2026-53728 / GHSA-m44r-7c5h-m6mj | `5d4a8654b02aa67c444f96ae1eb5663c09854006` | `7ae10035ddadde4dba7b18d3156553940465b3a1` | parent | packages/server/src/auth/external.ts :: `externalCallbackHandler()` (`res.redirect(redirectUrl)` with URI accepted by `getClientRedirectUri(client, body.redirectUri, true)` prefix match in packages/server/src/oauth/clients.ts) | `redirectUri` inside JSON `state` query parameter of `GET /auth/external` | unauthenticated (victim completes external IdP login) | Apache-2.0 | yes | high |
|
||||
| 20 | flowise-ipv4-mapped-ssrf | extra | https://github.com/FlowiseAI/Flowise | TypeScript, Express (+JS, HTML, SCSS) | untrusted_destination (SSRF, guard bypass) | CVE-2026-69257 / GHSA-c6xh-wv4j-ppv5 | `b7a2005fd7b394ba7874aded744fe3730d59ccb3` | `0fc769208395641c1411ccdb9c81416e54802155` | parent | packages/components/src/httpSecurity.ts :: `isDeniedIP()` (deny-list check used by `checkDenyList()` / `resolveAndValidate()` before `secureFetch()` / `secureAxiosRequest()`) | URLs in chatflow node inputs that go through `secureFetch`/`secureAxiosRequest`; attacker-controlled DNS returning `::ffff:<internal IPv4>` | authenticated (flow builder), possibly prediction API when a flow takes a URL from input | Apache-2.0 outside `packages/server/src/enterprise` (GitHub NOASSERTION; commercial licence for enterprise dir) | yes | medium |
|
||||
| 21 | nx-graph-server-wildcard-cors | extra | https://github.com/nrwl/nx | TypeScript, Node `http` server (+JS, Rust, many) | config_secrets (permissive CORS) | CVE-2026-54753 / GHSA-g2r8-wvmj-jf5w | `6ac9f82b04d02ab057b395b0e2b209fb3caf8545` | `e59122c5569cf201561b9995ce4e34a304f4fb03` | parent | packages/nx/src/command-line/graph/graph.ts :: `startServer()` request handler (`res.setHeader('Access-Control-Allow-Origin', '*')` on every response, including `/help` which runs `execSync(target.metadata.help.command)`) | any cross-origin web page the developer visits while `nx graph` runs | unauthenticated (cross-origin, local dev server) | MIT | yes | high |
|
||||
| 22 | budibase-mongo-template-nosqli | continuation | https://github.com/Budibase/budibase | TypeScript, Koa (+JS, Svelte, Shell) | injection (NoSQL) | GHSA-pmpg-2mxq-6xwr (no CVE); same fix family as GHSA-qw6m-8fw2-2v64 | `b7e63df737eede85d951aa9665b0c0778ece7ccb` | `dd8c0654b35cd89ce3645f2355f4bf2ff9a5dd80` | parent | packages/server/src/sdk/workspace/queries/queries.ts :: `enrichContext()` (handlebars `processStringSync(..., {noEscaping: true})` into the `json` template, then `JSON.parse`) → packages/server/src/integrations/mongodb.ts :: `MongoIntegration.read()` (`collection.find(json)`) | query parameters / bindings (e.g. `currentUser.email`, request parameters) of `POST /api/v2/queries/:queryId` (packages/server/src/api/controllers/query/index.ts :: `executeV2`) | authenticated app user (BASIC role) | GPL-3.0 (root LICENSE says "GPLv3 overall"; server package.json GPL-3.0; GitHub NOASSERTION) | yes | medium |
|
||||
| 23 | fuxa-tdengine-escape-sqli | continuation | https://github.com/frangoteam/FUXA | JavaScript, Express + Socket.IO (+TS, HTML, SCSS) | injection (SQLi, escape bypass) | CVE-2026-47720 / GHSA-h9fj-c2qr-76g2 | `193a888f8e3f805325072829b1644a2c768c948b` | `b78f6159d02a56ce5ff48207feb936afb3534fc8` | parent | server/runtime/storage/tdengine/index.js :: `TDengine.getDaqValue()` (`escapeTdString(tagid)` doubles quotes but not backslashes, then SQL string for `cursor.query`) | `sids` of `GET /api/daq` (server/api/daq/index.js) or Socket.IO `DAQ_QUERY` | unauthenticated (guest level; socket handler has no auth check) | MIT | yes | medium |
|
||||
| 24 | contao-feed-reader-ssrf | quota | https://github.com/contao/contao | PHP (+JS, Twig) | untrusted_destination (SSRF) | CVE-2026-57232 / GHSA-87mg-5grr-rhwh | `394d951e7f6a87074ddc1f75f1c0ad9f94d98d68` | `27f6201809553bee767dcef15535bb8f0f4eac5f` | parent (5.7 branch; fixed commit is an ancestor of tag 5.7.9) | core-bundle/src/Controller/FrontendModule/FeedReaderController.php :: `FeedReaderController::getResponse()` (`$this->feedIo->read($url, ...)` over `$model->rss_feed` URLs; HTTP client wired in core-bundle/config/services.yaml `contao.feed.http_adapter`) | `tl_module.rss_feed` field saved through the back end, rendered by any front-end page that holds the module | authenticated back-end user with module-edit rights | LGPL-3.0 | yes | high |
|
||||
| 25 | formie-form-settings-ssrf | quota | https://github.com/verbb/formie | PHP (+JS, TS) | untrusted_destination (SSRF) | CVE-2026-76086 / GHSA-v3f3-cmj4-cvj9 | `788fc375defb4def857c8737f7c92e487da92ca9` | `6735fe4ae8f6a2a76930716ad7876b236f7c530d` | parent (craft-5 branch; ancestor of tag 3.1.31) | src/controllers/IntegrationsController.php :: `IntegrationsController::actionFormSettings()` (`$integration->setAttributes($settings, false)`), then e.g. src/integrations/emailmarketing/ActiveCampaign.php :: `defineClient()` (`base_uri` from `$this->apiUrl`) → src/base/Integration.php :: `Integration::request()` | POST param `settings[...]` (for example `settings[apiUrl]`) to `formie/integrations/form-settings` | authenticated (any logged-in user, including front-end members) | **Craft License (proprietary, not OSI)**; GitHub says NOASSERTION | yes | medium |
|
||||
| 26 | sulu-smart-content-sql2-injection | quota | https://github.com/sulu/sulu | PHP (+JS) | injection (JCR-SQL2 query injection) | CVE-2026-92692 / GHSA-jg26-q8hg-3pq4 | `d061094f5b7bb1d5e974544fce30bede9c7adf8e` | `d19c01487af8c3de2fb3aa145856707a6367392c` | parent (2.6 branch; ancestor of tag 2.6.25) | src/Sulu/Component/Content/SmartContent/QueryBuilder.php :: `QueryBuilder::buildCategoriesWhere()` (also `buildTagsWhere()`, `buildAudienceTargeting()`), string-concatenated into the SQL2 WHERE clause built by `buildWhere()` | `?categories=` query parameter, read in src/Sulu/Component/Category/Request/CategoryRequestHandler.php :: `getCategories()` (explode + trim only), passed by src/Sulu/Component/SmartContent/ContentType.php as `websiteCategories` | unauthenticated (any public page with a category-filtered Smart Content block) | MIT | yes | high |
|
||||
| 27 | freeform-attribute-twig-ssti | quota | https://github.com/solspace/craft-freeform | PHP (+JS, EJS) | injection (SSTI, sandboxed Twig) | CVE-2026-73858 / GHSA-gxrg-x694-283w | `1b1b16a45c289cec7ac0cc2385e9431867427fe2` | `5f7555320635f1cd3b4c478aa0b58e8c3144313b` | parent (ancestor of tag v5.10.14, not of v5.10.13) | packages/plugin/src/Library/Attributes/Attributes.php :: `Attributes::toHtmlTagArray()` (`$twig->render($value, $properties)` through `Library\Helpers\IsolatedTwig`) | submitted form field values that are rendered back as the field's `value` attribute | unauthenticated (public form submission) | **Craft License (proprietary, not OSI)**; GitHub says NOASSERTION | yes | medium |
|
||||
| 28 | smw-uriresolver-open-redirect | extra | https://github.com/SemanticMediaWiki/SemanticMediaWiki | PHP (+JS) | untrusted_destination (open redirect) | CVE-2026-77609 / GHSA-hw3m-8j5x-94ff | `5821b9b865c58d40e5ecd1aa807b79fa32eb5c2f` | `765a6ac22305e2d5c6fc79415bfee7cbd58514cd` | parent (ancestor of tag 7.2.0, not of 7.1.0) | src/MediaWiki/Specials/SpecialURIResolver.php :: `SpecialURIResolver::execute()` (`$out->redirect($title->getFullURL(), '303')`) | subpage of `Special:URIResolver/<x>` (the `$query` argument), decoded and turned into a Title (interwiki prefixes allowed) | unauthenticated | GPL-2.0-or-later (composer.json; GitHub says NOASSERTION) | yes | high |
|
||||
| 29 | pheditor-default-admin-password | extra | https://github.com/pheditor/pheditor | PHP only (single file) | config_secrets (hardcoded default credential) | CVE-2026-55579 / GHSA-p4h7-p9rj-2pq2 | `e538f05b6faec99e5b23726bc9c17d6b57774297` | `a64eb935ce699e43f1ea60e46d47f33e2f87ffd6` | parent (= tag 2.0.5 → 2.0.6 compare, one commit) | pheditor.php :: top level `define('PASSWORD', '<sha512 of "admin">')` (line 11), checked by the top-level login block (`hash('sha512', $_POST['pheditor_password']) === PASSWORD`) | `pheditor_password` POST field on the login form | unauthenticated (default credential) | MIT | yes | low |
|
||||
| 30 | pimcore-studio-datefilter-sqli | continuation | https://github.com/pimcore/studio-backend-bundle | PHP (+Twig, tiny JS) | injection (SQLi, identifier) | CVE-2026-55208 / GHSA-79cw-hfcc-7mw9 | `ea9d329686f5e5aea2eec378d63ac2deb965bb27` | `f532428cfbf4f5d6e299a13cedd5c29541802552` | parent (ancestor of tags v2025.4.6 and v2026.1.6) | src/Listing/Filter/DateFilter.php :: `DateFilter::applyDateFilter()` (`'`' . $key . '` BETWEEN :minTime AND :maxTime'` into `$listing->addConditionParam()`); same pattern in EqualsFilter::apply(), LikeFilter::apply(), src/Note/Service/FilterService.php :: applyFieldFilters() | `columnFilters[].key` in the JSON body of Studio listing endpoints (e.g. `POST /pimcore-studio/api/website-settings`) | authenticated (any Studio back-end user who can reach a listing endpoint) | **Pimcore Open Core License (POCL), not OSI**; GitHub says NOASSERTION | yes | high |
|
||||
| 31 | froxlor-admin-ip-second-order-sqli | continuation | https://github.com/froxlor/froxlor | PHP (+JS, Twig) | injection (SQLi, second-order) | CVE-2026-54348 / GHSA-w27m-rmmf-g5w4 | `9bf03de643a2edee4642bfc6c94db1f05253da02` | `a1eaca5a1601c8a30e00814a4fc73ad0c185f89e` | parent (ancestor of tag 2.3.8) | lib/Froxlor/Api/Commands/IpsAndPorts.php :: `IpsAndPorts::listing()` / `listingCount()` (`implode(", ", json_decode($this->getUserDetail('ip'), true))` into `WHERE id IN (...)`); also lib/Froxlor/Api/Commands/Domains.php :: `validateIpAddresses()` | `ipaddress` API parameter of `Admins.add` / `Admins.update` (lib/Froxlor/Api/Commands/Admins.php), stored as JSON in `panel_admins.ip`, read back via `getUserDetail('ip')` | admin (API key with `change_serversettings`) | LGPL-2.1 (per GitHub) | yes | high |
|
||||
| 32 | wp-s2member-tracking-eval | quota | https://github.com/wpsharks/s2member | PHP (+JS, CSS) | injection (code injection / eval) | CVE-2026-19804 / GHSA-fgx2-6hhq-hwhw | `4e1fe70b342f911ae5cb5c66652f2a2aef45b08b` | `6c622839aa0efe14031ff7c8fcc1f7d3107563ba` | parent | src/includes/classes/tracking-codes.inc.php :: `c_ws_plugin__s2member_tracking_codes::display_signup_tracking_codes()` → src/includes/classes/utilities.inc.php :: `c_ws_plugin__s2member_utilities::evl()` (`eval('?>'.$code)` over a transient holding the admin template with `%%first_name%%` replaced by the payer's first name) | `$paypal['first_name']` from the PayPal return/IPN data, substituted in src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php (line ~315) and stored with `set_transient()` | unauthenticated (needs the admin template to contain `%%first_name%%` and the proxy key, see notes) | GitHub: none; plugin header "GNU General Public License" (GPL, version not stated) | yes | medium |
|
||||
| 33 | wp-directorist-avatar-ssrf | quota | https://github.com/sovware/directorist | PHP (+JS, Vue, TS, CSS) | untrusted_destination (SSRF) | CVE-2026-84046 / GHSA-6q63-3w56-m722 | `77cfc9bc8b453f97ab7e720330706fa4ef056867` | `1a67825d5cf6e7c768c3a692da1d18bae732bea9` | parent | includes/rest-api/functions.php :: `directorist_rest_upload_image_from_url()` (`download_url($image_url)`) | `$request['avater']['src']` in includes/rest-api/Version1/class-users-controller.php :: `update_user_meta_fields()` (REST user update) | authenticated (subscriber) | GitHub: NOASSERTION; plugin header GPLv3 | yes | medium |
|
||||
| 34 | wp-gopay-log-filter-sqli | quota | https://github.com/gopaycommunity/gopay-woocommerce-integration | PHP (+JS, CSS) | injection (SQLi) | CVE-2026-75959 / GHSA-88c4-2f34-57h5 | `626bc8b5f8d33de6eefd921e3a05ed52ac320bcd` | `7190c985370f9758f226c2f2addd79532a2f93d5` | parent | admin/views/log.php :: file scope (included by admin/class-gopay-gateway-admin.php), two `$wpdb->get_results(sprintf("... REGEXP '[\w\W]*%s[\w\W]*' ...", ..., strtoupper($log_table_filter)))` | `filter_input(INPUT_GET, 'log_table_filter')` | admin (shop manager, `manage_woocommerce`) | GitHub: none; plugin header GPLv2 or later | yes | high |
|
||||
| 35 | wp-user-registration-login-redirect | quota | https://github.com/themegrill/user-registration | PHP (+JS, TS, SCSS) | untrusted_destination (open redirect) | CVE-2026-80072 / GHSA-w78c-fx7q-9x8m | `e52cf151cb229852e6209fb3a627aa51485139c1` | `75c940dab15fd54d5a3fbb9f2213767b89467b08` | parent | includes/functions-ur-core.php :: `ur_process_login()` (`wp_redirect(wp_validate_redirect($redirect, $redirect))`, and `wp_send_json_success(['message'=>$redirect])` on the AJAX path) | `$post['redirect']` (login form POST) or `wp_get_raw_referer()` | unauthenticated attacker, victim logs in | GitHub: NOASSERTION; repo has license.txt, plugin header GPLv3 | yes | high |
|
||||
| 36 | wp-groundhogg-confirm-redirect | quota | https://github.com/groundhoggwp/groundhogg | PHP (+JS, SCSS, HTML) | untrusted_destination (open redirect) | CVE-2026-81741 / GHSA-m96f-c5vh-h7cm | `9472a1dd79d4556c40eece501a61930793f170d3` | `028247d4b8ec9ba051d3eb48f1466a759bc16bfd` (+ comment-only follow-up `2f03c8481a2236b0574f67fa8a7415b77556f730`) | parent (follow-up is the direct child of the fix) | templates/preferences.php :: file scope, `case 'confirm':` (`wp_redirect($redirect_to)`) | `get_url_var('redirect_to')` (query string, only through `esc_url_raw(sanitize_text_field(...))`) | unauthenticated (crafted preferences link) | GitHub: none, no LICENSE file; plugin header GPLv3 | yes | high |
|
||||
| 37 | wp-stripe-payments-thankyou-redirect | extra | https://github.com/Arsenal21/stripe-payments | PHP (+JS, CSS) | untrusted_destination (open redirect) | CVE-2026-81423 / GHSA-rvj6-j43w-hcgw | `71806a51e2c2c2ceb2cdaf00cbbde7a1b9da09f4` | `fb1f647deb6c92a2d2125da7a99fcb2914264f9c` | parent | stripe-payments/includes/class-asp-process-ipn-ng.php :: `ASP_Process_IPN_NG::ipn_completed()` (`wp_redirect($this->asp_redirect_url)`) | POST `asp_thankyou_page_url` (base64-decoded) in `ASP_Process_IPN_NG::process_ipn()`, triggered by POST `asp_process_ipn` on `wp_loaded` | unauthenticated (needs the public payment-form nonce) | GitHub: none; plugin header GPLv2 or later | yes | medium |
|
||||
|
||||
**Mix (37 candidates).**
|
||||
- **By language:** 12 Python, 11 JS/TS, 14 PHP. The 14 PHP split into 8 PHP web apps and 6 WordPress plugins, all of which ship JS.
|
||||
- **By family:**
|
||||
- 16 `injection`: SQLi in #12, #13, #23, #30, #31, #34; JCR-SQL2 query injection #26; NoSQL #22; SSTI #7, #27; code/eval #10, #11, #32; OS command #15, #16, #18.
|
||||
- 17 `untrusted_destination`: SSRF in #1, #2, #3, #4, #6, #8, #14, #17, #20, #24, #25, #33; open redirect in #19, #28, #35, #36, #37.
|
||||
- 4 `config_secrets`: wildcard CORS in #5, #9, #21; hardcoded default password #29.
|
||||
- **By phase:** 23 quota, 7 extra, 7 continuation.
|
||||
- **By privilege:** 19 unauthenticated (some only under a non-default setting, see the notes), 16 authenticated and 2 admin (#31, #34).
|
||||
- **Quota check (quota phase alone):** PHP 9 (5 WordPress), JS 7, Python 7. The quota rows alone give 5 injection, 15 untrusted_destination and 3 config_secrets. Most injection candidates come from the continuation passes, which is why those passes were run.
|
||||
|
||||
## Per-candidate notes
|
||||
|
||||
### Python (Flask / Django / FastAPI)
|
||||
|
||||
Source list: `gh api /advisories?ecosystem=pip&type=reviewed&cwes=<c>&sort=published&direction=desc` for CWEs 89, 78, 77, 94, 95, 1336, 943, 90, 918, 601, 489, 215, 942, 295, 798, 321, 259, 1188, 16, 346, 1004, merged and sorted newest first (list-pip.tsv). I worked from the top (2026-09-24) down. The quota of 7 was filled at GHSA-vwf3-4xxj-qg6h (2026-08-25, row 37). After that I continued newest first for `untrusted_destination` and `config_secrets` only, and stopped once each had one extra: Lemur (SSRF) and Glances (CORS). The last row examined is row 72 of list-pip.tsv (GHSA-fp27-88fp-2phg, 2026-08-17).
|
||||
|
||||
#### lightrag-md-image-ssrf (Python, FastAPI)
|
||||
- Repo: https://github.com/HKUDS/LightRAG (MIT, about 117 MB; Python plus a TypeScript web UI).
|
||||
- Advisory: CVE-2026-85740, GHSA-vv3m-f8x4-7377. ≤ 1.5.4, fixed in 1.5.5.
|
||||
- Flaw: the native markdown parser downloads images referenced by an uploaded document (`NATIVE_MD_IMAGE_DOWNLOAD_ENABLED` defaults to True). `_download()` calls `_host_is_public()`, then opens the URL through a guarded opener. Both rely on `_validated_addresses()`, which accepts an address when `ip.is_global` is true. Python classifies NAT64 (`64:ff9b::/96`, `64:ff9b:1::/48`), IPv4-compatible (`::a.b.c.d`) and 6to4 (`2002::/16`) wrappers around internal IPv4 addresses as global, so those addresses pass.
|
||||
- Fix: three consecutive commits on the same day. `a2586217` adds `_unwrap_embedded_ipv4()` and judges `is_global` on the embedded IPv4. `9207e7fd` corrects the RFC 8215 /48 decoding to deny by default. `c598545a` stops the 6to4 branch from loosening the stdlib verdict. The commits touch only parser.py and its tests.
|
||||
- Caveats: the vulnerable pin already has a guard (this is a guard bypass). The fix spans 3 commits, so use `480f4a01` → `c598545a`. Exploitation needs NAT64/DNS64 routing on the host. The sink is in the same file as the fix.
|
||||
- Confidence: medium. I read all three diffs and the sink at the vulnerable pin. The flaw is a subtle address-classification bug rather than a missing check.
|
||||
|
||||
#### lmdeploy-media-url-ssrf (Python, FastAPI)
|
||||
- Repo: https://github.com/InternLM/lmdeploy (Apache-2.0, about 17 MB; Python plus C++/CUDA kernels). This is an inference engine. The in-scope surface is its FastAPI OpenAI-compatible server (`lmdeploy serve api_server`).
|
||||
- Advisory: GHSA-39wr-7q6h-cf68 (no CVE), ≥ 0.12.3, < 0.15.0. **The advisory names no fix commit.** I found `03c31300` ("fix: restrict remote media domains", #4734) through the history of `lmdeploy/vl/media/connection.py`. It is an ancestor of tag v0.15.0 and the only commit in that range that changes the URL parser.
|
||||
- Flaw: `_is_safe_url()` validates `urlparse(url).hostname`, but `requests` connects to a different host for inputs such as `http://127.0.0.1:6666\@1.1.1.1`. `_load_http_url()` then fetches with `allow_redirects=True`, and redirect targets are not re-validated.
|
||||
- Fix: `_is_safe_url()` uses urllib3 `parse_url` (the parser `requests` uses). `_load_http_url()` follows redirects manually and re-validates each hop. The commit also adds an optional `allowed_media_domains` allowlist, plumbed through api.py, cli/serve.py, pipeline.py, the async engines and api_server.py (12 files).
|
||||
- Privilege: `api_keys` defaults to None in `serve()`, so the server is unauthenticated by default.
|
||||
- Confidence: medium. I read the diff and the sink. The fix commit is my inference because the advisory does not reference it. The vulnerable pin already has a guard (parser differential).
|
||||
|
||||
#### litellm-user-config-ssrf (Python, FastAPI)
|
||||
- Repo: https://github.com/BerriAI/litellm. **It is very large (about 1.7 GB of git data).** GitHub reports the license as NOASSERTION: the core is MIT and the `enterprise/` directory has its own license. Verify before publication.
|
||||
- Advisory: CVE-2026-59823, GHSA-hx8v-g79f-8w5f. ≤ 1.83.8, fixed in 1.83.9.
|
||||
- Flaw: `is_request_body_safe()` blocks `api_base`/`base_url` in the request body but not `user_config`. `route_request()` sends any request containing `user_config` to `_route_user_config_request()`, which builds `litellm.Router(**user_config)` and makes the call. An `api_base` nested in that config therefore redirects the proxy's outbound request, together with the provider credentials in the config.
|
||||
- Fix `47214be3`: adds `"user_config"` to `banned_params`. The same commit makes unrelated hardening changes: a ContextVar for internal calls, stripping `proxy_server_request`/`user_api_key_*` metadata keys, formatting churn, and file_search emulation (6 files).
|
||||
- Caveats: the sink (route_llm_request.py) is outside the changed files. The fix is a one-word denylist change.
|
||||
- Confidence: medium. The diff is clear. The outbound HTTP call itself sits deep in provider code, so I name the Router construction as the sink.
|
||||
|
||||
#### open-webui-playwright-ssrf (Python, FastAPI)
|
||||
- Repo: https://github.com/open-webui/open-webui (about 440 MB; Python backend plus a Svelte/TS frontend). **License: "Open WebUI License", which is BSD-3 plus a branding-preservation clause, not OSI-approved. GitHub reports NOASSERTION.**
|
||||
- Advisory: CVE-2026-87996, GHSA-4v28-j6q3-5m4r. ≥ 0.9.6, < 0.11.1.
|
||||
- Flaw: with `WEB_LOADER_ENGINE=playwright` (not the default), the route interceptor checks `validate_url(req.url)` and then lets the browser perform the fetch through `route.fetch()`. The browser resolves the hostname again, so DNS rebinding reaches internal addresses. Redirect hops are checked the same way.
|
||||
- Fix `27402ff2`: the interceptor now performs each request with the project's SSRF-safe requests/aiohttp sessions, which pin the validated address at connect time, and fulfils the page from that response. The change is 2 files.
|
||||
- Caveats: the vulnerable pin already has a guard (a TOCTOU bypass). The sink is a Playwright browser call, not an HTTP client. The feature is not on by default.
|
||||
- Confidence: medium. I read the diff and the vulnerable interceptor.
|
||||
- Two other Open WebUI advisories in the window (GHSA-34r3-9m95-vq73 and GHSA-5x7x-4c3c-qf5w, both fixed by `e3e4bd87`) were not taken, because the repo already has a candidate.
|
||||
|
||||
#### winml-cli-serve-cors (Python, FastAPI)
|
||||
- Repo: https://github.com/microsoft/winml-cli (MIT, about 26 MB). The package is a CLI whose `serve` subcommand runs a FastAPI server that exposes every CLI command over HTTP on 127.0.0.1.
|
||||
- Advisory: CVE-2026-84452, GHSA-96p9-rh4f-92cf (CWE-306/942). < 0.4.0.
|
||||
- Flaw: cli_api.py (module level) and app.py `create_app()` register `CORSMiddleware(allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])`. The API has no authentication, so any website can drive `POST /v1/cli/{command}`. With `--trust-remote-code` this leads to RCE through `AutoConfig.from_pretrained(..., trust_remote_code=True)`.
|
||||
- Fix `f4073e0e`: replaces CORSMiddleware with a new `SameOriginMiddleware` (serve/_security.py), makes the CLI and model-mutation routes loopback-only, and refuses remote-code execution requested over HTTP. It touches 12 files, all related.
|
||||
- Caveat: `allow_credentials=False` at the vulnerable pin. This is wildcard CORS on an API with no authentication, not wildcard CORS combined with credentials.
|
||||
- Confidence: high. I read the diff and the vulnerable module.
|
||||
|
||||
#### chainlit-mcp-ssrf (Python, FastAPI)
|
||||
- Repo: https://github.com/Chainlit/chainlit (Apache-2.0, about 12 MB; Python plus TypeScript).
|
||||
- Advisory: CVE-2026-45019, GHSA-hvfh-5mj3-5f3j. ≥ 2.4.0rc0, ≤ 2.11.1, fixed in 2.12.0.
|
||||
- Flaw: `connect_mcp()` (`POST /mcp`) builds `SseMcpConnection`/`HttpMcpConnection` from `payload.url` and `payload.headers` without any check, and its background runner opens `sse_client()` / `streamablehttp_client()` to that URL. The attacker also controls the forwarded headers.
|
||||
- Fix `0565fd0e` ("Merge commit from fork"): adds `validate_mcp_url()` against a new `features.mcp.user_servers.allowed_urls` allowlist, header filtering and connect-time destination checks, plus the fix for the sibling stdio command-injection advisory. It touches 24 files, all MCP-related (Python backend, React client and tests), about 4k added lines. That makes it large but coherent.
|
||||
- Caveats: the same commit fixes GHSA-w3fx-mc44-mf6j (command injection through the stdio transport, same endpoint). MCP is disabled by default since 2.7.0.
|
||||
- Confidence: high. I read the server.py hunks and the vulnerable handler.
|
||||
|
||||
#### contextforge-prompt-ssti (Python, FastAPI)
|
||||
- Repo: https://github.com/IBM/mcp-context-forge (Apache-2.0, about 450 MB; Python plus JS, Rust, Go).
|
||||
- Advisory: GHSA-vwf3-4xxj-qg6h (no CVE). < 1.0.0.
|
||||
- Flaw: prompt templates supplied by users are compiled with a plain `jinja2.Environment` (`_get_jinja_env()`) and rendered in `PromptService._render_template()`. That allows `__class__`/`__subclasses__` gadget chains, which give code execution on the gateway. The advisory quotes the 0.9.0 layout (`self._jinja_env` built in `__init__`). At the vulnerable pin the environment comes from the module-level `_get_jinja_env()`, and `__init__` stores that.
|
||||
- Fix `4d310046`: `Environment` becomes `SandboxedEnvironment`, and a regex template validator is added. The hunk is small and clear, but it sits inside a squashed feature PR (#4072, "content security US-3 and US-4", 27 files) that also adds malicious-pattern detection for resources and tools.
|
||||
- Caveats: the fix is mixed with related feature work. The template is stored first and rendered later (second order).
|
||||
- Confidence: high for the sink and the fix hunk.
|
||||
|
||||
#### lemur-acme-update-ssrf (Python, Flask). Extra `untrusted_destination`
|
||||
- Repo: https://github.com/Netflix/lemur (Apache-2.0, about 13 MB). **The repository is archived (read-only)** since after this fix. It is a real Netflix product, not a toy.
|
||||
- Advisory: CVE-2026-71303, GHSA-v5rc-cpwc-cfpr (incomplete fix of GHSA-v2wp-frmc-5q3v).
|
||||
- Flaw: `acme_url` is checked against `ACME_DIRECTORY_HOST_ALLOWLIST` only when an authority is created. `PUT /api/1/authorities/<id>` → `authorities.service.update()` stores new `options` as they are. At the next issuance, `AcmeHandler.setup_acme_client_no_retry()` reads `acme_url` and fetches it with `ClientV2.get_directory(directory_url, net)`.
|
||||
- Fix `edca0390`: `update()` re-validates `acme_url` with the now-public `validate_acme_url()`. The code change is 10 lines.
|
||||
- Caveats: stored (second-order) SSRF. The sink is outside the changed files. The outbound call runs inside the third-party `acme` library, called from project code.
|
||||
- Confidence: high. I read the diff, the handler and the PUT view.
|
||||
- The same repo has GHSA-f3qq-49m6-rw8f and GHSA-xpmj-wjcp-6pww (fixes `ed504a83`, `6dcb19b6`), which I did not take (one case per repo).
|
||||
|
||||
#### glances-restful-cors-credentials (Python, FastAPI). Extra `config_secrets`
|
||||
- Repo: https://github.com/nicolargo/glances (about 60 MB; Python plus a Vue web UI). **COPYING is LGPL-3.0; GitHub reports NOASSERTION.**
|
||||
- Advisory: CVE-2026-68517, GHSA-fp27-88fp-2phg (CWE-942). < 4.5.6.
|
||||
- Flaw: `GlancesRestfulApi.__init__()` disables credentials only when `cors_origins == ["*"]`. Starlette treats any list that contains `"*"` as allow-all and reflects the request Origin with `Access-Control-Allow-Credentials: true`. A configuration such as `cors_origins=*,https://trusted` with `cors_credentials=true` therefore exposes authenticated API data to any website.
|
||||
- Fix `89085894`: a membership test (`"*" in cors_origins`) at two sites. The code change is 4 lines, plus a test config and tests.
|
||||
- Caveats: the vulnerable behaviour depends on a non-default operator configuration. The defaults are `cors_origins=["*"]` with `cors_credentials=False`. The vulnerable pin already has a guard. The same repo has newer command-injection advisories (GHSA-59fj-m2j6-hcxh, GHSA-73wf-9vmv-5pv9, GHSA-qcpp-8x79-hhp3). I passed over them because the injection quota was full when I reached them.
|
||||
- Confidence: medium. The diff is clear, but whether the vulnerability exists depends on configuration.
|
||||
|
||||
#### qwed-sympy-eval (Python, FastAPI). Injection continuation
|
||||
- Repo: https://github.com/QWED-AI/qwed-verification (Apache-2.0, about 30 MB, 59 stars, created 2025-12). It is small but a real product (PyPI `qwed`), not a training app.
|
||||
- Advisory: CVE-2026-55585, GHSA-q27q-98j4-9pfv. < 5.1.2.
|
||||
- Flaw: `verify_math()` reads `request.get("expression")`, applies a cosmetic regex, and calls SymPy `parse_expr()`, which uses `eval` internally, with no restricted `global_dict`. `_verify_item()` in batch.py does the same with `item.query` for MATH items.
|
||||
- Fix: PR #200. The fix commit `dc9d4db7` (child of the vulnerable SHA) and its merge `6066b68c` add src/qwed_new/core/safe_parser.py (`safe_parse_expr`, validation of the input's syntax tree plus restricted namespaces) and switch every `parse_expr` call in main.py, batch.py and verifier.py to it. The merge also changes a binary `.coverage` file.
|
||||
- Caveat: the fixed SHA is a merge commit. Its first parent is the vulnerable SHA. The direct parent/child alternative is `06801f68` → `dc9d4db7`.
|
||||
- Confidence: high. I read the diffs and the vulnerable route.
|
||||
|
||||
#### xinference-llama3-tool-eval (Python, FastAPI). Injection continuation
|
||||
- Repo: https://github.com/xorbitsai/inference (Apache-2.0, about 94 MB; Python plus a TS/JS web UI). This is a model-serving platform with a FastAPI OpenAI-compatible REST API.
|
||||
- Advisory: CVE-2026-61539, GHSA-x2rj-828p-hx9m. ≤ 2.5.0, fixed in 2.7.0.
|
||||
- Flaw: when a chat request carries `tools`, the Llama3 tool-call post-processing calls `eval()` on the raw model output, with empty globals, which is not a sandbox. The attacker controls that output through the prompt.
|
||||
- Fix `1b3d220f`: `json.loads`, with a fall back to `ast.literal_eval`, at both eval sites. It touches 4 files and the change is small.
|
||||
- Caveats: the tainted value reaches `eval` only through the model. There is no direct data-flow edge from the HTTP request to the eval argument, so the source-to-sink link is model-mediated. Exploitation needs a Llama3-family model served with the Transformers backend.
|
||||
- Confidence: medium. The diff and sinks are unambiguous. The medium rating reflects the model-mediated source.
|
||||
|
||||
#### pgadmin-maintenance-sqli (Python, Flask). Injection continuation (SQLi)
|
||||
- Repo: https://github.com/pgadmin-org/pgadmin4 (about 370 MB; Python/Flask plus a JS/React UI). **The LICENSE file says PostgreSQL Licence, which is OSI-approved. GitHub reports NOASSERTION.**
|
||||
- Advisory: CVE-2026-7815, GHSA-hp84-p2gq-6fvr. < 9.15. The advisory references the short SHA `cf53953d9`, which I resolved to the full SHA.
|
||||
- Flaw: the Maintenance tool's Jinja SQL template concatenates `vacuum_index_cleanup`, `vacuum_parallel`, `buffer_usage_limit` (inside double quotes) and `reindex_tablespace` (inside double quotes) into a VACUUM/ANALYZE/REINDEX statement. `create_maintenance_job()` renders it and runs `psql --command <query>` through `BatchProcess`. Injected SQL can reach `COPY ... TO PROGRAM`.
|
||||
- Fix `cf53953d`: `validate_maintenance_data()` allow-lists the four fields before rendering, and the template switches `reindex_tablespace` to `conn|qtIdent`. 4 files, 2 of them tests.
|
||||
- Caveats: the sink is a Jinja SQL template executed by an external `psql` process, not a DB-API call. The same repo has GHSA-j74f-g7vx-fh4x (OS command injection in Import/Export, published at the same time). I passed over it because this phase took SQLi only.
|
||||
- Confidence: high. I read the diff and the vulnerable route.
|
||||
|
||||
### JavaScript / TypeScript (Node servers)
|
||||
|
||||
Source list: `list-npm.tsv` (reviewed npm advisories, CWE-prefiltered at fetch time to 89/78/77/94/95/1336/943/90/918/601/489/215/942/295/798/321/259/1188/16/346/1004, published >= 2023-01-01, sorted by publication date descending). Lines 1-59 (2026-09-24 → 2026-08-17) filled the quota of 7. Lines 60-94 (2026-08-14 → 2026-07-31) were the extra phase (RULES.md): only config_secrets and untrusted_destination considered, until +1 of each was taken.
|
||||
|
||||
"Diff seen" = I read the patch hunks of the named fix commit through `gh api repos/O/R/commits/SHA`. Every SHA below was confirmed with `gh api repos/O/R/commits/SHA --jq .sha`. Every fixed commit was confirmed to be an ancestor of the default branch (`compare/<default>...<sha>` = `behind`) unless stated.
|
||||
|
||||
#### reactpress-findall-column-sqli (TypeScript, NestJS)
|
||||
- Repo: https://github.com/fecommunity/reactpress (MIT, about 50 MB, 757 stars). Server in `server/` (NestJS + TypeORM), Next.js client alongside.
|
||||
- Advisory: CVE-2026-61685, GHSA-wmw4-mw6x-6vfm. ≤ 3.6.0, fixed in 3.7.0.
|
||||
- Flaw: `findAll(queryParams)` destructures `page`, `pageSize`, `status` and loops over every remaining query key: `query.andWhere(\`article.${key} LIKE :${key}\`)`. TypeORM binds the value but the key goes into the SQL text as an identifier. The controller passes `@Query() queryParams` straight in. The same loop exists in comment, file, page and knowledge services.
|
||||
- Fix `78ecb70a`: new `server/src/utils/query-whitelist.util.ts` (`filterByWhitelist(entity, params)`), applied in all five services. The same commit also adds HTML sanitisation after markdown rendering, a `JwtAuthGuard` on one comment route and `starter.ts` changes (9 files total).
|
||||
- Confidence: high. I read the diff and the vulnerable `findAll()` and controller at the parent.
|
||||
- Caveat: five parallel sinks; the fix commit also carries unrelated XSS and auth hardening.
|
||||
|
||||
#### 9router-kiro-region-ssrf (JavaScript, Next.js API routes)
|
||||
- Repo: https://github.com/decolua/9router (MIT, about 16 MB, ~30k stars). Next.js app with API routes.
|
||||
- Advisory: CVE-2026-56678, GHSA-6mwv-4mrm-5p3m. ≤ 0.5.2 per advisory, fixed in 0.5.6.
|
||||
- Flaw: the route reads `{ apiKey, region }` from the JSON body and calls `kiroService.validateApiKey(apiKey, region || "us-east-1")`, which calls `listAvailableProfiles()`: `fetch(\`https://codewhisperer.${region}.amazonaws.com\`, { headers: { Authorization: \`Bearer ${accessToken}\` } })`. A region like `evil.host:8443#` moves the request to another host and forwards the key. The route also reflected the upstream error body.
|
||||
- Fix `126aa244`: `assertValidAwsRegion()` (regex `^[a-z]{2}-[a-z]+-\d{1,2}$`) in `constants/oauth.js`, called before every region-interpolated URL in `kiro.js` and `providers.js`; the route stops reflecting upstream errors. 4 files, ~30 lines.
|
||||
- Confidence: high. I read the diff and the vulnerable `listAvailableProfiles()` / `validateApiKey()`.
|
||||
- Caveat: the same function family builds `https://oidc.${region}.amazonaws.com/...` in four more places, all fixed by the same commit; the advisory's exploited path is the codewhisperer one. The repo has three more SSRF advisories and a hardcoded-JWT-secret advisory (GHSA-jphh-m39h-6gwx, CWE-798) that I did not take (one case per repo).
|
||||
|
||||
#### omniroute-acp-agent-rce (TypeScript, Next.js API routes)
|
||||
- Repo: https://github.com/diegosouzapw/OmniRoute (MIT; **very large: GitHub reports about 1 GB, 75 MB of TypeScript**). Default branch `release/v3.8.51`.
|
||||
- Advisory: CVE-2026-88062, GHSA-hf57-cqmx-p4gr. ≤ 3.8.50; the GHSA lists no patched version but references the fix commit.
|
||||
- Flaw: `POST /api/acp/agents` accepts `binary` and `versionCommand`, stores the custom agent and calls `refreshAgentCache()`. `detectAgent()` runs `execFileSync(probe.command, probe.args)`. `resolveVersionProbe()` only checks that the first token equals the (also attacker-supplied) `binary` and that there are no shell metacharacters, so `binary: "node"`, `versionCommand: "node -e ..."` runs arbitrary code.
|
||||
- Fix `60829241`: in `resolveVersionProbe()`, untrusted probes may only carry a single recognised version flag (`SAFE_VERSION_PROBE_ARG`). The same commit closes three other advisories (db-backups auth path, uppercase path authz bypass, a spawn-capable prefix), 10 files.
|
||||
- Privilege: unauthenticated when `requireLogin=false` or during first-run bootstrap; a management session otherwise.
|
||||
- Confidence: medium. The diff and the vulnerable sink are clear; the vulnerable pin already has a guard (metacharacter filter and binary match), the fix bundles four advisories, and the repo size is a practical concern.
|
||||
|
||||
#### cct-studio-server-cmdi (JavaScript, Express)
|
||||
- Repo: https://github.com/davila7/claude-code-templates (MIT, about 250 MB, mostly template content in many languages). The Express server is `cli-tool/src/sandbox-server.js`, started by `npx claude-code-templates --studio`.
|
||||
- Advisory: CVE-2026-73222, GHSA-79wm-x847-7cvg. ≤ 1.29.2, fixed in 1.29.4.
|
||||
- Flaw: two POST routes pass body fields to `child_process.spawn(cmd, args, { shell: true })`: `prompt` (via `finalPrompt` in `executeLocalTask()`) and `agentName` (`/api/install-agent` and `checkAndInstallAgent()`). With `shell: true` the args are joined and parsed by `sh -c`. The server listens on all interfaces and sends `Access-Control-Allow-Origin: *`.
|
||||
- Fix `bc4618b0` ("Merge commit from fork", single parent): drops `shell: true`, adds a slug regex for `agentName`, restricts CORS to the localhost UI origin with a 403 for other origins, binds to 127.0.0.1. One source file (+57/-17) plus version bumps and CHANGELOG.
|
||||
- Confidence: high. I read the diff; the vulnerable code is visible in the removed lines.
|
||||
- Caveat: this is a developer-local server shipped in a CLI package, not a deployed web app. The same fix also removes a wildcard CORS header, so the case carries a secondary config_secrets aspect.
|
||||
|
||||
#### unleash-addon-webhook-ssrf (TypeScript, Express)
|
||||
- Repo: https://github.com/Unleash/unleash (AGPL-3.0, about 313 MB).
|
||||
- Advisory: CVE-2026-63004, GHSA-5vf6-jrqr-78fj. < 7.5.2, 7.6.0–7.6.4, 8.0.0–8.0.1.
|
||||
- Flaw: every addon (webhook, Slack, Teams, Datadog, New Relic) sends the configured `parameters.url` to `Addon.fetchRetry()`, which calls `ky(url, ...)` with no host checks. The webhook addon also forwards the configured `Authorization` header and custom headers. The request is blind, but success and status land in the integration-event log.
|
||||
- Fix `2100db76` (main, 8.x): new `src/lib/addons/validate-url.ts` (private/loopback/link-local/metadata deny by default, allow-list, http/https only) and `fetchPinned()` with a pinned DNS lookup and `redirect: 'manual'` in `addon.ts`. The advisory also lists two backports, `d45f99df` and `d862562a` (7.x lines), which are not ancestors of main.
|
||||
- Confidence: high. I read the addon.ts diff and the vulnerable `fetchRetry()` / webhook call site at the parent.
|
||||
- Caveat: stored input; the request fires later on a feature event, not in the request that sets the URL.
|
||||
|
||||
#### nocobase-backup-restore-cmdi (TypeScript, Koa)
|
||||
- Repo: https://github.com/nocobase/nocobase (**repo licence: "NocoBase License Agreement", non-OSI, GitHub NOASSERTION; the `plugin-backups` package.json declares Apache-2.0**; about 358 MB).
|
||||
- Advisory: CVE-2026-55410, GHSA-p853-83gj-wjj3. `@nocobase/plugin-backups` < 2.1.19.
|
||||
- Flaw: `PostgresAdapter.restore()` builds `${restoreCmd} -U ... -n ${srcSchema} -d ${database} ...` where `srcSchema` is the `database.schema` value read from `_metadata.json` in the uploaded backup, and runs it through the module-level `run()` → `promisify(exec)`. `#restoreSchema()` also puts `srcSchema` into SQL. Reached when the restore action is called with `force`.
|
||||
- Fix: the advisory references `0e1aba1b7b112ffc841588963f7343c00edd9806` (switch from exec strings to spawn argument arrays). **At that commit's parent the schema value is already validated**: `aecc6758` ("reject unsafe PostgreSQL schema names", 2026-06-12, 11 lines) added `assertPostgresSchemaIdentifier()` for `schema` and `schemaOption` in `restore()`. I use `aecc6758` and its parent as the pair because it is the commit that closes the reported path. `0e1aba1b` (parent `3f4ec12fc60157ea15c41ea3f696f0ddb7366e7c`) is the follow-up hardening.
|
||||
- Confidence: medium. I read both diffs and the vulnerable `restore()` / `run()` at `a89b1ea0`. Medium because the advisory's own fix reference points at the later commit, and because of the licence.
|
||||
- Caveat: sink input comes from a file inside an uploaded archive, not directly from a request field. The same repo has another advisory (GHSA-p849-8hwh-84j9, SQLi, 2026-07-31) that I did not take (one case per repo, and it came up in the extra phase).
|
||||
|
||||
#### medplum-external-auth-open-redirect (TypeScript, Express)
|
||||
- Repo: https://github.com/medplum/medplum (Apache-2.0, **large: about 440 MB**, monorepo; the server is `packages/server`).
|
||||
- Advisory: CVE-2026-53728, GHSA-m44r-7c5h-m6mj. `@medplum/core` ≤ 5.1.5 (the flaw is in `packages/server`), fixed in 5.1.6.
|
||||
- Flaw: `externalCallbackHandler()` parses `req.query.state` as JSON and calls `getClientRedirectUri(client, body.redirectUri, true)`. With `allowPartial` the function returns the requested URI whenever it `startsWith` a registered one (`http://cb.example` accepts `http://cb.example.evil.com/`). The exact-match check afterwards only logs. The handler then appends `login` and `code` and calls `res.redirect()`.
|
||||
- Fix `7ae10035`: exact match first; partial match only when the project sets `allow-dangerous-redirect`, and then only same origin with a path prefix (`isAllowedPartialRedirectUri`). Server change is ~40 lines in two files; the rest of the 7 files are FHIR definitions, docs and tests.
|
||||
- Confidence: high. I read the server diff and the vulnerable handler at the parent.
|
||||
- Caveat: the vulnerable pin already has a (prefix-match) guard.
|
||||
|
||||
#### flowise-ipv4-mapped-ssrf (TypeScript, Express) — extra, untrusted_destination
|
||||
- Repo: https://github.com/FlowiseAI/Flowise (Apache-2.0 for everything outside `packages/server/src/enterprise`, commercial licence inside it; GitHub NOASSERTION; about 83 MB).
|
||||
- Advisory: CVE-2026-69257, GHSA-c6xh-wv4j-ppv5. ≤ 3.1.2, fixed in 3.1.3. Bypass of the earlier fix for CVE-2026-31829.
|
||||
- Flaw: `isDeniedIP()` compares the resolved address to CIDR deny entries only when `parsedIp.kind() === parsedRange.kind()`. An IPv4-mapped IPv6 address (`::ffff:169.254.169.254`) has kind `ipv6`, so every IPv4 deny rule is skipped and `secureFetch()` / `secureAxiosRequest()` go ahead.
|
||||
- Fix `0fc76920`: normalise IPv4-mapped addresses (and deny entries) to IPv4 before matching, strip IPv6 brackets in `checkDenyList()` and `resolveAndValidate()`. One source file (+54/-9) plus a new test file.
|
||||
- Confidence: medium. I read the diff and the vulnerable `isDeniedIP()`. The flaw is in the guard itself; the request call sites are in many node implementations and I did not trace a specific one.
|
||||
- Caveat: vulnerable pin already has a guard; exploitation needs attacker-controlled DNS (AAAA record).
|
||||
|
||||
#### nx-graph-server-wildcard-cors (TypeScript, Node http) — extra, config_secrets
|
||||
- Repo: https://github.com/nrwl/nx (MIT; **very large: about 1 GB**).
|
||||
- Advisory: CVE-2026-54753, GHSA-g2r8-wvmj-jf5w. 17.0.4 – < 22.7.2 and 23.0.0-beta.0 – beta.1.
|
||||
- Flaw: the `nx graph` local server (`http.createServer` in `startServer()`) set `Access-Control-Allow-Origin: *` on every response, so any page could read the project graph and the output of `/help`, which runs a target's configured help command with `execSync`.
|
||||
- Fix: the advisory lists two identical 2-line commits. `e59122c5` is on master (parent `6ac9f82b`); `7620c1ff` (parent `0591aa5f`) is the 22.x backport and is not an ancestor of master. I use the master pair.
|
||||
- Confidence: high for the diff (2 lines removed).
|
||||
- Caveat: developer-local server inside a CLI, not a deployed web app. Huge repo.
|
||||
|
||||
#### budibase-mongo-template-nosqli (TypeScript, Koa) — SQLi/NoSQLi continuation phase
|
||||
- Repo: https://github.com/Budibase/budibase (GPL-3.0 per root LICENSE and `packages/server/package.json`; GitHub NOASSERTION; **very large, about 790 MB**; also contains a `pro` package).
|
||||
- Advisory: GHSA-pmpg-2mxq-6xwr (no CVE, published 2026-07-24). `@budibase/server` ≤ 3.38.1; release 3.39.9 referenced. GHSA-qw6m-8fw2-2v64 describes the same flaw through `POST /api/v2/queries/:queryId` and references the PR #18907 merge commit `2d6c1d17`.
|
||||
- Flaw: `enrichContext()` runs every string field of a saved query through handlebars with `noEscaping: true`, then `JSON.parse`s the `json`/`customData`/`requestBody` result. A binding value containing `"` and `{` breaks out of its JSON string and adds MongoDB operators (e.g. `$ne`), so a BASIC user reads documents the builder's filter was meant to hide. `MongoIntegration.read()` passes the parsed object to `collection.find(json)`. `validateQueryInputs()` in the query controller blocks only `{{ }}` in parameter values.
|
||||
- Fix `dd8c0654` ("Fix JSON template binding handling", on the PR branch, ancestor of master): new `processJsonStringSync()` in packages/string-templates that substitutes values in JSON-aware form, used by `enrichContext()` for the three JSON fields.
|
||||
- Confidence: medium. I read the diff and the vulnerable `enrichContext()` and `read()` at the parent. Medium because master already had an interim escaping helper (`jsonEscapeForContext`) when PR #18907 was merged; the merge `2d6c1d17` replaced it. The pair `b7e63df7` → `dd8c0654` is on the feature branch, where the parent has no escaping at all. Use that pair, not the merge.
|
||||
- Caveat: the sink is a datasource integration; the injected structure goes into a MongoDB query, not SQL.
|
||||
|
||||
#### fuxa-tdengine-escape-sqli (JavaScript, Express/Socket.IO) — SQLi/NoSQLi continuation phase
|
||||
- Repo: https://github.com/frangoteam/FUXA (MIT; SCADA/HMI web server; **large: about 600 MB**, mostly the Angular client and assets).
|
||||
- Advisory: CVE-2026-47720, GHSA-h9fj-c2qr-76g2. `fuxa-server` ≤ 1.1.14-1243 per GHSA (no patched package version); the text says fixed in 1.3.2. **The advisory references no commit.** I found the fix from the path history of the connector: `b78f6159` ("fix: tdengine string escaping (#2343)", 2026-05-03). It is in tag v1.3.2 and not in v1.3.1.
|
||||
- Flaw: `escapeTdString()` only doubles single quotes. TDengine treats `\'` as an escaped quote, so a tag id like `x\' OR 1=1--` closes the string and appends SQL in `getDaqValue()`'s SELECT. The ids come from `query.sids` in `GET /api/daq` (guest-level `secureFnc`) or the Socket.IO `DAQ_QUERY` handler, which has no authorization check.
|
||||
- Fix `b78f6159`: also escape backslashes (one line), plus a unit test.
|
||||
- Confidence: medium. I read the diff and the vulnerable connector and DAQ route at the parent. Medium because I inferred the fix commit from history rather than an advisory reference, and because the path only exists when TDengine is the configured DAQ backend.
|
||||
- Caveat: the vulnerable pin already has a guard (incomplete escaping). An earlier commit `5b59296c` (2026-03-08, "parameterize queries") introduced `escapeTdString`; before it the connector concatenated raw values.
|
||||
|
||||
### PHP web applications (not WordPress)
|
||||
|
||||
Source list: `list-composer.tsv` (GitHub Advisory Database, ecosystem composer, reviewed, CWE-prefiltered to the three families, published >= 2023-01-01, newest first). The quota of 4 was filled at the 5th advisory examined (2026-09-23). After that I continued newest first only for the extra `untrusted_destination` and `config_secrets` slots (RULES.md), which gave one extra of each.
|
||||
|
||||
#### contao-feed-reader-ssrf (PHP, Symfony-based CMS)
|
||||
- Repo: https://github.com/contao/contao (LGPL-3.0; monorepo, about 150 MB, PHP 10 MB, JS 0.2 MB, Twig 0.5 MB).
|
||||
- Advisory: CVE-2026-57232, GHSA-87mg-5grr-rhwh. Affects 5.3.35–5.3.47 and 5.4.0–5.7.8.
|
||||
- Flaw: the Feed Reader front-end module splits the module's `rss_feed` text into URLs and passes each to `$this->feedIo->read($url, new Feed())`. The FeedIo adapter used the plain `@psr18.http_client`, with no scheme check and no private-network block, so loopback, RFC1918 and cloud metadata hosts are fetched.
|
||||
- Fix `27f62018` (5.7 line; the 5.3 backport is `53b939ff2c4718e3a1d7c54ddd8886e9370618e4`, parent `96b216faa19cee9a3a21a103300b7c85d8f759d5`): new `core-bundle/src/HttpClient/NoPrivateNetworkExceptRootPagesHttpClient.php` (Symfony `NoPrivateNetworkHttpClient` variant that allows the site's own root pages), and `services.yaml` wires `contao.feed.http_adapter` and the crawler to it.
|
||||
- Caveat: **the sink file is not in the diff.** The fix is a dependency-injection rewiring in YAML plus a new decorator class. `FeedReaderController.php` is byte-identical before and after. The fixed revision is only distinguishable by the service configuration.
|
||||
- Confidence: high. I read the diff and the sink at the vulnerable pin. The source is stored configuration written by a back-end user, not a request parameter at the sink.
|
||||
|
||||
#### formie-form-settings-ssrf (PHP, Craft CMS plugin)
|
||||
- Repo: https://github.com/verbb/formie (about 196 MB; PHP 4.8 MB, JS 0.6 MB, TS small). **License: Craft License (proprietary, commercial); not OSI.** Must be cleared before publishing any result.
|
||||
- Advisory: CVE-2026-76086, GHSA-v3f3-cmj4-cvj9. Incomplete remediation of GHSA-cvpc-hccg-wmw4 (the `form-settings` action was left out of the 3.1.28 permission gate).
|
||||
- Flaw: `actionFormSettings()` had no permission check and called `$integration->setAttributes($settings, false)` with the request's `settings` array (unsafe mass assignment). An attacker can overwrite `apiUrl` (and similar host properties) of a configured integration. `getFormSettings()` then makes the integration call its API through `defineClient()`/`request()`, sending stored credentials to the attacker-chosen host and returning the response body in JSON.
|
||||
- Fix `6735fe4a` (Craft 5 line; Craft 4 line is `dde7799dfa7e4d0a11e28754ad8544dba62d5def`): `requireCpRequest()`, a per-form integration permission check, and an allowlist filter that drops keys containing `url`, `host`, `key`, `token`, etc.
|
||||
- Caveat: the outbound request is several dynamic-dispatch hops away from the changed file (controller → integration subclass `defineClient()` → base `Integration::request()` → Guzzle). The sink is not in the diff. The case is equally a missing-authorization / mass-assignment bug.
|
||||
- Confidence: medium. Diff read and the concrete ActiveCampaign sink checked at the vulnerable pin. The weakness is the licence and the indirect sink.
|
||||
|
||||
#### sulu-smart-content-sql2-injection (PHP, Symfony CMS)
|
||||
- Repo: https://github.com/sulu/sulu (MIT; about 165 MB, PHP 11 MB, JS 5 MB admin UI). Default branch is 3.x; this fix is on the 2.6 line.
|
||||
- Advisory: CVE-2026-92692, GHSA-jg26-q8hg-3pq4. < 2.6.25 and 3.0.0–3.0.7.
|
||||
- Flaw: `CategoryRequestHandler::getCategories()` reads `?categories=`, explodes on commas and trims. `ContentType` passes the list as `websiteCategories`. `QueryBuilder::buildWhere()` → `buildCategoriesWhere()` appends `'page.[' . $name . '] = ' . $category` to a JCR-SQL2 WHERE clause. The tag filter already validated numbers; the category and target-group paths did not.
|
||||
- Fix `d19c0148`: three `(int)` casts in QueryBuilder.php plus a new unit test. The diff is 3 changed lines of code.
|
||||
- Caveat: the query language is JCR-SQL2 (PHPCR content repository, executed through Jackalope), not SQL sent to the RDBMS. The impact is read/enumeration of content nodes and DoS. Record it as query-language injection, not classic SQLi. 3.0.8 has a separate fix commit (not examined).
|
||||
- Confidence: high. I read the diff, the sink, and the source-to-sink path at the vulnerable pin.
|
||||
|
||||
#### freeform-attribute-twig-ssti (PHP, Craft CMS plugin)
|
||||
- Repo: https://github.com/solspace/craft-freeform (about 100 MB, PHP 6 MB). **License: Craft License (proprietary); not OSI.**
|
||||
- Advisory: CVE-2026-73858, GHSA-gxrg-x694-283w. 5.0.0–5.10.13. "Limited" SSTI: the isolated Twig environment only exposed constants such as `PHP_VERSION` and `CRAFT_BASE_PATH`; RCE was not shown.
|
||||
- Flaw: `Attributes::toHtmlTagArray()` renders every attribute key and value through `IsolatedTwig::render()` with the form properties. When a submitted field value comes back as the `value` attribute, attacker text is evaluated as a Twig template.
|
||||
- Fix `5f755532` (PR #1986): skips Twig rendering for the `value`, `name` and `id` attributes. The diff is 5 lines. The commit's author date is 2025-05-13, but it only lands after v5.10.13 (compare: ahead of v5.10.13, behind v5.10.14).
|
||||
- Caveat: attribute keys and other attribute values are still rendered through Twig in the fixed version. It is a deny-list fix. I inferred the exact path from the submission to the `value` attribute from the advisory and did not trace it in the field classes.
|
||||
- Confidence: medium (licence, limited impact, source path partly inferred).
|
||||
|
||||
#### smw-uriresolver-open-redirect (PHP, MediaWiki extension), extra untrusted_destination slot
|
||||
- Repo: https://github.com/SemanticMediaWiki/SemanticMediaWiki (GPL-2.0-or-later per composer.json, GitHub says NOASSERTION; about 66 MB, JS 0.9 MB).
|
||||
- Advisory: CVE-2026-77609, GHSA-hw3m-8j5x-94ff. ≤ 7.1.0, fixed in 7.2.0. The advisory lists no commit. I found the fix through the history of `SpecialURIResolver.php` and confirmed it is between the 7.1.0 and 7.2.0 tags.
|
||||
- Flaw: `execute($query)` decodes the subpage, builds a Title with `TitleFactory::newFromText()` (interwiki prefixes such as `mw:` resolve off-site) and issues `$out->redirect($title->getFullURL(), '303')` with no host check.
|
||||
- Fix `765a6ac2`: new `isLocalRedirectTarget()` compares the target host with the server host at the sink and rejects userinfo. It shows a bad-title page otherwise.
|
||||
- Caveat: the redirect primitive (`OutputPage::redirect`) and URL building live in MediaWiki core, which is not in this repository. A checkout is an extension, not a runnable app.
|
||||
- Confidence: high. I read the diff and the vulnerable function.
|
||||
|
||||
#### pheditor-default-admin-password (PHP, single-file web file editor), extra config_secrets slot
|
||||
- Repo: https://github.com/pheditor/pheditor (MIT, 182 stars, one 97 KB PHP file, not archived).
|
||||
- Advisory: CVE-2026-55579, GHSA-p4h7-p9rj-2pq2. 2.0.1–2.0.5. The advisory lists no commit. The 2.0.5...2.0.6 compare has exactly one commit, `a64eb935`.
|
||||
- Flaw: `define('PASSWORD', ...)` holds the unsalted SHA-512 of `admin`. The login block accepts it and nothing forces a change, so a default install gives unauthenticated access to the editor, upload and terminal.
|
||||
- Fix `a64eb935`: when `PASSWORD` still equals `hash('sha512','admin')`, login shows a forced "change password" form that rewrites the `define` line in the file. The same commit also tightens the terminal command filter (`|`, backtick, newlines), which is the fix for sibling advisory GHSA-wg4w-wr5q-6vjc (command injection).
|
||||
- Caveat: **the hardcoded default hash is still present, unchanged, on line 11 of the fixed file.** The fix is a behavioural gate, not removal of the constant, so the fixed revision still contains the same literal. The commit also mixes in an unrelated command-injection fix. Ground truth for "fixed = clean" is therefore doubtful for this family.
|
||||
- Confidence: low (see caveat). Kept because it is the first config_secrets advisory newest-first. The next config_secrets candidate in the list, not examined because the slot was filled, is Kimai GHSA-jr9p-4h4j-6c58 (default `APP_SECRET` in the Docker image, CWE-1188).
|
||||
|
||||
#### pimcore-studio-datefilter-sqli (PHP, Symfony bundle, SQLi continuation phase)
|
||||
- Repo: https://github.com/pimcore/studio-backend-bundle (about 10 MB, PHP 7.9 MB). It is the REST backend of Pimcore Studio, a Symfony bundle installed into a Pimcore application, so a checkout is not a runnable app by itself. **License: Pimcore Open Core License (POCL, June 2025), not OSI.**
|
||||
- Advisory: CVE-2026-55208, GHSA-79cw-hfcc-7mw9. < 2025.4.6 and 2026.1.0–2026.1.5. The advisory is filed under pimcore/pimcore, but the code and fix are in studio-backend-bundle.
|
||||
- Flaw: listing endpoints map a `columnFilters` array into `ColumnFilter` objects. `DateFilter::applyDateFilter()` wraps the user-supplied `key` in literal backticks and concatenates it into the condition string. Because the bound parameters are the fixed names `:minTime`/`:maxTime`, a backtick in the key breaks out and allows time-based blind SQLi. EqualsFilter, LikeFilter and the Note FilterService share the pattern (there the key also names the bound parameter, which limits exploitation).
|
||||
- Fix `f532428c` (PR #1883): all four sites use `$this->dbResolver->get()->quoteIdentifier(...)`. About 30 changed code lines plus tests.
|
||||
- Caveat: the SQL is executed in Pimcore core (`AbstractListing::addConditionParam()` → Doctrine DBAL), which is not in this repository. The sink in-repo is the string build handed to the listing object.
|
||||
- Confidence: high. Diff read and vulnerable pin checked.
|
||||
|
||||
#### froxlor-admin-ip-second-order-sqli (PHP, server-management panel, SQLi continuation phase)
|
||||
- Repo: https://github.com/froxlor/froxlor (LGPL-2.1 per GitHub; about 30 MB).
|
||||
- Advisory: CVE-2026-54348, GHSA-w27m-rmmf-g5w4. < 2.3.8.
|
||||
- Flaw: `Admins::add()` / `update()` store any `ipaddress` array as `json_encode($ipaddress)` in `panel_admins.ip` (the write itself is a prepared statement). Later, `IpsAndPorts::listing()` and `listingCount()` run `implode(", ", json_decode($this->getUserDetail('ip'), true))` into `WHERE id IN (...)`, and that string reaches `Database::prepare()`. `Domains::validateIpAddresses()` does the same.
|
||||
- Fix `a1eaca5a` ("verify numeric values for ipaddress-id's"): `array_filter(..., 'is_numeric')` and `array_map('intval', ...)` at the store and at both read sites. 3 files, about 20 lines.
|
||||
- Caveat: second-order. The source (API parameter) and sink (a later API call by the poisoned account) are in different requests, joined through the database. The attacker must already be an admin with an API key.
|
||||
- Confidence: high. Diff read, and the sink checked at the vulnerable pin.
|
||||
|
||||
### WordPress plugins (PHP+JS)
|
||||
|
||||
#### wp-s2member-tracking-eval (PHP+JS, WordPress)
|
||||
- Repo: https://github.com/wpsharks/s2member (PHP 4.0 MB, JS 0.15 MB; about 28 MB of git data). Default branch `dev`. It has a git submodule `phings` (build tooling only).
|
||||
- Advisory: CVE-2026-19804, GHSA-fgx2-6hhq-hwhw (Wordfence, unreviewed GHSA). "Up to and including 260814". Unauthenticated RCE.
|
||||
- Flaw: the PayPal return handler replaces `%%first_name%%` (and last/full name) in the admin's Signup Tracking Codes template with the payer's name. The only escaping is `esc_refs()`, which strips regex back-references, not PHP tags. The result is saved in a transient. `display_signup_tracking_codes()` (on `wp_footer`/`login_footer`) reads the transient and runs it through `c_ws_plugin__s2member_utilities::evl()`, which does `eval('?>'.trim($code))`. The same pattern is in `display_modification_tracking_codes()`, `display_ccap_tracking_codes()` and `display_sp_tracking_codes()` (the last one calls `eval("?>".$code)` directly).
|
||||
- Fix `6c622839` ("Update notification template handling", 9 files, small): removes the `evl()`/`eval` calls in all four display functions and echoes the code through `do_shortcode()` behind a new filter. Related template-handling changes in the admin menu pages and PayPal notify handlers are part of the same commit.
|
||||
- Caveats: the RCE chain also needs the site-global proxy verification key, which the advisory says leaked in PayPal Checkout AJAX responses. That leak was addressed separately (probably `37c29337d9256bf17a2bd115ac1d16bf07923061` "Fix PayPal proxy request handling", which I did not read). The advisory's version bound (≤ 260814) does not match the GitHub history, where the eval removal landed on 2026-09-14 and shipped in v260917. The source-to-sink path crosses a WordPress transient (second-order). Confidence medium because of the version mismatch and the two-part chain. The eval sink and its removal are unambiguous.
|
||||
|
||||
#### wp-directorist-avatar-ssrf (PHP+JS, WordPress)
|
||||
- Repo: https://github.com/sovware/directorist (JS 21 MB of built assets, PHP 4.8 MB). Default branch `trunk`. Tags v8.9.4 = `d0fbb4f8c63582d43c0860e9add24d95b04e4a43`, v8.9.5 = `a3aa54a9a58e3c6d7784302dbe48a16ddcc14712` (101 commits apart, so use the commit pair).
|
||||
- Advisory: CVE-2026-84046, GHSA-6q63-3w56-m722 (WPScan). < 8.9.5, subscriber and above.
|
||||
- Flaw: the REST users controller passes `$request['avater']['src']` to `directorist_rest_upload_image_from_url()`, which calls WordPress `download_url($image_url)` after only `wp_parse_url`/`esc_url_raw`. The same helper is also called from the categories, locations and tools code paths.
|
||||
- Fix `1a67825d` ("readme update & security fix", 7 files): adds `directorist_rest_validate_remote_image_url()` (http/https only, rejects localhost, resolves the host and rejects private or reserved ranges) and forces `reject_unsafe_urls` on the download. The same commit also tightens permission checks in the orders, users and favorites controllers.
|
||||
- Caveat: WordPress core's `download_url()` already uses `wp_safe_remote_get()`, which blocks 127/8, 10/8, 172.16/12 and 192.168/16 but not 169.254/16 (cloud metadata). The practical exposure is therefore narrower than the advisory text suggests. Confidence medium for that reason. The sink and the fix are clear.
|
||||
|
||||
#### wp-gopay-log-filter-sqli (PHP+JS, WordPress/WooCommerce)
|
||||
- Repo: https://github.com/gopaycommunity/gopay-woocommerce-integration (small: PHP 0.14 MB; the plugin ships a committed `vendor/` with the GoPay SDK, but the sink is in the plugin's own code). Plugin slug `gopay-gateway`.
|
||||
- Advisory: CVE-2026-75959, GHSA-88c4-2f34-57h5 (Wordfence). ≤ 1.0.36 per advisory, shop manager and above.
|
||||
- Flaw: admin/views/log.php reads `filter_input(INPUT_GET, 'log_table_filter')` and puts `strtoupper()` of it into `sprintf("... REGEXP '[\w\W]*%s[\w\W]*' ...")` for both the COUNT and the page query, without escaping. The view is included from the admin class's log submenu page (capability `manage_woocommerce`).
|
||||
- Fix `7190c985` ("fix authenticated SQL injection in log admin view (CVE-2026-75959)"): one file. Both queries become `$wpdb->prepare(... LIKE %s ...)` with `$wpdb->esc_like()`.
|
||||
- Caveats: the sink is at file scope in a view template, not inside a function. The advisory says ≤ 1.0.36, but the fix commit precedes the 1.0.35 bump (`329113ac`). The commit message names the CVE, so I trust the commit. Confidence high.
|
||||
|
||||
#### wp-user-registration-login-redirect (PHP+JS/TS, WordPress)
|
||||
- Repo: https://github.com/themegrill/user-registration (PHP 4.9 MB, JS 2.5 MB, TS 0.2 MB; about 75 MB). Tags 5.2.7 = `c532e6aee2efc73ab711bdb6f8b36a06674e5d7d`, 5.2.8 = `28e2e931c3c0b13a8e1b577d56d6224c494fdae0`. That pair is 360 commits and 117 files apart, so use the commit pair.
|
||||
- Advisory: CVE-2026-80072, GHSA-w78c-fx7q-9x8m (WPScan). < 5.2.8, unauthenticated.
|
||||
- Flaw: `ur_process_login()` takes `$redirect = esc_url_raw(wp_unslash($post['redirect']))` (or the raw referer), then calls `wp_redirect(wp_validate_redirect($redirect, $redirect))`. The fallback equals the input, so validation is a no-op. On the AJAX-login path it returns `$redirect` in JSON, and the front-end JS navigates to it. The failed-login branch also redirects to `wp_get_raw_referer()` with `wp_redirect`.
|
||||
- Fix `75c940da` ("#1408 Fix - Prevent membership privilege escalation and open redirect after login", 8 files): `wp_validate_redirect($redirect, get_home_url())` before both branches, `wp_safe_redirect` in both success and failure, and an `allowed_redirect_hosts` filter for the admin-configured external URL. The same commit also fixes a membership privilege escalation in modules/membership. Those hunks are separate. A later commit, `e537e78738aff363b5f250253d2b64bf9dc79c80` "Fix - Open redirect after login via unvalidated redirect parameter (#1417)", has an empty diff against its parent (already applied).
|
||||
- Confidence high: the diff is readable and the no-op validation is visible at the vulnerable pin.
|
||||
|
||||
#### wp-groundhogg-confirm-redirect (PHP+JS, WordPress)
|
||||
- Repo: https://github.com/groundhoggwp/groundhogg (PHP 4.8 MB, JS 2.0 MB). Tags 4.7.1 = `3cc86bfa09e8bb85cbf15f7c8affa727c6b65961`, 4.7.2 = `0f19a08505d0bd0ec27dd4668ac39f00ad3d0d5d` (17 commits).
|
||||
- Advisory: CVE-2026-81741, GHSA-m96f-c5vh-h7cm (WPScan). < 4.7.2, unauthenticated.
|
||||
- Flaw: in the email-preferences template, `case 'confirm':` sets `$redirect_to = esc_url_raw(sanitize_text_field(get_url_var('redirect_to')))`, passes it through a filter and calls `wp_redirect($redirect_to)` (with a phpcs ignore comment for SafeRedirect).
|
||||
- Fix `028247d4` ("use safe redirect"): one line, `wp_redirect` → `wp_safe_redirect`. Its child `2f03c848` only removes the phpcs comment.
|
||||
- Caveats: the sink is at file scope in a template. The preferences page needs a contact context (the tracking/permissions link from an email), which the advisory treats as a crafted link. Confidence high. There is no LICENSE file in the repo, but the plugin header says GPLv3.
|
||||
|
||||
#### wp-stripe-payments-thankyou-redirect (PHP+JS, WordPress), extra untrusted_destination pick
|
||||
- Repo: https://github.com/Arsenal21/stripe-payments (PHP 1.5 MB, JS 0.13 MB). The plugin lives in the `stripe-payments/` subdirectory.
|
||||
- Advisory: CVE-2026-81423, GHSA-rvj6-j43w-hcgw (WPScan). < 2.1.4, unauthenticated.
|
||||
- Flaw: `process_ipn()` (hooked on `wp_loaded` when POST `asp_process_ipn` is set) takes POST `asp_thankyou_page_url`, base64-decodes it into `$this->asp_redirect_url`, and `ipn_completed()` ends with `wp_redirect($this->asp_redirect_url)`.
|
||||
- Fix `fb1f647d` (2 files, 5 lines): `wp_validate_redirect(..., home_url('/'))` plus `wp_safe_redirect`.
|
||||
- Caveats: `process_ipn()` first checks the `asp_payment_form_nonce` (a front-end nonce on any page with a payment form), and `ipn_completed()` runs after payment processing (or on the "already processed" branch). The attacker needs a victim to submit such a request. Confidence medium, because of those preconditions. The sink and the fix are unambiguous. Taken as the "+1 untrusted_destination" extra after the 5-case quota, per RULES.md.
|
||||
|
||||
## Open decisions for the maintainer
|
||||
- **Licences that are not OSI or not verified.** These must be resolved before any result is published:
|
||||
- Not OSI: Open WebUI (#4, Open WebUI License), NocoBase (#18, NocoBase License), Formie and Freeform (#25, #27, proprietary Craft License), Pimcore Studio (#30, Pimcore Open Core License). LiteLLM (#3) and Flowise (#20) have enterprise directories under a commercial licence; the core is MIT or Apache.
|
||||
- GitHub reports NOASSERTION, but the repo's own files state an OSI licence: Glances (#9), pgAdmin (#12), Budibase (#22), Semantic MediaWiki (#28).
|
||||
- All six WordPress plugins (#32–#37) have no GitHub licence metadata and rely on the plugin-header GPL declaration. s2Member's header does not state a GPL version.
|
||||
- **Fix commits the advisory does not name (pair inferred from file history):** LMDeploy (#2), FUXA (#23), Semantic MediaWiki (#28). NocoBase (#18) uses an earlier commit than the one the advisory cites, because at the cited commit's parent the reported path was already closed.
|
||||
- **Fixed SHA is a merge commit:** qwed (#10). The PR commit `dc9d4db72ca4b4ae3f96d0e6a0c27a9e38a06f61` is a plain child of the vulnerable SHA if a non-merge pair is preferred.
|
||||
- **Weak ground truth:**
|
||||
- Pheditor (#29, low): the default hash is still in the fixed file.
|
||||
- xinference (#11): the input reaches `eval` only through model output.
|
||||
- s2Member (#32): the advisory's version bound does not match the history, and exploitation needs a second key leak.
|
||||
- Glances (#9) and Chainlit (#6): vulnerable only with a non-default configuration.
|
||||
- Directorist (#33): WordPress core already blocks most private ranges.
|
||||
- **Sink outside the repository or outside the changed files:**
|
||||
- The sink's final call is outside the repo: Semantic MediaWiki (#28, MediaWiki core), Pimcore Studio (#30, Pimcore core), Lemur (#8, the `acme` library).
|
||||
- The sink is outside the fix's changed files: LiteLLM (#3), Contao (#24).
|
||||
- **Scope edge cases:** winml-cli (#5), cct-studio (#16) and nx (#21) are servers started from a developer CLI, not deployed web apps. Lemur (#8) is archived.
|
||||
- **Guard bypasses (the vulnerable pin already has a check):** #1, #2, #4, #9, #15, #19, #20, #23.
|
||||
- **Very large repositories:** LiteLLM (about 1.7 GB), OmniRoute and nx (about 1 GB each), Budibase (about 790 MB), FUXA (about 600 MB), Open WebUI, ContextForge and medplum (about 440–450 MB).
|
||||
- **Family balance:** in strict newest-first order, recent advisories are dominated by SSRF. A selection of 12–15 with "the rest injection" will need most of the 16 injection rows. Taking fewer untrusted_destination rows is a selection choice; no case was excluded for being hard.
|
||||
|
||||
## Rejection log
|
||||
|
||||
Total: 261 advisories examined (Python 72, JS/TS 112, PHP 20, WordPress 57), 37 taken, 224 rejected. Each lane's log follows, one line per advisory. Advisories that were passed over after a lane's family slot filled are listed separately where the lane recorded them, and are not counted as examined.
|
||||
|
||||
### Python (Flask / Django / FastAPI)
|
||||
|
||||
- GHSA-mr95-65j8-9mxp / CVE-2026-57170 (oscal-compass/compliance-trestle, SSTI): library/CLI, not a web app.
|
||||
- GHSA-rr49-f9g6-c9r5 / CVE-2026-57149 (plone/plone.app.portlets, TALES injection): framework out of scope (Zope/Plone).
|
||||
- GHSA-4ghv-53cq-7wp3 / CVE-2026-91129 (home-assistant/core, SSRF): framework out of scope (aiohttp), and a huge repository.
|
||||
- GHSA-v9m3-wfh8-5646 / CVE-2026-77249 (sooperset/mcp-atlassian, SSRF): fix mixed with unrelated changes. The single squash `b0417334` (#1448, 34 files) fixes about ten advisories across the attachment, transport, SSRF, authorization, filter and OAuth layers. The named sink (jira/users.py `_lookup_user_by_permissions`) is not in the diff. The MCP transport is not Flask/Django/FastAPI.
|
||||
- GHSA-hgcf-4mq8-5266 / CVE-2026-77274 (mcp-atlassian, SSRF): same squash as above.
|
||||
- GHSA-6vmq-24h2-pj7j / CVE-2026-77271 (mcp-atlassian, path traversal to RCE): family out of scope (path traversal), same squash.
|
||||
- GHSA-5wf4-jqxh-8gm3 / CVE-2026-77267 (mcp-atlassian, SSRF): same squash as above.
|
||||
- GHSA-6529-c226-h328 / CVE-2026-77261 (mcp-atlassian, SSRF): same squash as above.
|
||||
- GHSA-49xv-9743-pw8w / CVE-2026-77265 (mcp-atlassian, SSRF): same squash as above.
|
||||
- GHSA-ch3g-4xvr-674q / CVE-2026-62282 (opencve/opencve, SSRF): repo in used list.
|
||||
- GHSA-82r6-8w77-94w6 / CVE-2026-63374 (agronholm/anyio, TLS verification): library, not a web app.
|
||||
- GHSA-rqfv-2mw9-78g2 / CVE-2026-59971 (designcomputer/mysql_mcp_server, CWE-306/346): family out of scope (missing Origin/Host validation, DNS rebinding against an MCP SSE transport, not a permissive-configuration setting). The MCP transport is not Flask/Django/FastAPI.
|
||||
- GHSA-34r3-9m95-vq73 / CVE-2026-87999 (open-webui, SSRF): repo already has a candidate.
|
||||
- GHSA-5x7x-4c3c-qf5w / CVE-2026-88001 (open-webui, SSRF through redirect): repo already has a candidate.
|
||||
- GHSA-jf6q-chmf-3h3v / CVE-2026-55073 (kozea/weasyprint, SSRF): library, not a web app.
|
||||
- GHSA-4hhp-h66f-j5j7 / CVE-2026-73560 (vllm-project/vllm, SSRF): the sink (`mimo_v2_omni.py` `_fetch_image`) is reached through the offline `LLM.generate(multi_modal_data=...)` API in the advisory. The HTTP server fetches media through `MediaConnector`, so this is a library path, not a web app.
|
||||
- GHSA-284h-m62q-gf8w / CVE-2026-78676 (gitpython-developers/gitpython, config injection): library, not a web app.
|
||||
- GHSA-6ww7-3frv-cqxh / CVE-2026-78682 (nltk/nltk, SSRF): library, not a web app.
|
||||
- GHSA-4mvj-m6j5-pmf7 / CVE-2026-71428 (Unstructured-IO/unstructured, SSRF): library, not a web app.
|
||||
- GHSA-jrrm-9hc7-2v3h / CVE-2026-62674 (omnigent-ai/omnigent, CWE-94): family out of scope. This is missing authorization on a shared-agent overwrite. Command execution comes from the intended stdio-MCP feature.
|
||||
- GHSA-756x-9hf6-q4h4 / CVE-2026-62675 (omnigent, CWE-94): family out of scope (uploaded agent bundle runs Python tools by design; authorization issue), and the repo was already examined.
|
||||
- GHSA-p3m8-78j2-g5p3 / CVE-2026-62388 (nltk, CWE-1188): library, not a web app.
|
||||
- GHSA-3gqm-fcw5-w839 / CVE-2026-63311 (nltk, SSRF fail-open): library, not a web app.
|
||||
- GHSA-p6gw-4frg-j7jw / CVE-2026-55509 (mar10/wsgidav, SQLi): framework out of scope (its own WSGI server, not Flask/Django/FastAPI).
|
||||
- GHSA-jw39-3688-r4rx / CVE-2026-54757 (compliance-trestle, SSTI): library/CLI, not a web app.
|
||||
- GHSA-6hx8-3wjj-gr8g / CVE-2026-54770 (Pylons/webob, open redirect): library, not a web app.
|
||||
- GHSA-6wvf-77m9-58rm / CVE-2026-37004 (BerriAI/litellm, SSTI in /prompts/test): repo already has a candidate.
|
||||
- GHSA-jrw6-7x4q-w25j / CVE-2026-54569 (senaite/senaite.core, eval injection): framework out of scope (Plone/Zope).
|
||||
- GHSA-mv8m-v9v6-5f94 / CVE-2026-54548 (siemens/kas, SSH host key checking disabled): library/CLI, not a web app.
|
||||
- GHSA-w3fx-mc44-mf6j / CVE-2026-45018 (Chainlit/chainlit, command injection): repo already has a candidate (published 2 minutes before GHSA-hvfh and fixed by the same commit).
|
||||
- GHSA-8cp3-qxj6-px34 (universal-tool-calling-protocol/python-utcp, SSRF): library, not a web app.
|
||||
- GHSA-ppx3-28rw-8fpf / CVE-2026-12210 (python-utcp, SSRF): library, not a web app.
|
||||
- GHSA-9qhg-99ww-9mqc (python-utcp, SSRF through redirect): library, not a web app.
|
||||
- GHSA-pvph-5j39-v8qc / CVE-2026-55532 (MervinPraison/PraisonAI, CWE-346/352): family out of scope (a prefix-match Origin check leading to CSRF on an MCP HTTP transport, not a permissive CORS setting).
|
||||
- GHSA-hmfx-4v44-9qw9 / CVE-2026-55535 (PraisonAI, SSRF by DNS rebinding in `webhook_url`): the fix is mixed with unrelated changes. The single commit `2f9677ab` ("harden input validation and query parameterization", 16 files) fixes about seven advisories across memory, tools, workflows, the jobs, browser and MCP servers. It only turns `gaierror` into a rejection. `executor.py` still re-resolves at send time, so the fixed tree does not remove the rebinding the advisory describes.
|
||||
- GHSA-rg5q-pp8p-f7jm / CVE-2026-55537 (PraisonAI, SSRF by DNS fail-open): same multi-advisory commit as above.
|
||||
- GHSA-vg6p-v9vm-6fgj / CVE-2026-55524 (PraisonAI `praisonaiagents` web_crawl, SSRF): library agent tool, not a web app; same commit.
|
||||
- GHSA-x44h-65qv-cw74 / CVE-2026-55526 (PraisonAI `praisonaiagents` spider_tools, SSRF): library agent tool, not a web app.
|
||||
- GHSA-wj6g-v78p-6fx3 / CVE-2026-55529 (PraisonAI MCP HTTP stream, origin validation): family out of scope (CSRF/origin).
|
||||
- GHSA-8hjw-25cg-g52h / CVE-2026-55523 (PraisonAI web_crawl, SSRF): library agent tool, not a web app.
|
||||
- GHSA-5r34-2g38-6569 / CVE-2026-55525 (PraisonAI web_crawl, SSRF): library agent tool, not a web app.
|
||||
- GHSA-crp9-r7rq-c8cg (nltk): duplicate advisory.
|
||||
- GHSA-qg9p-xrhj-435m (nltk): duplicate advisory.
|
||||
- GHSA-8vh5-mgjj-w6hg (nltk): duplicate advisory.
|
||||
- GHSA-5p3m-vhh6-9236 (eidetic-labs/stigmem, SSRF): negligible real-world use (4 stars, repo created 2026-05, alpha release 0.9.0a11). I treated it as a toy project.
|
||||
- GHSA-p77j-g7h5-r2vw (geolens-io/geolens, 9 CWEs incl. 918): family out of scope (the advisory is mainly authorization and cache-scope data disclosure).
|
||||
- GHSA-f3qq-49m6-rw8f / CVE-2026-70667 (Netflix/lemur, SSRF): repo already has a candidate.
|
||||
- GHSA-xpmj-wjcp-6pww / CVE-2026-70666 (Netflix/lemur, SSRF): repo already has a candidate.
|
||||
|
||||
Injection-only continuation (examined):
|
||||
- GHSA-mw6r-2hvm-4rp2 / CVE-2026-55546 (QWED-AI/qwed-mcp, SymPy eval): MCP stdio server, not a Flask/Django/FastAPI web app.
|
||||
- GHSA-hxmv-c4g6-5fqc / CVE-2026-55522 (MervinPraison/PraisonAI, tools.py autoload in workflow include): library workflow feature, not a web app. The fix is the same multi-advisory commit `2f9677ab`.
|
||||
- GHSA-xm98-3vcf-fph7 / CVE-2026-53710 (IBM/mcp-context-forge, RestrictedPython sandbox bypass): repo already has a candidate.
|
||||
- GHSA-2cp2-2r3c-7p7r / CVE-2026-68508 (hydra-ecosystem/hydra, hydra.utils.instantiate): library, not a web app.
|
||||
|
||||
SQLi-only continuation (CWE-89, pip, newest first from 2026-08-21):
|
||||
- GHSA-7xw9-549r-8jrc (DIRACGrid/DIRAC, SQLi in PilotManager): framework out of scope (DIRAC's own DISET/Tornado RPC services).
|
||||
- GHSA-m4m7-4cw8-62j6 / CVE-2026-61667 (DIRAC, SQLi + eval in FileCatalog): framework out of scope (same repo, DISET services).
|
||||
- GHSA-6xc5-4r68-67fc / CVE-2026-54760 (langroid/langroid, SQLChatAgent blocklist bypass): library, not a web app.
|
||||
- GHSA-pmch-g965-grmr / CVE-2026-50180 (langroid, SQLChatAgent blocklist): library, not a web app.
|
||||
- GHSA-pqrj-4gwg-h5f7 / CVE-2025-66336 (apache/doris-mcp-server, SQLi): MCP server, not a Flask/Django/FastAPI web app.
|
||||
- GHSA-9pc9-4crj-mhpj (eidetic-labs/stigmem, identifier quoting): negligible real-world use (toy project, see above). The advisory also describes defensive hardening rather than a demonstrated injection.
|
||||
- GHSA-82m5-3pcp-hccq / CVE-2026-10105 (agno-agi/agno, ClickHouse `delete_by_metadata`): library vector-DB backend, not a web app. There is no patched version, and the fix sits in PR #7883 with no merged commit reference.
|
||||
- GHSA-mxfr-6hcw-j9rq / CVE-2026-25879 (langroid, prompt-to-SQL): library, not a web app.
|
||||
|
||||
Passed over in the continuation phase and not examined, because their family quota was already met. Listed for completeness, not counted as examined:
|
||||
- Injection, older than the last row the injection continuation reached (GHSA-x2rj, 2026-08-21): GHSA-533j-2v4q-mw5h (langchain-mongodb), GHSA-w47q-945m-q9pc (document-merge-service), GHSA-9gmc-jqmh-3rvm (copier), GHSA-rghg-q7wp-9767 (MONAI), GHSA-59fj-m2j6-hcxh, GHSA-73wf-9vmv-5pv9 (glances), GHSA-3496-9g83-7v6x (sqlparse).
|
||||
- Non-SQLi injection passed over in the SQLi-only phase: GHSA-j74f-g7vx-fh4x (pgadmin4 OS command injection, same repo as the SQLi taken).
|
||||
- Untrusted destination, passed after Lemur filled the extra slot: GHSA-95px-34x5-p37h (MobSF), GHSA-7gwp-5pfp-969j (mlflow).
|
||||
|
||||
Counts: Examined 72, taken 12 (7 quota, 2 extra-family, 3 injection continuation), rejected 60. The first two phases examined 57 advisories (rows 1–72 of list-pip.tsv down to 2026-08-17, with 15 rows passed over). The injection continuation examined 6 of those 15 passed-over rows, which leaves 9 passed over. The CWE-89 continuation examined 9 more entries from pip-89.json (2026-07-13 down to 2026-05-11) and passed over 1 non-SQLi entry (pgadmin GHSA-j74f). In total, 10 rows were passed over without being examined. Advisories outside the CWE filter never entered the list. Their number is not known, because the list was built from per-CWE queries.
|
||||
|
||||
### JavaScript / TypeScript (Node servers)
|
||||
|
||||
Main phase (lines 1-59 of list-npm.tsv):
|
||||
- GHSA-3cj3-hqcr-g934 / CVE-2026-59723 (cline/cline, CWE-346 cross-origin WebSocket hijacking): VS Code extension, not a Node web app; family out of scope.
|
||||
- GHSA-fm8p-53ww-hf6w / CVE-2026-61742 (bytebase/dbhub, 306/346 DNS rebinding): MCP tool server; missing Host/Origin check is out of the three families.
|
||||
- GHSA-cmhj-wh2f-9cgx / CVE-2026-56676 (decolua/9router, SSRF DNS rebinding): repo already has a candidate.
|
||||
- GHSA-798p-78g2-v556 / CVE-2026-61612 (ondata/ckan-mcp-server, SSRF): MCP tool server (input is LLM tool arguments), not a web app.
|
||||
- GHSA-q69g-4hcv-6jg4 / CVE-2026-71538 (cyclonedx/cyclonedx-node-npm, cmdi): CLI, not a web app.
|
||||
- GHSA-q8hw-4fvp-9rwv / CVE-2026-61793 (nuxt-modules/og-image, SSRF): library (Nuxt module), not an app.
|
||||
- GHSA-2h44-8472-frjj / CVE-2026-61559 (zereight/gitlab-mcp, SSRF): MCP tool server.
|
||||
- GHSA-rqx4-3f6q-3x2v / CVE-2026-59148 (mockoon/mockoon, CORS): repo in used list.
|
||||
- GHSA-65h7-9wrw-629c / CVE-2026-59973 (agentfront/frontmcp, SSRF): library / MCP framework.
|
||||
- GHSA-4x45-gxvp-6283 / CVE-2026-59960 (argos-ci/argos-javascript, cmdi): CI client library/CLI.
|
||||
- GHSA-q5wm-mgqx-fv2f / CVE-2026-86074 (n8n-io/n8n, SSRF): no fix commit referenced; 2.38.1...2.38.2 is a 213-file "Bundle 2.x" commit `8c8b059e` (fix mixed with unrelated changes); licence is the non-OSI Sustainable Use License.
|
||||
- GHSA-6xcw-7xm6-48c6 / CVE-2026-86083 (n8n, code injection): same, fix inside 213-file bundle commit.
|
||||
- GHSA-34ff-336r-5q23 / CVE-2026-86082 (n8n, SSRF): same.
|
||||
- GHSA-hw8v-xxg5-vvvx / CVE-2026-86076 (n8n, code injection): same.
|
||||
- GHSA-f6mr-pjwc-34m4 / CVE-2026-88056 (angular/angular platform-server, SSRF): framework library, not an app.
|
||||
- GHSA-8m3c-c648-2xjj (nodemailer, SSRF/file): library.
|
||||
- GHSA-gx45-xrj5-g6c4 / CVE-2026-75911 (hmbown/codewhale, code injection): TUI/CLI.
|
||||
- GHSA-wrj3-vj8c-784f / CVE-2026-75858 (codewhale): TUI/CLI.
|
||||
- GHSA-6v2g-fpxh-pmmh / CVE-2026-75856 (codewhale, SSRF): TUI/CLI.
|
||||
- GHSA-6hxq-p678-4hr2 (masterkale/simplewebauthn, 295): library; attestation-chain validation is not a permissive-config case.
|
||||
- GHSA-fg9p-mrxr-hvq7, GHSA-88f2-fpv8-89q2, GHSA-w727-8j6c-2rj4, GHSA-2h9g-j24r-h63g, GHSA-8j6p-r8jg-mxqh, GHSA-2w86-xfrc-g85r, GHSA-3575-w9fc-c2j6, GHSA-653q-5476-x79g, GHSA-6437-gxhq-pqv8, GHSA-cxq5-97v7-87j8, GHSA-p4cg-3328-rvfg, GHSA-6mr6-jvcr-2f25 (orval-labs/orval, 12 advisories, code injection / SSRF in generated code): code-generator CLI, not a web app.
|
||||
- GHSA-4q39-2jhr-7qx8 / CVE-2026-65842 (udecode/plate, SSRF): library.
|
||||
- GHSA-cpjf-6666-r8fx / CVE-2026-61704 (op-engineering/link-preview-js, SSRF): library.
|
||||
- GHSA-f65p-4m7j-42xc / CVE-2026-75975 (fastify/fast-uri, SSRF): library.
|
||||
- GHSA-fph4-wmhf-6fwf / CVE-2026-75899 (fastify/fast-uri, SSRF): library.
|
||||
- GHSA-83x6-42hr-jc76 / CVE-2026-73845 (ondata/ckan-mcp-server, SSRF): MCP tool server.
|
||||
- GHSA-vq4v-j7r6-jq4m / CVE-2026-82393 (pnpm/pnpm): CLI; path traversal/file write, family out of scope.
|
||||
- GHSA-qxc2-j82w-r537 / CVE-2026-73231 (faker-js/faker, eval): library.
|
||||
- GHSA-3wgp-x9p5-c7cc / CVE-2026-58191 (appium/appium base-driver, XSS/489): library; the flaw is reflected XSS in test routes, family out of scope.
|
||||
- GHSA-g5xc-5w98-jfvm / CVE-2026-55855 (mariadb-connector-nodejs, SQLi in escaping): database driver library.
|
||||
- GHSA-cqhc-2h57-wpxf / CVE-2026-55215 (mariadb-connector-nodejs, TLS): database driver library.
|
||||
- GHSA-86m2-fcxq-5q7c / CVE-2026-55641 (decolua/9router, Host spoofing / SSRF): repo already has a candidate.
|
||||
- GHSA-66mm-25pp-rfff / CVE-2026-77415 (jsonata-js/jsonata, code execution): library.
|
||||
- GHSA-2943-5xfg-gq5f / CVE-2026-77414 (jsonata): library.
|
||||
- GHSA-8gq3-vp5j-2grp / CVE-2026-77413 (jsonata): library.
|
||||
- GHSA-wppf-h75h-6pm6 / CVE-2026-54689 (ihor-sokoliuk/mcp-searxng, SSRF): MCP tool server.
|
||||
- GHSA-q87f-qc2r-2gw4 / CVE-2026-54688 (mcp-searxng, SSRF): MCP tool server.
|
||||
- GHSA-2xhg-73j7-rrgx / CVE-2026-53957 (contentful/contentful-mcp-server, SSRF): MCP tool server.
|
||||
- GHSA-w6x9-28jw-hq7j / CVE-2026-63643 (MagicMirrorOrg/MagicMirror, SSRF calendar): the referenced fix `58c2a5e6` only enforces the IP whitelist on Socket.IO handshakes; the `fetch(url)` sink is unchanged in the fixed version (fix is access control, not destination validation).
|
||||
- GHSA-998g-7v5w-cr7g / CVE-2026-63642 (MagicMirror, SSRF newsfeed): same fix commit, same reason.
|
||||
- GHSA-8g4w-4ffg-8vgx / CVE-2026-56677 (decolua/9router, SSRF OIDC test): repo already has a candidate.
|
||||
|
||||
Extra phase (lines 60-94; only config_secrets and untrusted_destination sought, +1 each):
|
||||
- GHSA-49mq-fc6q-3h46 / CVE-2026-55157 (ooples/token-optimizer-mcp, cmdi): quota filled, family not sought in extra phase (also an MCP tool server).
|
||||
- GHSA-5fpj-28rv-84r7 / CVE-2026-35219 (Budibase/budibase, SSRF): no fix commit referenced; the 3.41.2...3.41.3 compare (47 commits) does not touch the automation steps (fix not identifiable, possibly in the private pro submodule).
|
||||
- GHSA-fjgc-3mj7-8rg8 / CVE-2026-55087 (ether/etherpad, open redirect): fix inside a large "assorted hardening" PR (#7784), as found in the v1 research.
|
||||
- GHSA-vhh6-v828-x62f / CVE-2026-58230 (@sap/approuter, 601): no public source repository.
|
||||
- GHSA-wcx4-wpfv-mc5c (aws/jsii jsii-diff), GHSA-6x64-9x62-f2gx (mermaid), GHSA-9473-5f9j-94wq and GHSA-279x-mwfv-vcqv (nuxt/nuxt), GHSA-f2r8-jv7c-xqmp and GHSA-4f78-qhmw-8j8m (electron): injection family, not sought in extra phase.
|
||||
- GHSA-v64r-4m7r-3mvq / CVE-2026-70605 (electron/electron, SSRF): framework out of scope (Electron).
|
||||
- GHSA-v3j7-r9gq-3gjw / CVE-2026-70604 (electron, 942 CORS): framework out of scope (Electron).
|
||||
- GHSA-9pf5-hg6p-4pwp / CVE-2026-70599 (electron, 346): framework out of scope.
|
||||
- GHSA-x5mm-wm4g-j5xv, GHSA-gcvv-72q8-9v76, GHSA-g366-23fw-ggp6, GHSA-ch52-px8q-f22j, GHSA-wvp2-4qqp-4h3r (tryghost/ghost, 5x SSRF): repo in used list.
|
||||
- GHSA-5xvg-pmgg-3mxr, GHSA-4j8x-x6v7-w9rq, GHSA-x3hf-7cj6-3r4m (Flowise, code injection, listed before the SSRF advisory on the same day): injection family, not sought in extra phase.
|
||||
- GHSA-x6vm-w76m-8j7g, GHSA-vmv7-4m6c-3cg5, GHSA-3769-jgqc-cxm7, GHSA-wg86-r78f-74mp, GHSA-g32j-mmxr-gfq5 (Flowise, code injection): repo already has a candidate; injection not sought.
|
||||
- GHSA-v8fg-2rw7-q452 / CVE-2026-69240 (sequelize, SQLi Oracle): ORM library; injection not sought.
|
||||
- GHSA-mwp4-54f8-5fhr, GHSA-4xrf-jv44-h6hh, GHSA-22jq-vg5j-6vgg (beaugunderson/ip-address, SSRF classification): library; untrusted extra already filled.
|
||||
- GHSA-34pj-2622-jvxq / CVE-2026-53607 (apostrophecms/apostrophe, SSRF via Host header): untrusted_destination extra already filled by Flowise (newer); not examined further.
|
||||
- GHSA-p849-8hwh-84j9 / CVE-2026-52887 (nocobase/nocobase, SQLi): repo already has a candidate; injection not sought.
|
||||
- GHSA-5846-7qm3-r52j / CVE-2026-54729 (hackingrepo/dssrf-js, SSRF): library; untrusted extra already filled.
|
||||
|
||||
SQLi/NoSQLi continuation phase (CWE-89 / CWE-943 entries of list-npm.tsv after GHSA-p849-8hwh-84j9, newest first; requested by the coordinating session because SQLi was thin):
|
||||
- GHSA-pqh8-p93p-2rx7 (dynatrace-oss/dynatrace-mcp, DQL injection): MCP tool server.
|
||||
- GHSA-6wcc-39rp-hh9p / CVE-2026-54658 (hypequery/hypequery, ClickHouse escaping): library (query builder).
|
||||
- GHSA-pmpg-2mxq-6xwr (Budibase, MongoDB NoSQLi): TAKEN (budibase-mongo-template-nosqli).
|
||||
- GHSA-2xgg-r2wc-c5r2 (Budibase, MySQL DESCRIBE backtick): repo already has a candidate.
|
||||
- GHSA-qw6m-8fw2-2v64 (Budibase, MongoDB NoSQLi via query endpoint): repo already has a candidate (same flaw family as the taken case).
|
||||
- GHSA-q6x4-v3qx-85qw / CVE-2026-73300 (Budibase, MySQL multipleStatements): repo already has a candidate.
|
||||
- GHSA-652q-gvq3-74qv (n8n, Snowflake node SQLi): no fix commit referenced; 2.32.0...2.32.1 has only a "Bundle/2.x" backport commit `f69dfc6d` plus two unrelated commits (fix mixed into bundle); non-OSI licence.
|
||||
- GHSA-jqwr-vx3p-r266 (n8n, PostgresTrigger SQLi): same release bundle as above.
|
||||
- GHSA-hwmj-qg4v-cvg9 / CVE-2026-59257 (n8n, MySQL v1 SQLi): 2.28.0...2.28.1 fix is again inside a "Bundle/2.x" commit `08ffbcc1`.
|
||||
- GHSA-2qp2-6frj-p9pq (n8n MySQL v1): duplicate advisory.
|
||||
- GHSA-5c7w-4wm3-85vw (asymmetric-effort/specifyjs, GraphQL tag): library.
|
||||
- GHSA-8qv3-p479-cj62 / CVE-2026-54350 (Budibase, NoSQL operator injection): repo already has a candidate.
|
||||
- GHSA-9ggv-8w38-r7pm (typeorm/typeorm, orderBy SQLi): ORM library.
|
||||
- GHSA-qqf5-x7mj-v43p (Budibase, connector SQLi): repo already has a candidate.
|
||||
- GHSA-jpq7-226w-6cxx / CVE-2026-54313 (n8n, MongoDB node): n8n release-bundle pattern (not re-checked per release); non-OSI licence.
|
||||
- GHSA-c37g-w77q-m4vp / CVE-2026-54310 (n8n, Postgres v1 node): same.
|
||||
- GHSA-98xf-r82g-9mhx / CVE-2026-48121 (langchain-ai/langgraphjs, MongoDBSaver NoSQLi): library.
|
||||
- GHSA-h9fj-c2qr-76g2 / CVE-2026-47720 (frangoteam/FUXA, TDengine SQLi): TAKEN (fuxa-tdengine-escape-sqli). Quota of 2 reached; stopped.
|
||||
|
||||
Counts: Examined 112 advisories (59 in the main phase, 35 in the extra phase, 18 in the SQLi/NoSQLi continuation phase), taken 11 (7 main + 2 extra + 2 SQLi phase), rejected 101. CWE-prefiltered skips: 0 inside the list (the list was CWE-filtered at fetch time, so reviewed npm advisories with other CWEs never entered it and were not counted).
|
||||
|
||||
Families in this lane: injection 6 (2 SQLi, 1 NoSQLi, 3 OS command/code), untrusted_destination 4 (2 SSRF, 1 SSRF guard bypass, 1 open redirect), config_secrets 1 (wildcard CORS). Privilege: unauthenticated 5 (reactpress, cct, medplum, nx, fuxa) + 1 conditional (omniroute); authenticated 5.
|
||||
|
||||
### PHP web applications (not WordPress)
|
||||
|
||||
- GHSA-rw77-vq4g-x3hp / CVE-2026-56738 (thorsten/phpMyFAQ, SQLi): repo in used list.
|
||||
- GHSA-9ccq-2jfg-qw33 / CVE-2026-72702 (getgrav/grav, CWE-346): extra phase; Referer prefix-match origin check, neither a configuration weakness nor a redirect sink as described; untrusted slot already filled by SMW.
|
||||
- GHSA-68jx-f42c-7599 / CVE-2026-19418 (typo3/typo3, CWE-346/352): extra phase; broken access control, family out of scope.
|
||||
- GHSA-qh7h-6c7g-x8m6 (getgrav/grav, CWE-346): duplicate advisory.
|
||||
- GHSA-4f2f-jr2m-j7p4 (typo3, CWE-346): duplicate advisory.
|
||||
- GHSA-f7vp-7xgx-4w4r / CVE-2026-69245 (guzzle/guzzle, CWE-346): library, not a web app.
|
||||
- GHSA-g446-98w2-8p5w / CVE-2026-59883 (guzzle/guzzle, CWE-346): library, not a web app.
|
||||
|
||||
SQLi-only continuation phase (requested after the cross-lane family balance; CWE-89 entries in list-composer.tsv, newest first, resuming after Sulu on 2026-09-23):
|
||||
- GHSA-vjqc-q4mp-2rvf / CVE-2026-79752 (cakephp/cakephp, SQLi): framework library (`FunctionsBuilder`), not a web app.
|
||||
- GHSA-23rh-xw42-fq82 / CVE-2026-55416 (pimcore/pimcore, SQLi): Custom Reports lets a `reports_config` admin write SQL fragments by design (only a keyword blacklist). No fix commit is linked, and `Sql.php` history shows several successive hardening commits (`3fd77334`, `1999fee2`, `62ad7c5a`). No single identifiable fix.
|
||||
- GHSA-fxf7-vhh8-7vpq / CVE-2026-77635 (cakephp/cakephp, SQLi): framework library, not a web app.
|
||||
- GHSA-9x44-4gxf-8c25 / CVE-2026-55634 (pimcore/pimcore, CWE-89+94): primary sink is PHP code generation (field name injected into a generated class file, RCE). The ALTER TABLE SQLi is a secondary sink of the same fix (`a4f8c3cf`). Rejected in the SQLi-only phase as mixed-family ground truth. It would be a valid `injection` (code) case, licence POCL.
|
||||
- (taken) GHSA-79cw-hfcc-7mw9: pimcore-studio-datefilter-sqli.
|
||||
- (taken) GHSA-w27m-rmmf-g5w4: froxlor-admin-ip-second-order-sqli.
|
||||
|
||||
config_secrets alternate, examined on request:
|
||||
- GHSA-jr9p-4h4j-6c58 / CVE-2026-52824 (kimai/kimai, CWE-1188, default `APP_SECRET`): rejected. The advisory links no commit. The fix is in release commit `31a8f887a5cda517db7b4320a7ad997c87d08601` ("Release 2.58", 85 files, fixes four GHSAs plus unrelated changes; parent `8d245ae223fd8fcbd508362dfe7fd04254a066c4`). That is a fix mixed with unrelated changes. The default secret also lives in `Dockerfile` (`ENV APP_SECRET=...`) and `.docker/entrypoint.sh`, not in PHP code. The fix adds a shell `ensureAppSecret()` function, and the bare-metal `.env.dist` default is, per the advisory, unchanged. pheditor stays the only (low-confidence) config_secrets case from this lane.
|
||||
|
||||
Passed over without examination because the injection quota was filled and only the extra families were being sought (2026-09-17 → 2026-07-16, 39 advisories), including: Grav CWE-94 ×5 (GHSA-r94f-hx44-8jqf, -f8wv-xp27-6gq7, -7pgq-cr25-xvc8, -2c4f-86xc-cr74, -fj2p-qj2f-74v5), CakePHP SQLi ×2 (GHSA-vjqc-q4mp-2rvf, -fxf7-vhh8-7vpq; framework library), Chamilo GHSA-g4c3-4g96-6g4m, Pimcore SQLi ×4, Smarty SSRF GHSA-cq55-c7wv-pxmq, elFinder SSRF GHSA-8x3q-jpjh-qh5c, Snipe-IT open redirect GHSA-wg2f-x2c2-c4rp, SilverStripe ×2, Phalcon, Backpack CRUD, LibreNMS ×3, Froxlor, jmespath.php, Winter, CodeIgniter4, PHP_CodeSniffer, Easy!Appointments SSRF, Poweradmin, WPCS, Pheditor ×2 other, PHPSpreadsheet, Koel SSRF ×6 (partly beyond 07-16). Note for the maintainer: the untrusted_destination ones among them (Smarty, elFinder, Snipe-IT, Easy!Appointments, Poweradmin, Koel) are the next-in-line alternates for that family.
|
||||
|
||||
No WordPress plugin appeared in the composer list up to 2026-07-16 (WPCS GHSA-3pwp-g2mj-5p3v is a coding-standards tool, not a plugin).
|
||||
|
||||
Counts: Examined 20, taken 8 (4 quota + 1 extra untrusted_destination + 1 extra config_secrets + 2 SQLi continuation), rejected 12. SQLi continuation phase: 6 examined, 2 taken. Kimai alternate: 1 examined, rejected. Passed over after the quota was filled (other families): 39. CWE-prefiltered skips: the composer list was prefiltered by CWE at fetch time, so out-of-family advisories never entered it (not counted individually).
|
||||
|
||||
### WordPress plugins (PHP+JS)
|
||||
|
||||
- GHSA-7jgm-vpwg-fj48 / CVE-2026-93899 (Better Messages – Chat Rooms, Group Chat, Private Messages & AI Chat Bots, SQLi, published 2026-09-25): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-g4cp-fj5c-7599 / CVE-2026-12037 (Asset CleanUp: Page Speed Booster, SSRF, published 2026-09-25): only GitHub repo is gabelivan/wp-asset-clean-up, last push 2019; fix only on wp.org SVN
|
||||
- GHSA-6rpr-h6cv-7xm2 / CVE-2026-78397 (Link Library, SSRF, published 2026-09-25): the author repo ylefebvre/link-library stops at 7.9.2 (2026-04-26); the fix is in 7.9.6, which is only on wp.org SVN
|
||||
- GHSA-8q4x-8xp2-6ww3 / CVE-2026-18335 (Kirki – Freeform Page Builder, Website Builder & Customizer, SSRF, published 2026-09-24): themeum/kirki exists but last push 2026-04-10 and has no 6.x code; fix (trac changeset 3636487) only on wp.org SVN
|
||||
- GHSA-4rj9-f57h-53hg / CVE-2026-93368 (Rename wp-login.php to anything you want, SQLi, published 2026-09-23): only unrelated/stale repo (ellatrix/rename-wp-login, 2020); fix only on wp.org SVN
|
||||
- GHSA-8743-f7m5-qxwx / CVE-2026-91024 (Booking Manager, SQLi, published 2026-09-23): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-ppcx-fj6x-46xf / CVE-2026-75799 (YAHMAN Add-ons, code injection, published 2026-09-23): file-type validation (upload), family out of scope; also no GitHub repo
|
||||
- GHSA-9rxg-m349-fgqj / CVE-2022-4997 (jet-form-builder-stripe-gateway, SQLi, published 2026-09-23): CVE-2022 id (disclosed 2022, GHSA published 2026); no GitHub repo
|
||||
- GHSA-gg2m-8p7j-v3mx / CVE-2026-92235 (The WP Ultimate Review, code injection, published 2026-09-22): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-h9fx-ggrg-2982 / CVE-2026-16542 (Import and export users and customers, SSRF, published 2026-09-20): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-h6cq-4ghp-3r32 / CVE-2026-87067 (Forminator Forms, code injection, published 2026-09-20): Forminator: no public source repo; also object-injection style (class restriction), family doubtful
|
||||
- GHSA-fv9m-mw9j-43cg / CVE-2026-9855 (Custom Field Template, SQLi, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-wc9h-566w-4rw2 / CVE-2026-7527 (WP Ghost (Hide My WP Ghost) – Security & Firewall, open redirect, published 2026-09-19): johndarrel/hide-my-wp is a release-dump mirror; the fix (<=7.0.02 -> 7.0.08) sits in one 7.0.08 release commit of 60+ files (fix mixed with unrelated changes)
|
||||
- GHSA-jvc3-5jgr-chw6 / CVE-2026-85658 (Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile &..., code injection, published 2026-09-19): ProfilePress: only profilepress/profilepress-readmes on GitHub; fix only on wp.org SVN
|
||||
- GHSA-cv43-j772-67wp / CVE-2026-6295 (WP Optimizer, SQLi, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-c79v-6v55-pvvh / CVE-2026-13191 (Create, SQLi, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-mw9j-687w-wh97 / CVE-2026-1641 (Wow Elements Addons for Elementor, SSRF, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-fmwg-rf74-rjqj / CVE-2026-4327 (The Welcomizer, code injection, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-5j33-g9fw-4r4f / CVE-2026-88926 (VikRentItems Flexible Rental Management System, SQLi, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-jhj7-pcmr-9q9p / CVE-2026-13200 (Create, SQLi, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-2vrv-wr6r-v79v / CVE-2026-92229 (The Forminator Forms – Contact Form, Payment Form & Custom Form Builder, code injection, published 2026-09-19): Forminator: no public source repo (wpmudev has only forminator-ui); fix only on wp.org SVN
|
||||
- GHSA-3fg9-62hh-37mh / CVE-2026-92807 (Save as PDF Plugin by PDFCrowd, code injection, published 2026-09-19): pdfcrowd/save-as-pdf-addon is the browser add-on, not the WP plugin; fix only on wp.org SVN
|
||||
- GHSA-x57m-9xmj-gh9p / CVE-2026-89274 (WP Recipe Maker, code injection, published 2026-09-19): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-34mw-p6wx-m38q / CVE-2026-85652 (Photo Gallery by 10Web – Mobile-Friendly Image Gallery, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-7wpp-2mgh-3vfp / CVE-2026-75961 (NEX-Forms – Ultimate Forms Plugin for, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-jvcr-7q46-jqm3 / CVE-2026-85705 (Location Manager, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-35gj-4m99-3jxg / CVE-2026-18442 (WCFM Marketplace – Multivendor Marketplace for WooCommerce, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-q2g2-xwwx-rp3m / CVE-2026-17607 (WP Inventory Manager, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-prxh-6hcq-7gvg / CVE-2026-15275 (WP Multi Store Locator Pro, SQLi, published 2026-09-18): wpexpertsio/WP-Multi-Store-Locator last push 2023-12 (Pro advisory); fix not on GitHub
|
||||
- GHSA-xcfp-7r4f-6367 / CVE-2026-17576 (InfiniteWP Client, SQLi, published 2026-09-18): Revmakx/iwp-client last push 2025-09-23, before the fix; fix only on wp.org SVN
|
||||
- GHSA-7v2c-5xw3-f2hc / CVE-2026-12106 (Auto Upload Images, SSRF, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-93x5-88q4-f8c3 / CVE-2026-87775 (Tz Weekly Radio Schedule, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-hm83-7h6c-m5rj / CVE-2026-87774 (Tz Weekly Radio Schedule, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-c3g6-9rgm-rg4m / CVE-2026-90984 (Generate PDF using Contact Form 7, SSRF, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-4778-xmgc-pc88 / CVE-2026-87770 (Price Drop Alert for Woo Commerce, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-rjgv-8pq9-p3c3 / CVE-2026-87771 (Product Question and Answer, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-52cc-m98v-q64v / CVE-2026-87767 (wp shortcut link and advertisement baner, SQLi, published 2026-09-18): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-7mpj-9p6h-7gr6 / CVE-2026-84738 (AF Companion, code injection, published 2026-09-18): file upload validation, family out of scope; also no GitHub repo
|
||||
- GHSA-vfmj-6hfh-5m4v / CVE-2026-87963 (Yo, SQLi, published 2026-09-17): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-xg7f-297r-82hv / CVE-2026-88795 (wpShopGermany IT-RECHT KANZLEI, code injection, published 2026-09-17): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-xhvp-v8j9-2mrf / CVE-2026-86823 (Newsletter, open redirect, published 2026-09-16): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-49j8-gcx6-2f92 / CVE-2026-76559 (WP Import Export Lite, SSRF, published 2026-09-16): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-g78q-54w3-px42 / CVE-2026-76550 (WP Import Export Lite, code injection, published 2026-09-16): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-v7v7-79q5-j8cf / CVE-2026-76551 (WP Import Export Lite, code injection, published 2026-09-16): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-9c57-9fxg-8x9j / CVE-2026-78159 (The Events Calendar, code injection, published 2026-09-12): repo in used list (the-events-calendar)
|
||||
- GHSA-3q5p-wcf4-wvm5 / CVE-2026-14560 (teddy-bear-customize-addon, code injection, published 2026-09-11): file upload validation, family out of scope; also no GitHub repo
|
||||
- GHSA-p236-jmm4-v336 / CVE-2026-18021 (The Beaver Builder Page Builder – Drag and Drop Website Builder, code injection, published 2026-09-08): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-c2fp-562c-5p2q / CVE-2026-12757 (The Email Subscribers & Newsletters – Email Marketing, Post Notifications & Newsletter Plugin..., code injection, published 2026-09-07): icegram/email-subscribers last push 2019; fix only on wp.org SVN
|
||||
- GHSA-368m-gh85-cq79 / CVE-2026-81342 (MasterStudy LMS, open redirect, published 2026-08-29): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-qc2w-xhvw-6xmh / CVE-2026-16296 (Clearfy Cache, open redirect, published 2026-08-04): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
- GHSA-66xh-mw5m-g493 / CVE-2026-14236 (Contact Form 7, open redirect, published 2026-07-27): no GitHub repo / fix only on wp.org SVN (GitHub search found only SVN mirrors or nothing)
|
||||
|
||||
Counts: - Enumeration: `gh api "/advisories?type=unreviewed&cwes=<C>&per_page=100&page=<1..3>&sort=published&direction=desc"` for C in 89, 78, 94, 918, 601, 798, 942. That gives the 300 newest unreviewed advisories per CWE, covering roughly 2026-09-14 to 2026-09-25 for 89/918, and back to 2026-07-27 for 601. I kept those whose description mentions "WordPress" and "plugin" (57 advisories, 2026-07-27 → 2026-09-25) and worked through them newest first. For each, I took the plugin slug from the trac/wordpress.org references and searched GitHub (`search/repositories` by slug, name and vendor). I rejected repos that are only SVN mirrors (common-repository, WordPressBugBounty, WPPlugins, wp-plugins, WordpressPluginDirectory) or that stop before the fix. No WordPress advisory with CWE 798 or 942 appeared in that window.
|
||||
- Examined 57, taken 6 (5 quota + 1 extra untrusted_destination), rejected 51.
|
||||
- Family outcome: 1 code injection, 1 SQLi, 1 SSRF, 3 open redirect, 0 config_secrets. Most WordPress SQLi advisories in the window (Better Messages, Custom Field Template, WP Optimizer, Mediavine Create, Photo Gallery, NEX-Forms, WCFM, InfiniteWP, etc.) have no public development repository, so SQLi is thin in this lane for that reason only.
|
||||
@@ -12,18 +12,22 @@ import re
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
POPULATION = ROOT / "benchmarks" / "independent" / "population-v1.toml"
|
||||
POPULATIONS = sorted((ROOT / "benchmarks" / "independent").glob("population-v*.toml"))
|
||||
POPULATION = POPULATIONS[0]
|
||||
FAMILIES = {"injection", "untrusted_destination", "config_secrets"}
|
||||
SHA = re.compile(r"[0-9a-f]{40}")
|
||||
|
||||
|
||||
def _population() -> dict:
|
||||
return tomllib.loads(POPULATION.read_text())
|
||||
def _population(path: Path = POPULATION) -> dict:
|
||||
return tomllib.loads(path.read_text())
|
||||
|
||||
|
||||
def test_the_reservation_is_well_formed() -> None:
|
||||
data = _population()
|
||||
@pytest.mark.parametrize("path", POPULATIONS, ids=lambda p: p.stem)
|
||||
def test_the_reservation_is_well_formed(path: Path) -> None:
|
||||
data = _population(path)
|
||||
assert data["status"] in {"reserved-unscanned", "frozen"}
|
||||
cases = data["case"]
|
||||
ids = [case["id"] for case in cases]
|
||||
@@ -40,21 +44,22 @@ def test_the_reservation_is_well_formed() -> None:
|
||||
for case in cases:
|
||||
benign = case.get("benign") or {}
|
||||
assert SHA.fullmatch(benign.get("base", "")) and SHA.fullmatch(benign.get("tip", "")), case["id"]
|
||||
assert (POPULATION.parent / data["freeze_record"]).is_file()
|
||||
assert (path.parent / data["freeze_record"]).is_file()
|
||||
languages = {case["language"] for case in cases}
|
||||
assert {"php", "python"} <= languages and languages & {"javascript", "typescript"}
|
||||
|
||||
|
||||
def test_a_multi_language_repository_is_reserved() -> None:
|
||||
@pytest.mark.parametrize("path", POPULATIONS, ids=lambda p: p.stem)
|
||||
def test_a_multi_language_repository_is_reserved(path: Path) -> None:
|
||||
"""The declared v0.1 target is a WordPress plugin: PHP plus its admin JavaScript (task 13.4)."""
|
||||
data = _population()
|
||||
data = _population(path)
|
||||
by_id = {case["id"]: case for case in data["case"]}
|
||||
members = data["multi_language_members"]
|
||||
assert members and all(by_id[m]["language"] == "php" and "javascript" in by_id[m].get("also", []) for m in members)
|
||||
|
||||
|
||||
def test_no_reserved_repository_is_referenced_by_the_development_tree() -> None:
|
||||
reserved = {case["repo"].rstrip("/").lower() for case in _population()["case"]}
|
||||
reserved = {case["repo"].rstrip("/").lower() for path in POPULATIONS for case in _population(path)["case"]}
|
||||
names = {url.rsplit("/", 2)[-2] + "/" + url.rsplit("/", 1)[-1] for url in reserved}
|
||||
offenders = []
|
||||
for top in ("benchmarks", "src", "tests"):
|
||||
|
||||
Reference in New Issue
Block a user