Skip to content

Commit 7839d5b

Browse files
dshoen619claude
andcommitted
fix: clear remaining pdp-v2 image CVEs from customer scan (PER-15358)
A customer scan of permitio/pdp-v2:0.9.13 surfaced 43 unique CVEs. Most were already fixed after 0.9.13 was cut (Go deps via permit-opa #38, cryptography pin, and the libssl3/pyjwt/aiohttp bumps a rebuild picks up automatically). This covers what was genuinely left. Fixed: * GHSA-27jp-wm6q-gp25 - sqlparse 0.5.0 -> 0.5.5 (fixed in 0.5.4). Note sqlparse is never imported; it has only ever been carried as a floor pin from past vuln sweeps. Flagged inline as a removal candidate. Waived (unreachable code paths, verified against the source): * CVE-2026-48710 - malformed Host header desyncs request.url.path from the routed path. No PDP security decision reads request.url: auth is per-route via FastAPI Depends/HTTPBearer, there is no add_middleware or BaseHTTPMiddleware, and the one path-keyed check (cloud_proxy) reads request.path_params, which the router fills from the raw scope path. * CVE-2026-48817 - unsafe getattr dispatch in starlette HTTPEndpoint. The PDP defines no HTTPEndpoint/WebSocketEndpoint subclass; every route is an APIRouter decorator with explicit methods. Both are fixed only in starlette >=1.x, which OPAL's starlette<1 cap forbids, so they join the two existing starlette waivers. Dropped: * CVE-2026-50163 (oras-go) - waiver removed; oras-go v2.6.2 fixes it and permit-opa now pins it. That bump MUST merge to permit-opa@main before this branch merges, since the PDP build clones permit-opa@main and the scout gate will otherwise fail with the waiver gone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e4e0361 commit 7839d5b

3 files changed

Lines changed: 42 additions & 21 deletions

File tree

.docker/scout/pdp-v2.vex.json

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,28 @@
22
"@context": "https://openvex.dev/ns/v0.2.0",
33
"@id": "https://openvex.dev/docs/permitio-pdp/pdp-v2-cve-waivers",
44
"author": "Permit.io",
5-
"timestamp": "2026-07-07T00:00:00Z",
6-
"version": 2,
5+
"timestamp": "2026-07-21T00:00:00Z",
6+
"version": 3,
77
"statements": [
88
{
99
"vulnerability": {
10-
"name": "CVE-2026-50163"
10+
"name": "CVE-2026-48818"
1111
},
1212
"products": [
1313
{
1414
"@id": "pkg:docker/permitio/pdp-v2@next",
1515
"subcomponents": [
16-
{ "@id": "pkg:golang/oras.land/oras-go/v2@2.6.1" }
16+
{ "@id": "pkg:pypi/starlette@0.50.0" }
1717
]
1818
}
1919
],
2020
"status": "not_affected",
2121
"justification": "vulnerable_code_not_in_execute_path",
22-
"impact_statement": "The PDP runs OPA in inline mode via OPAL over OPA's REST API and never pulls OCI artifacts, so oras-go's tar/hardlink extraction (the vulnerable code path in CVE-2026-50163) is never executed. oras-go is an indirect dependency of the bundled OPA (built from permitio/permit-opa); no fixed release exists yet (all versions <= 2.6.1 are affected, upstream fix expected ~2026-07-15). This waiver is temporary and must be removed once permit-opa bumps oras-go to the patched release. Tracked under PER-15358."
22+
"impact_statement": "CVE-2026-48818 is an SSRF / NTLM credential-theft issue in starlette.staticfiles that only triggers on Windows, via UNC-path resolution when serving a StaticFiles mount. The PDP runs exclusively on Linux (Alpine) and mounts no StaticFiles anywhere in the app (only API routers are mounted), so the vulnerable code path is never present or executed. The fix lands only in starlette >= 1.1.0, but opal-common/opal-client 0.9.6 cap starlette<1, so the PDP is pinned to 0.50.0 (see requirements.txt). This waiver is temporary and must be removed once OPAL relaxes its starlette<1 bound and starlette is upgraded to >= 1.3.1. Tracked under PER-15358."
2323
},
2424
{
2525
"vulnerability": {
26-
"name": "CVE-2026-48818"
26+
"name": "CVE-2026-54283"
2727
},
2828
"products": [
2929
{
@@ -35,11 +35,11 @@
3535
],
3636
"status": "not_affected",
3737
"justification": "vulnerable_code_not_in_execute_path",
38-
"impact_statement": "CVE-2026-48818 is an SSRF / NTLM credential-theft issue in starlette.staticfiles that only triggers on Windows, via UNC-path resolution when serving a StaticFiles mount. The PDP runs exclusively on Linux (Alpine) and mounts no StaticFiles anywhere in the app (only API routers are mounted), so the vulnerable code path is never present or executed. The fix lands only in starlette >= 1.1.0, but opal-common/opal-client 0.9.6 cap starlette<1, so the PDP is pinned to 0.50.0 (see requirements.txt). This waiver is temporary and must be removed once OPAL relaxes its starlette<1 bound and starlette is upgraded to >= 1.3.1. Tracked under PER-15358."
38+
"impact_statement": "CVE-2026-54283 causes request.form() size limits to be silently ignored for application/x-www-form-urlencoded bodies, enabling a memory-exhaustion DoS. The PDP is a JSON-only API: it never calls request.form(), does not depend on python-multipart, and parses no form-encoded or multipart request bodies, so the vulnerable form-parsing path is never executed. The fix lands only in starlette >= 1.3.1, but opal-common/opal-client 0.9.6 cap starlette<1, so the PDP is pinned to 0.50.0 (see requirements.txt). This waiver is temporary and must be removed once OPAL relaxes its starlette<1 bound and starlette is upgraded to >= 1.3.1. Tracked under PER-15358."
3939
},
4040
{
4141
"vulnerability": {
42-
"name": "CVE-2026-54283"
42+
"name": "CVE-2026-48710"
4343
},
4444
"products": [
4545
{
@@ -51,7 +51,23 @@
5151
],
5252
"status": "not_affected",
5353
"justification": "vulnerable_code_not_in_execute_path",
54-
"impact_statement": "CVE-2026-54283 causes request.form() size limits to be silently ignored for application/x-www-form-urlencoded bodies, enabling a memory-exhaustion DoS. The PDP is a JSON-only API: it never calls request.form(), does not depend on python-multipart, and parses no form-encoded or multipart request bodies, so the vulnerable form-parsing path is never executed. The fix lands only in starlette >= 1.3.1, but opal-common/opal-client 0.9.6 cap starlette<1, so the PDP is pinned to 0.50.0 (see requirements.txt). This waiver is temporary and must be removed once OPAL relaxes its starlette<1 bound and starlette is upgraded to >= 1.3.1. Tracked under PER-15358."
54+
"impact_statement": "CVE-2026-48710 lets a malformed HTTP Host header make request.url.path diverge from the path actually routed, so security checks that read request.url can be bypassed. The PDP performs no security decision on request.url: authentication is enforced per-route through FastAPI Depends security dependencies (HTTPBearer in horizon/authentication.py), not by path matching, and the app registers no add_middleware/BaseHTTPMiddleware and reads no raw ASGI scope paths. The one path-keyed authorization check - the write-route test in horizon/proxy/api.py cloud_proxy - reads request.path_params, which Starlette's router populates from the raw scope path and which this CVE explicitly leaves unaffected. The fix lands only in starlette >= 1.0.1, but opal-common/opal-client 0.9.6 cap starlette<1, so the PDP is pinned to 0.50.0 (see requirements.txt). This waiver is temporary and must be removed once OPAL relaxes its starlette<1 bound and starlette is upgraded to >= 1.3.1. Tracked under PER-15358."
55+
},
56+
{
57+
"vulnerability": {
58+
"name": "CVE-2026-48817"
59+
},
60+
"products": [
61+
{
62+
"@id": "pkg:docker/permitio/pdp-v2@next",
63+
"subcomponents": [
64+
{ "@id": "pkg:pypi/starlette@0.50.0" }
65+
]
66+
}
67+
],
68+
"status": "not_affected",
69+
"justification": "vulnerable_code_not_in_execute_path",
70+
"impact_statement": "CVE-2026-48817 is an unsafe-reflection issue in starlette.endpoints.HTTPEndpoint request dispatch: the HTTP method is lowercased and resolved with getattr against the endpoint instance without restricting the lookup to known HTTP verbs, letting an attacker invoke internal helper methods that bypass authorization. The PDP defines no HTTPEndpoint or WebSocketEndpoint subclass anywhere in the codebase - every route is registered through FastAPI APIRouter decorators with explicit HTTP methods - so the vulnerable dispatch class is never instantiated or reached. The fix lands only in starlette >= 1.1.0, but opal-common/opal-client 0.9.6 cap starlette<1, so the PDP is pinned to 0.50.0 (see requirements.txt). This waiver is temporary and must be removed once OPAL relaxes its starlette<1 bound and starlette is upgraded to >= 1.3.1. Tracked under PER-15358."
5571
}
5672
]
5773
}

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ jobs:
215215
# Accept CVEs marked not_affected in the OpenVEX doc (see
216216
# .docker/scout/pdp-v2.vex.json) so the gate does not fail on
217217
# vulnerabilities whose code path the PDP never executes and for which
218-
# no usable fix is available: oras-go CVE-2026-50163 (no upstream fix
219-
# yet) and starlette CVE-2026-48818 / CVE-2026-54283 (fixed only in
220-
# starlette >=1.x, which OPAL's starlette<1 cap forbids). See PER-15358.
218+
# no usable fix is available: starlette CVE-2026-48710 / CVE-2026-48817 /
219+
# CVE-2026-48818 / CVE-2026-54283, all fixed only in starlette >=1.x,
220+
# which OPAL's starlette<1 cap forbids. See PER-15358.
221221
vex-location: .docker/scout
222222
only-vex-affected: true
223223
# REQUIRED: docker scout only *applies* VEX statements whose author

requirements.txt

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,27 @@ typer>=0.4.1,<1
1010
uvicorn[standard]>=0.17.6,<1
1111
logzio-python-handler
1212
ddtrace>=3.9.1,<4
13-
sqlparse==0.5.0
13+
# sqlparse is not imported anywhere in the codebase - it has only ever been carried as a
14+
# floor pin from past vulnerability sweeps (see f837d77). Bumped to 0.5.5 to clear
15+
# GHSA-27jp-wm6q-gp25 (fixed in 0.5.4). Candidate for outright removal - tracked separately.
16+
sqlparse==0.5.5
1417
scalar-fastapi==1.0.3
1518
httpx>=0.27.0,<1
1619
# TODO: change to use re2 in the future, currently not supported in alpine due to c++ library issues
1720
# google-re2 # use re2 instead of re for regex matching because it's simiplier and safer for user inputted regexes
1821
protobuf>=6.33.5 # pinned to avoid CVE-2026-0994
1922
cryptography>=48.0.1,<49 # pinned to avoid GHSA-537c-gmf6-5ccf (and CVE-2026-26007)
2023
# starlette is capped <1 by opal-common/opal-client 0.9.6 (and further to <0.51.0 via
21-
# fastapi-utils -> fastapi 0.125.0). The two starlette High CVEs (CVE-2026-54283 fixed
22-
# in 1.3.1, CVE-2026-48818 fixed in 1.1.0) have no <1 backport, so they cannot be
23-
# upgraded away while OPAL caps starlette<1. Both are waived in
24-
# .docker/scout/pdp-v2.vex.json as not_affected (unreachable code paths: JSON-only API
25-
# with no request.form(); Linux with no StaticFiles). Pinned exactly so the waiver
26-
# PURLs stay bound (the image build has no lockfile, so an unpinned starlette could
27-
# drift to a new 0.x and silently break the match). Remove this pin + both waivers and
28-
# upgrade starlette>=1.3.1 once OPAL relaxes its starlette<1 bound. See PER-15358.
24+
# fastapi-utils -> fastapi 0.125.0). Four starlette CVEs (CVE-2026-54283 fixed in 1.3.1,
25+
# CVE-2026-48817 fixed in 1.1.0, CVE-2026-48818 fixed in 1.1.0, CVE-2026-48710 fixed in
26+
# 1.0.1) have no <1 backport, so they cannot be upgraded away while OPAL caps
27+
# starlette<1. All four are waived in .docker/scout/pdp-v2.vex.json as not_affected
28+
# (unreachable code paths: JSON-only API with no request.form(); Linux with no
29+
# StaticFiles; no HTTPEndpoint subclasses; no security decision reads request.url).
30+
# Pinned exactly so the waiver PURLs stay bound (the image build has no lockfile, so an
31+
# unpinned starlette could drift to a new 0.x and silently break the match). Remove this
32+
# pin + all four waivers and upgrade starlette>=1.3.1 once OPAL relaxes its starlette<1
33+
# bound. See PER-15358.
2934
starlette==0.50.0
3035
opal-common==0.9.6
3136
opal-client==0.9.6

0 commit comments

Comments
 (0)