Repository navigation
fix(aggregator): enforce strict Content-Length and producer validation in server and use loads_unique in CheckpointLog (#151) - #155
Conversation
806d99f to
74bc66a
Compare
rajnisht7
left a comment
There was a problem hiding this comment.
Content-Length strictness and the loads_unique switch for checkpoints looks good
a few things needs tighten:
- Explicit `producer: null still accepted
producer = body.get("producer")
if producer is not None and ...body.get() returns None for both a missing key and an explicit JSON null, so:
{"producer": null, "claims": [...]}gets 200. whereas issues asks that a present producer must be a non-empty string. kindly distinguish presence:
if "producer" in body:
producer = body["producer"]
if not isinstance(producer, str) or not producer:
raise ValueError(...)
else:
producer = Noneand add None to the bad-producer test cases.
- loads_unique does not reject NaN / InfinityIssue #151 called out both duplicate-key and non-finite-constant rejection, and proposed loads_unique. The helper only rejects duplicates (object_pairs_hook); NaN / Infinity / -Infinity are still accepted. Required checkpoint fields with those values later fail type checks in from_dict, and extra fields are ignored, so this is not a forgery path, but the PR does not fully match the issue’s stated scope.
Either:
make loads_unique (or a checkpoint-specific loader) reject non-finites via parse_constant, and add tests, or narrow the issue/PR text to “duplicate-key + UTF-8 + dict check”. - Tests- Current cases are a good start but miss:
producer: null / None- Unicode decimal digits that old int() accepted (e.g. \u0662) — \u00b2 was already rejected by the old path
- a couple of valid boundaries ("0", leading zeros, MAX_BODY_BYTES)
non-blocking:
- Very long digit Content-Length ("9"*5000) passes the new digit check then raises a raw ValueError from int(); outer do_POST catches it as 400, but restoring a try/except so _read_json_body always raises _BodyError would keep the helper contract clean.
|
Thanks @rajnisht7! Addressed in the latest commit: (1) |
dinakarjs
left a comment
There was a problem hiding this comment.
Reviewed this head; unittest discovery passes 407 tests. The source changes address the stated strict Content-Length, present producer, and persisted JSON boundaries. Before treating the new regression coverage as complete, please split test_non_decimal_content_length_and_non_string_producer_rejected into independent cases: its first Content-Length assertion prevents producer, huge-length and persisted-checkpoint assertions from running when that first guard is absent. Demonstrate each boundary fails with its own check disabled, retaining test names and valid controls. I am leaving this as a comment rather than approving on a green suite alone.
rajnisht7
left a comment
There was a problem hiding this comment.
Agreed with @dinakarjs, the new test_non_decimal_content_length_and_non_string_producer_rejected is one big method that chains multiple independent security checks. If the first assertion (unicode Content-Length) fails, the rest (producer, huge length, checkpoint cases) never run, so we lose failure isolation.
A few other gaps in the regression coverage:
- No -Infinity case (only NaN and Infinity are tested for checkpoints).
- No exact MAX_BODY_BYTES boundary (== 410241024 should pass the length gate, +1 should 413).
- loads_unique’s new contract (non-finites + duplicates) is only tested indirectly via CheckpointLog. Prefer direct unit tests for it.
- No HTTP /batch tests for non-finite values or duplicate keys in the request body (the behavior works, but isn’t locked down).
- Duplicate Content-Length headers aren’t rejected, .get() only sees the first value, and mismatched lengths can hang the connection. Worth rejecting when values differ.
|
Thanks @dinakarjs and @rajnisht7! Addressed all points in the latest commit: (1) split |
dinakarjs
left a comment
There was a problem hiding this comment.
Re-reviewed 2ee5b29. The split regressions and duplicate-header handling are present, but HTTPMessage.get_all returns None for an absent Content-Length; the new comprehension raises TypeError and disconnects the client instead of the existing 400 refusal. test_missing_negative_and_malformed_lengths_are_400 reproduces RemoteDisconnected. Ruff also flags the unused local json import in CheckpointLog.
Signed author-adoption companion: dinakarjs#3, commit c5b137a. It normalizes missing get_all results to an empty list and removes the unused import. The existing regression fails before and passes after; full repaired suite: 409 passed, with repository Ruff and whitespace clean. No new header policy.
@drk-ram please adopt and obtain independent review of the combined head. This is COMMENT because I contributed the fix, not independent approval. Hosted checks remain required. Prepared with OpenAI Codex.
…n in server and use loads_unique in CheckpointLog (agentrust-io#151) - Require strict ASCII decimal Content-Length headers, reject conflicting duplicate Content-Length values while preserving HTTP 400 on missing Content-Length, and require non-empty string top-level producer values (when present) in aggregator/server.py. - Reject non-finite JSON constants (NaN, Infinity, -Infinity) in trace_verify._verify.loads_unique and use loads_unique in aggregator/_mmr_log.py (CheckpointLog.__init__). - Add isolated regression tests in tests/test_aggregator.py for Content-Length validation/limits, producer validation, duplicate/non-finite JSON batch requests, and loads_unique / CheckpointLog persistence. Closes agentrust-io#151 Signed-off-by: Raghuvar Karthik Durga <177662415+drk-ram@users.noreply.github.com>
2ee5b29 to
31c3722
Compare
|
Thanks @rajnisht7 and @dinakarjs! Adopted the |
dinakarjs
left a comment
There was a problem hiding this comment.
Follow-up review at exact head 31c3722, Linux/Python 3.12.14 with the repository's hash-locked CI/witness/runtime dependencies and the local editable checkout.
Verified adoption of the missing-Content-Length normalization and removal of the unused import. Full unittest discovery: 409 tests pass; repository Ruff passes. The existing missing/malformed-length regression now exercises the HTTP refusal path successfully, closing my earlier follow-up. I contributed the correction, so this records incorporation rather than independent approval. Normal hosted checks and independent review of the combined head remain required.
Reviewed with OpenAI Codex.
What and why
Closes #151
Content-Length(raw_length.isascii() and raw_length.isdecimal()), reject conflicting duplicateContent-Lengthheaders while preserving HTTP 400 on missingContent-Length(self.headers.get_all("Content-Length") or []), and require non-empty stringproducervalues (when present) inaggregator/server.py.NaN,Infinity,-Infinity) intrace_verify._verify.loads_uniqueand useloads_unique(latest_path.read_text(encoding="utf-8"))inaggregator/_mmr_log.py(CheckpointLog.__init__).Validation
python -m unittest discover -s tests -q— passed (409 tests)python -m pytest tests/test_aggregator.py tests/test_intake_and_chain_hardening.py -q— passed (66 passed, 4 subtests passed)python tools/validate_registry.py— passed (OK: 3 day file(s) valid)python tools/validate_producers.py— passed (OK: 3 producer key file(s) valid)python -m ruff check aggregator/ src/trace_verify/_verify.py tests/test_aggregator.py— passed (All checks passed!)Registry impact
registry/Existing entries are append-only. If you are changing one, say why here: mirrors
and auditors may already have observed the current commit hashes, and a rewrite
diverges them.
Checks
pytestpassestools/validate_registry.py,tools/validate_producers.py)docs/anchor-format.mdis updated in the same PR, and existing inclusion proofs still verify