Skip to content

VEX three no-fix Python findings on the agent image, leave the reachable one visible - #40

Merged
LouisLotter merged 1 commit into
mainfrom
agent-python-no-fix-vex
Sep 8, 2026
Merged

LouisLotter merged 1 commit into
mainfrom
agent-python-no-fix-vex

Conversation

@ai-collaboration-app

Copy link
Copy Markdown
Contributor

Closes part of #34 (does not close it — see Remaining below).

The daily dev chart scan gates stackstate-k8s-agent across CRITICAL,HIGH,MEDIUM,LOW. After the 3.13.15 realignment in #39 the image is down to four findings, all against embedded CPython 3.13.15, all with no upstream fix. No version bump can clear them, and a repo-local exception cannot either — the chart scan does not read consumer exception trees — so VEX is the only artifact that can act here.

This PR statements three of the four and deliberately leaves the fourth reporting.

Evidence

All four claims were re-derived on 2026-09-08 against the exact image the gate scans — quay.io/stackstate/stackstate-k8s-agent:158e1098, digest sha256:35e66696a1bdc0d470e0d11d7711e426548a09d31190bdd94cd1350b464dc839 (matches image_digest in the run-34191503812 aggregate). Interpreter confirmed 3.13.15 via sys.version_info. Searches cover the shipped tree under /opt/stackstate-agent and /etc/stackstate-agent, counting the stdlib module that defines an API separately from code that calls it — the distinction that decides three of these four.

CVE-2026-15806 — not_affected / vulnerable_code_not_in_execute_path

urllib.request.HTTPPasswordMgr{,WithDefaultRealm} returns an https-registered password for the same host over plain http, so credentials can leave unencrypted. It requires code to register a credential via add_password().

  • .add_password( call sites in the shipped tree: zero, outside the stdlib's own definitions (urllib/request.py:810,885) and a docstring example (:53).
  • Remaining textual matches are inert MovedAttribute alias-table rows in six.py.
  • Agent HTTP goes through requests/urllib3, which use their own HTTPBasicAuth and never touch urllib.request password managers.

With nothing registered, find_user_password has nothing to return and the downgrade cannot occur.

CVE-2026-19672 — not_affected / vulnerable_code_not_in_execute_path

tarfile's tar and data filters can create empty directories outside the destination on POSIX. It requires extraction to run with one of those two filters.

  • tarfile.TarFile.extraction_filter is None in this image, so on 3.13 the default is fully_trusted_filter; the affected filters apply only when passed explicitly.
  • The only explicit filter='data' is site-packages/build/_compat/tarfile.py:27 — the PEP 517 build frontend. Grepping /etc/stackstate-agent and /opt/stackstate-agent/bin for pyproject_hooks or from build import returns zero references, and /etc/stackstate-agent/checks.d contains no Python checks, so no agent entrypoint invokes it.
  • The other extractall() sites (build/_builder.py:376, pyproject_hooks/_in_process/_in_process.py:241) operate on wheels via zipfile, not tarfile.
  • The runtime python-dateutil path uses extractfile() only and writes nothing; the one genuine tarfile.extract() (dateutil/zoneinfo/rebuild.py) passes no filter= and needs the zic binary, absent from this image.

This is the same premise already reviewed for CVE-2026-4360, re-derived against the current image.

CVE-2026-15310 — not_affected / vulnerable_code_not_in_execute_path

A crafted zip drives memory exhaustion when a member declares a large uncompressed size under bzip2/LZMA/Zstandard, because the decompressor pre-allocates from the attacker-supplied header.

  • References to ZIP_BZIP2, ZIP_LZMA, ZIP_ZSTANDARD in the shipped tree: zero. No compress_type= selection outside packaging tooling, so those codecs are never chosen.
  • Of four zipfile.ZipFile() sites, three are build-frontend tooling no entrypoint invokes, reading locally-built wheels rather than untrusted input.
  • The only runtime-reachable site is requests/utils.py:280 in extract_zipped_paths(). Verified in-image that certifi.where() resolves to an existing file, so the function returns at its os.path.exists(path) guard before reaching ZipFile.

Reviewer note: this is the weakest of the three. It rests on "no attacker-controlled zip reaches the runtime" rather than on the API being absent, and the status_notes says so explicitly. Please weigh it separately from the other two.

CVE-2026-17084 — deliberately no statement

stringprep tables B.2/B.3 use modern Unicode instead of 3.2.0 (IDNA 2003 mappings). This one is reachable, proven live:

  • encodings.idna.nameprep calls stringprep.map_table_b2 — confirmed by reading the shipped source.
  • Instrumenting map_table_b2 in the shipped interpreter and running 'straße.Example.COM'.encode('idna') produced b'strasse.Example.COM' with 6 calls to the vulnerable table.
  • Live .encode('idna') call sites in shipped code: asn1crypto/_iri.py:78, asn1crypto/x509.py:229,2896, pydantic/v1/networks.py:377,379. urllib3/util/connection.py idna-encodes the host on outbound connections.

We cannot honestly answer "not reachable", so no statement is written. It stays visible and keeps gating. (The CVE text names in_table_b2(); 3.13 exposes map_table_b2/map_table_b3 — those are the real symbols.)

Validation

tools/build_index.py --check passes; index.json is unchanged because all three statements reuse product PURLs already indexed for this image.

Grype, --by-cve, same invocation shape the image-pipeline gate uses:

active ignored
no VEX 13 0
this file + containerd statements 1 12

The 12 suppressed are the 3 new Python rows, CVE-2025-15367, CVE-2026-4360, 4× krb5 and 3× containerd. The single remaining active finding is CVE-2026-17084 — exactly the one intended to stay visible.

Trivy cannot demonstrate these: it reports no Python rows for this image at all (its only agent finding is GO-2026-5932 on golang.org/x/crypto, keyed by Go advisory ID). These 3.13.15 statements are Grype-effective by design, matching every existing Python statement in this file.

Remaining

…ble one visible

The daily dev chart scan gates on stackstate-k8s-agent across
CRITICAL,HIGH,MEDIUM,LOW. After the 3.13.15 realignment the image is down to
four findings, all CPython 3.13.15 with no upstream fix, so no bump can clear
them and a repo-local exception cannot either -- the chart scan does not read
consumer exception trees.

Three of the four are genuinely unreachable in the shipped runtime:

- CVE-2026-15806 (urllib.request password-manager credential downgrade): the
  shipped tree has zero .add_password() call sites, so no credential is ever
  registered and find_user_password cannot return one.
- CVE-2026-19672 (tarfile 'tar'/'data' filters create dirs outside the
  destination): extraction_filter is None on 3.13 so those filters are never
  the default, and the only explicit filter='data' is in the PEP 517 build
  frontend, which no agent entrypoint invokes.
- CVE-2026-15310 (zipfile bzip2/LZMA/Zstd pre-allocation): those compressors
  are never selected, and the only runtime-reachable ZipFile site returns at
  its os.path.exists guard.

The fourth, CVE-2026-17084 (stringprep tables B.2/B.3 use modern Unicode
rather than 3.2.0), is deliberately left with no statement. It is reachable:
encodings.idna.nameprep calls stringprep.map_table_b2, and instrumenting the
shipped interpreter shows a plain host.encode("idna") invoking it six times.
There are live call sites in asn1crypto and pydantic. Quieting a finding we
cannot honestly call unreachable would be worse than reporting it, so it
stays visible and keeps gating.

Each claim is re-derived against the exact gated image
quay.io/stackstate/stackstate-k8s-agent:158e1098, digest
sha256:35e66696a1bdc0d470e0d11d7711e426548a09d31190bdd94cd1350b464dc839,
counting stdlib modules that define an API separately from code that calls it.

Verified with Grype --by-cve: 13 active / 0 ignored without VEX, 1 active / 12
ignored with this file plus the containerd statements. The one remaining
active finding is CVE-2026-17084, as intended.
@LouisLotter
LouisLotter merged commit 75e0769 into main Sep 8, 2026
1 check passed
@LouisLotter
LouisLotter deleted the agent-python-no-fix-vex branch September 8, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants