fix(xtest)!: read the manifest entry as manifest.json, per spec - #600
pflynn-virtru wants to merge 4 commits into
Conversation
The OpenTDF spec puts `manifest.json` at the root of the archive
("The manifest.json file MUST be in JSON format and reside within the
root of the OpenTDF Zip archive" -- spec/schema/OpenTDF/manifest.md).
xtest hardcoded `0.manifest.json` in four places, so the suite was
asserting a container the spec does not describe. Every SDK agreed on
the same wrong name, so nothing ever caught it.
platform#4049 aligns the go writer. Its xct cells came back 93 failed,
5 passed, and 78 of those failures were xtest's own Python rather than
anything wrong with the SDK: 46 `KeyError: There is no item named
'0.manifest.json'` from `manifest()`/`validate_manifest_schema()`, and
32 `FileNotFoundError` from `update_manifest()` reaching into the
extracted tree by name.
Reads the spec name and no other. No fallback to the legacy name: a
tolerant reader is what let the divergence survive unreported, and
accepting both here would hide exactly the defect the suite exists to
find. `manifest_entry_name()` centralizes the lookup and lists the
archive's actual members on failure -- zipfile's own KeyError names
only the entry it wanted, which made a renamed manifest read as an
empty archive.
`0.payload` is unchanged. It is not fixed by the spec: it is whatever
the manifest's `payload.url` says, so renaming it would change manifest
contents rather than archive layout.
Adds `test_manifest_entry_name_is_spec_compliant`, ungated. A writer
still emitting `0.manifest.json` fails it, and that red cell is the
finding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change standardizes TDF manifest archives on ChangesManifest entry compliance
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The manifest conformance test follows the repository’s normal focused-test behavior and covers all configured encryption SDKs by default. No actionable merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit checks the archive door Comment |
Keep the normative rule and the reason a fallback is refused; the platform#4049 timeline and failure counts belong in the PR description, not in comments that outlive it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
X-Test Failure Report |
Out of scope. The payload entry name is not what platform#4049 changes, so update_payload and the fixtures keep their 0.payload literals. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
X-Test Failure Report |
…4 fixtures These fixtures model TDF layout deliberately -- payload first, manifest after it at the ZIP64 boundary -- so the entry should come from the same constant the readers use rather than a repeated literal. Also drops a byte count from the overrun test's comment. It said the name was 15 bytes, which stopped being true when the entry was renamed, and zipinspect never reads the name anyway. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
X-Test Failure Report |
The OpenTDF spec puts the manifest at the archive root under `manifest.json`. This SDK writes and reads `0.manifest.json`. The `0.` prefix is a holdover from an early design that anticipated several payload/manifest pairs per archive; that design never shipped. Write: `writeStream` emits `manifest.json`. Read: `loadTDFStream` and `getPolicyId` resolve the entry through `manifestEntryName()`, which prefers the spec name and falls back to the legacy name, so archives written by earlier releases keep opening. The dual-name handling lives at the TDF layer rather than in `ZipReader`, whose signature is unchanged — its other lookup is a payload path that must stay exact. Resolution runs against the central directory, not against a failed read, so an oversized manifest under the spec name stays a size error instead of silently yielding the superseded legacy entry. `0.payload` is unchanged: it is recorded in the manifest's `payload.url`, so renaming it would alter manifest contents rather than just archive layout. BREAKING CHANGE: archives written by this SDK name the manifest entry `manifest.json`. Readers that look it up by exact name — web-sdk <= 0.20, java-sdk, client-cpp — cannot read them until they accept the spec name. Refs opentdf/platform#3513, opentdf/platform#4049, opentdf/tests#600. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Paul Flynn <pflynn-virtru@users.noreply.github.com>
The OpenTDF spec puts the manifest at the archive root under `manifest.json`. This SDK writes and reads `0.manifest.json`. The `0.` prefix is a holdover from an early design that anticipated several payload/manifest pairs per archive; that design never shipped. Write: `writeStream` emits `manifest.json`. Read: `loadTDFStream` and `getPolicyId` resolve the entry through `manifestEntryName()`, which prefers the spec name and falls back to the off-spec name, so archives written by earlier releases keep opening. The dual-name handling lives at the TDF layer rather than in `ZipReader`, whose signature is unchanged — its other lookup is a payload path that must stay exact. Resolution runs against the central directory, not against a failed read, so an oversized manifest under the spec name stays a size error instead of silently yielding the superseded off-spec entry. `0.payload` is unchanged: it is recorded in the manifest's `payload.url`, so renaming it would alter manifest contents rather than just archive layout. BREAKING CHANGE: archives written by this SDK name the manifest entry `manifest.json`. Readers that look it up by exact name — web-sdk <= 0.20, java-sdk, client-cpp — cannot read them until they accept the spec name. Refs opentdf/platform#3513, opentdf/platform#4049, opentdf/tests#600. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Paul Flynn <pflynn-virtru@users.noreply.github.com>



Refs opentdf/platform#3513. Unblocks opentdf/platform#4049.
Warning
Breaking for every SDK shipping today. xtest now requires the spec's
manifest.jsonand rejects0.manifest.json. go (before platform#4049),java-sdk and web-sdk all still write the legacy name, so they fail. That is
the intended report — see Expected failures.
Problem
The spec puts the manifest at the archive root under
manifest.json:xtest hardcoded
0.manifest.jsonin its four manifest lookups, so the suiteasserted a container the spec does not describe. Every SDK agreed on the same
wrong name, so nothing caught it —
test_manifest_validitychecks themanifest's contents against
manifest.schema.jsonand never looks at whatthe archive calls it.
platform#4049 aligns the go writer. Its xct cells came back 93 failed, 5
passed, identical across all three cells building go from that branch.
Classifying all 93:
KeyError: There is no item named '0.manifest.json' in the archivemanifest(),validate_manifest_schema()FileNotFoundError: .../unzipped/0.manifest.jsonupdate_manifest()CalledProcessErroron decryptzip: file not found(go@main),tdf doesn't contain a manifest(java@main),Unable to retrieve CD manifest(js@main)assert b'segment' in ...test_tdf_altered_payload_endexpects a segment-integrity message, gets a manifest-lookup failure78 of 93 are this repo. The other 15 are 5 tests × 3 decrypting SDKs, and are
the downstream reader gaps platform#4049's own impact table predicts.
Change
Every manifest lookup goes through
manifest_entry_name(), which returnsmanifest.jsonor raises. No fallback to the legacy name — a tolerantreader would report a conformant archive and a non-conformant one identically,
which is how the divergence went unnoticed. The unit tests pin the rejection,
not just the acceptance.
Adds one ungated conformance test asserting the writer's entry name.
0.payloadis untouched: the spec does not fix it (it is whatever themanifest's
payload.urlsays), and platform#4049 does not change it.Expected failures
This is the test half of platform#3513 and cannot be green until the writers
move:
TDFReader.javaexactcontainsKey,SDK.isTDFasserts an exactly 2-entry set. Not startedgetManifest(cd, '0.manifest.json')intdf3/src/tdf.ts. Not startedDraft until at least go lands — merging sooner would leave this repo's CI red
on main.
This PR's own xct run bears that out: all 12 cells report 116 failed, 30
passed, 1 skipped, and the failures are two kinds only — 115
KeyErrorfromthe manifest lookup and 1
AssertionErrorfrom the conformance test. Nodecrypt failures, because each SDK writes and reads the legacy name
consistently. The 30 passes are the five tests in
test_tdfs.pythat neverparse a manifest.
116 is a floor, not a ceiling: the failing step aborts the job, so the later
step covering
test_abac.py,test_pqc.pyandtest_dpop.pynever runs, andthose parse manifests too.
Testing
The four rejection cases are mutation-tested: re-adding a legacy-name fallback
to
manifest_entry_name()fails exactly those four and leaves the other 24green, so they cannot pass vacuously.
Not run: the integration matrix, which needs a live platform and the three
SDK CLIs. The 78 cells this fixes are characterized from the platform#4049 CI
logs, not from a local run.
🤖 Generated with Claude Code