Repository navigation
Description
ROADMAP M8 just landed cross-port. The new spec at docs/HARDENING.md defines the decoder-safety contract every port must enforce against untrusted input; the adversarial corpus tests it; the 9-port matrix workflow runs it on every protowire PR.
Companion check_decode reference for this port: scripts/check_decode.py (commit 51df4a6).
The workflow ships in advisory mode (continue-on-error: true) so failures do not block PRs. This issue tracks driving this port's regressions to zero so we can flip the gate to required for python.
Current baseline — 4 advisory failures (1 crash)
| Corpus | Verdict | Reason |
|---|---|---|
pxf/deep-nesting-200.pxf |
FAIL_VERDICT |
accepted; missing MaxNestingDepth cap (heavy lifting in C++ FFI) |
pxf/deep-nesting-1000.pxf |
FAIL_VERDICT |
same |
pxf/deep-nesting-100000.pxf |
FAIL_CRASH |
C++ FFI stack overflow leaks through to Python |
pxf/invalid-utf8-string.pxf |
FAIL_VERDICT |
C++ FFI's PXF decoder doesn't enforce UTF-8 on proto3 string |
The Python port passes most corpus inputs because:
pb/deep-submessage-200.binpbrejected bygoogle.protobuf(which has built-in recursion limits).pb/length-prefix-truncated.binpbrejected bygoogle.protobuf.pxf/long-numeric.pxfrejected by Python'sint(str)raisingValueErroron the int64 parser path.pxf/lone-surrogate.pxfrejected at the lexer (already validatesValidRune).
What to fix
The 4 PXF failures are all on the C++ FFI side (the _protowire/module.cc extension and its protowire-cpp dependencies). Two HARDENING.md invariants in protowire-cpp will fix them upstream:
- §Recursion —
MaxNestingDepth = 100cap on the C++ PXF parser/decoder. Tracked in [protowire-cpp's M8 issue]; once that lands, this port inherits the fix automatically. The 100k-deep crash converts to a clean error returned through the FFI. - §UTF-8 — strict UTF-8 enforcement on proto3
stringpopulating in protowire-cpp's PXF decoder. Same upstream fix.
If the maintainers want a Python-side defense-in-depth in addition (e.g. setting sys.setrecursionlimit lower, or wrapping the FFI call in a stack-budget check), that's optional but not required to clear the corpus — the upstream C++ fixes are sufficient.
The pure-Python envelope.py decoder also has a couple of IndexError-vs-ValueError exception-type leaks the cross-port review flagged (envelope.py:131-132, 85-93); those aren't tested by the current corpus but are good hygiene fixes.
Reproduce locally
# In the venv with `pip install -e .` already done:
WALLCLOCK_SECONDS=10 bash ../protowire/scripts/cross_security_check.sh
# Or run a single corpus input:
python3 scripts/check_decode.py --format pxf \
--schema adversarial.v1.Tree \
--proto ../protowire/testdata/adversarial/adversarial.proto \
--input ../protowire/testdata/adversarial/pxf/deep-nesting-100000.pxf
echo $? # 0 = accepted, 1 = clean reject, 134/139 = crash via FFIConvergence
When all 4 corpus inputs pass on this port, comment here. We'll flip continue-on-error: false for the python matrix entry in protowire/.github/workflows/security.yml. When all 9 ports converge, the workflow becomes a required check.
Cross-port context: 8 sibling per-port issues track the same convergence. The full cross-port matrix lives in protowire's ROADMAP § M8. Most of this port's findings are upstream of protowire-cpp — see that repo's M8 issue.
Activity
trendvidia commentedon May 5, 2026
Cross-port convergence is tracked at trendvidia/protowire#39 — that issue holds the full 9-port verdict matrix and per-port convergence checklist.
trendvidia commentedon May 5, 2026
M8 corpus convergence: all 4 advisory failures cleared on this port.
Root cause: wheels CI was pinned to
protowire-cpp@v0.70.0(SHA0ac4067), which predates the M8 decoder-hardening commits (25b93aePXFMaxNestingDepth+ UTF-8,329bdeaSBE bounds) that landed onmainvia PR #2 (merge7b712cd). The fixes are not in any tag yet.Changes (this repo, on
main):d86fc01— bumpPROTOWIRE_CPP_REFdefaultv0.70.0→main. Will bump tov0.71.0once the C++ port cuts that tag.4ed1684—envelope.pyhygiene:_dec_varintand_skip_fieldnow raiseValueErroron truncated input instead of leakingIndexError. Two regression tests added. (Cross-port review nit, not corpus-blocking — but in scope here so folded in.)Verification (local rebuild against
main):cross_security_check.shshows zeropythonrows in the failure list.pytest: 51/51 pass.Ready to flip
continue-on-error: falsefor thepythonmatrix entry inprotowire/.github/workflows/security.ymlwhenever you're ready. Wheels CI on next push will rebuild againstprotowire-cpp@main.trendvidia commentedon May 5, 2026
Follow-up: pin bumped to
protowire-cpp@v0.71.0(commitdf8e504, the first tag containing both M8 fixes).1505bd3— replaces the temporarymainpin fromd86fc01. Re-verified against the tag locally: all 4 adversarial PXF corpus inputs reject cleanly, pytest 51/51.Wheels CI will rebuild against
v0.71.0on next push.trendvidia commentedon May 5, 2026
Released
protowire-python@v0.71.0(commit69b4465, tag pushed) — package version aligned withprotowire-cpp@v0.71.0. Wheels CI build queued for the tag push.Final state of this port re: issue:
protowire-cpp@v0.71.0(immutable tag).envelope.pycross-portValueErrorcontract enforced + tested.cross_security_check.shshows zeropythonrows.Ready to flip
continue-on-error: falsefor thepythonmatrix entry whenever you flip the gate.trendvidia commentedon May 5, 2026
Gate flip in flight: trendvidia/protowire#54 adds
pythonto the required-port set insecurity.yml. CI on the PR will exercise the now-required gate against this port; once that's green and merged, HARDENING corpus regressions inprotowire-pythonwill hard-failprotowirePRs instead of being advisory.diegocoronado1416-collab commentedon May 9, 2026
bfrancojr commentedon Nov 5, 2026
This issue's done-when is not met yet. The comments above say the gate flip was in flight (protowire#54). But at protowire main
c48fcc0,.github/workflows/cross-port-security-check.ymlnever mentionspython: no checkout, no build, andREQUIRE_PORTS: 'go,cpp,java,java-lite,ts,swift'. So protowire's CI does not run the corpus against Python at all.Locally, the port does conform.
cross_security_check.shwithREQUIRE_PORTS=pythonpasses against protowire-cpp v2.0.0's release head (cc25129): 124 pairs across cpp and python.What's left: a protowire PR that builds this port in that workflow and adds
pythontoREQUIRE_PORTS. It is best done after the pin moves to v2.0.0 (#31), so the workflow builds the C++ version that rejects thenullfixtures. Close this issue when that PR is merged.bfrancojr commentedon Nov 7, 2026
Harness half: #49 (
check_decode --format pxf-pb/pb-text). Gate half: trendvidia/protowire#590 (draft until #49 merges) runs Python in the write, text and security gates and adds it to each workflow'sREQUIRE_PORTS. Measured locally: write 87/87 and text 60/60 for Python with no exclusions, security 136/136. This closes when #590 merges.