Skip to content

chore(xtest): cross-SDK test for duplicate KAO on same KAS+split (DSPX-3379)#555

Open
dmihalcik-virtru wants to merge 2 commits into
mainfrom
DSPX-3379-multisplit-allowed
Open

chore(xtest): cross-SDK test for duplicate KAO on same KAS+split (DSPX-3379)#555
dmihalcik-virtru wants to merge 2 commits into
mainfrom
DSPX-3379-multisplit-allowed

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Jul 9, 2026

Copy link
Copy Markdown
Member

What

Adds a cross-SDK xtest, test_tdf_with_duplicate_kao_same_kas, asserting that a
TDF whose keyAccess array contains two elements with the same split id and
the same KAS URI
still decrypts on every SDK.

Uses the existing update_manifest helper to append a byte-identical copy of the
first KAO (new change function duplicate_first_kao), producing a valid,
decryptable duplicate — either copy unwraps the split.

Why (DSPX-3379)

The same KAS wrapping the same split is legitimate — an authoring mistake (same
key used twice) or an intentional multi-key-on-one-KAS setup. Expected behavior:
the file decrypts (or fails with permission-denied) exactly as if the copies
lived on distinct URIs.

This is the executable spec for the fix:

  • go-sdk / java-sdk already handle this (skip the already-satisfied split,
    keep reconstructing) → expected pass.
  • web-sdk (js) currently throws InvalidFileError: Multiple keys detected for Key Access Server [...] in splitLookupTableFactory → expected fail
    until the companion web-sdk fix lands, after which this goes green for js.

Test status

Merging this test first intentionally red-lights js decrypt to lock in the
expected behavior; the opentdf/web-sdk fix (branch DSPX-3379-multisplit-allowed)
makes it pass.

Local validation

pytest --collect-only discovers the new test against a running platform
(v0.9.0). A full cross-SDK roundtrip run requires the SDK CLI wrappers
(otdf-sdk-mgr checkout --all + make), which were not built in this
environment.

Summary by CodeRabbit

  • New Features
    • Added support for the new “multikao” capability in SDK support detection, including CLI handling with version-based gating.
    • Updated feature recognition so “multikao” is considered supported for selected SDKs without running the external supports check.
  • Tests
    • Added a tamper test that duplicates the first key-access entry in an encrypted manifest, verifies the manifest contents update correctly, and confirms decrypted output still matches the original plaintext.

@dmihalcik-virtru
dmihalcik-virtru requested review from a team as code owners July 9, 2026 11:11
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 009ed7d7-c36c-4589-8685-aa5ed262d463

📥 Commits

Reviewing files that changed from the base of the PR and between 6e4cf33 and 37604a2.

📒 Files selected for processing (1)
  • xtest/sdk/js/cli.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • xtest/sdk/js/cli.sh

📝 Walkthrough

Walkthrough

Adds multikao support detection for SDK feature checks and a tamper test helper that duplicates the first KAO entry in a TDF manifest, then verifies decryption still succeeds.

Changes

Duplicate KAO Tamper Test

Layer / File(s) Summary
Multikao support detection
xtest/tdfs.py, xtest/sdk/js/cli.sh
Adds "multikao" to the feature type and routes support checks through SDK-specific handling, including a new supports multikao CLI branch that gates on the SDK version.
Duplicate KAO manifest test
xtest/test_tdfs.py
Adds duplicate_first_kao to deep-copy and append the first KAO entry, and adds test_tdf_with_duplicate_kao_same_kas to mutate the manifest, validate the duplicated entry, decrypt, and compare plaintext.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • opentdf/tests#551: Extends the same JavaScript CLI supports subcommand with another feature flag.

Sequence Diagram(s)

sequenceDiagram
  participant TestHarness
  participant SDK
  participant JSCLI
  TestHarness->>SDK: Check multikao support
  alt Go or Java SDK
    SDK-->>TestHarness: Return supported
  else JavaScript SDK
    SDK->>JSCLI: Run supports multikao
    JSCLI->>JSCLI: Parse SDK version
    JSCLI-->>SDK: Return version gate result
  end
Loading

Poem

A rabbit found a KAO pair,
Duped one gently with great care.
Same sid, same url, all aligned,
Then decryption came out fine.
Hop-hop, the manifest held strong 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: a cross-SDK xtest for duplicate KAO handling on the same KAS+split.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-3379-multisplit-allowed

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a test case test_tdf_with_duplicate_kao_same_kas and a helper function duplicate_first_kao in xtest/test_tdfs.py to ensure that a TDF file remains decryptable when the manifest contains duplicate Key Access Objects (KAOs) sharing the same split ID and KAS URI. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xtest/test_tdfs.py (1)

876-879: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider asserting wrappedKey equality to strengthen the "byte-identical" duplicate verification.

The test verifies url and sid match between the original and duplicated KAO, but not wrappedKey — the field that actually matters for split unwrapping. The PR summary and docstring both claim a "byte-identical copy," so asserting wrappedKey equality would make that claim explicit and catch any future regression in the copy logic that might alter the wrapped key.

♻️ Suggested addition
     assert kaos[0].url == kaos[-1].url
     assert kaos[0].sid == kaos[-1].sid
+    assert kaos[0].wrappedKey == kaos[-1].wrappedKey
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xtest/test_tdfs.py` around lines 876 - 879, The duplicate KAO check in the
tdfs manifest test only verifies url and sid, so it does not fully prove the
copy is byte-identical. Update the assertion block around
tdfs.manifest(b_file).encryptionInformation.keyAccess to also compare wrappedKey
between kaos[0] and kaos[-1], alongside the existing url and sid checks, so the
test explicitly guards the split-unwrapping key material.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@xtest/test_tdfs.py`:
- Around line 876-879: The duplicate KAO check in the tdfs manifest test only
verifies url and sid, so it does not fully prove the copy is byte-identical.
Update the assertion block around
tdfs.manifest(b_file).encryptionInformation.keyAccess to also compare wrappedKey
between kaos[0] and kaos[-1], alongside the existing url and sid checks, so the
test explicitly guards the split-unwrapping key material.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b0b564d5-6006-4849-a7e4-2f3869a7c00e

📥 Commits

Reviewing files that changed from the base of the PR and between 58fd341 and 2e4565f.

📒 Files selected for processing (1)
  • xtest/test_tdfs.py

dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 9, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from 2e4565f to 2cdec32 Compare July 9, 2026 19:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xtest/sdk/js/cli.sh (1)

99-105: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the jq filter into a variable.

SonarCloud flags '.["@opentdf/sdk"]' as repeated 5 times across the supports block. A local variable would reduce duplication and make future version checks cleaner. This is optional and can be deferred since it touches lines outside this PR's scope.

♻️ Optional refactor
   if [ "$1" == "supports" ]; then
     if ! cd "$SCRIPT_DIR"; then
       echo "failed: [cd $SCRIPT_DIR]"
       exit 1
     fi
+    SDK_VERSION_FILTER='.["`@opentdf/sdk`"]'
     case "$2" in
       # ...
       multikao)
         # Tolerate a KAO array where the same KAS wraps the same split more than
         # once (DSPX-3379). Shipped in web-sdk >= 0.20.0.
         set -o pipefail
-        npx $CTL --version | jq -re '.["`@opentdf/sdk`"]' | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 >= 20)) exit 0; else exit 1; }'
+        npx $CTL --version | jq -re "$SDK_VERSION_FILTER" | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 >= 20)) exit 0; else exit 1; }'
         exit $?
         ;;

The same SDK_VERSION_FILTER variable could then replace the literal in the ecwrap, key_management, obligations, and attribute_traversal branches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xtest/sdk/js/cli.sh` around lines 99 - 105, Extract the repeated jq filter
used in the supports block into a local variable, and update the multikao branch
to use it through the existing cli.sh flow. Keep the change centered on the
version-check logic around npx, jq, and awk so the same SDK version filter can
be reused consistently in the other branches mentioned by SonarCloud (ecwrap,
key_management, obligations, and attribute_traversal).

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@xtest/sdk/js/cli.sh`:
- Around line 99-105: Extract the repeated jq filter used in the supports block
into a local variable, and update the multikao branch to use it through the
existing cli.sh flow. Keep the change centered on the version-check logic around
npx, jq, and awk so the same SDK version filter can be reused consistently in
the other branches mentioned by SonarCloud (ecwrap, key_management, obligations,
and attribute_traversal).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4f60caf-f898-4549-95ad-8090781a02f4

📥 Commits

Reviewing files that changed from the base of the PR and between 2e4565f and 2cdec32.

📒 Files selected for processing (3)
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_tdfs.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • xtest/test_tdfs.py

@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from 2cdec32 to b8939fa Compare July 10, 2026 14:15
@dmihalcik-virtru dmihalcik-virtru changed the title test(xtest): DSPX-3379 — same KAS may wrap the same split (duplicate KAO must decrypt) chore(xtest): cross-SDK test for duplicate KAO on same KAS+split (DSPX-3379) Jul 10, 2026
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 10, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 10, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
…X-3379)

Adds test_tdf_with_duplicate_kao_same_kas + duplicate_first_kao manifest
mutation. Gated behind a new 'multikao' feature so SDK versions without the
fix are skipped instead of failing the workflow: go/java always support it;
web-sdk (js) reports support at >= 0.20.0 (where DSPX-3379 ships).

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-3379-multisplit-allowed branch from b8939fa to 6e4cf33 Compare July 13, 2026 17:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xtest/sdk/js/cli.sh (1)

103-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the SDK version jq filter constant.

This adds another occurrence of the literal .["@opentdf/sdk"], which is already duplicated across the version checks. Define the filter once and reuse it to reduce maintenance drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xtest/sdk/js/cli.sh` at line 103, Define a shared variable for the jq SDK
version filter used by the version checks, then update the command in the
version check to reference that variable instead of repeating the literal
.["`@opentdf/sdk`"].

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@xtest/sdk/js/cli.sh`:
- Line 103: Define a shared variable for the jq SDK version filter used by the
version checks, then update the command in the version check to reference that
variable instead of repeating the literal .["`@opentdf/sdk`"].

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6dae0e98-b758-486c-9103-a480fcab9244

📥 Commits

Reviewing files that changed from the base of the PR and between b8939fa and 6e4cf33.

📒 Files selected for processing (3)
  • xtest/sdk/js/cli.sh
  • xtest/tdfs.py
  • xtest/test_tdfs.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • xtest/tdfs.py
  • xtest/test_tdfs.py

dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 16, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 16, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 16, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 16, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Jul 16, 2026
splitLookupTableFactory now returns split id -> KeyAccessObject[] (a
disjunction of alternatives) instead of a url-keyed map, and no longer
throws when a KAS repeats within a split. unwrapKey feeds each alternative
to anyPool with a unique key so duplicate KAS entries are each tried until
one succeeds. Matches go-sdk/java-sdk behavior; validated by xtest
test_tdf_with_duplicate_kao_same_kas (opentdf/tests#555).

Adds unit tests for duplicate + same-KAS-different-KID, an in-process
decrypt-path test, and fills spec/DSPX-3379.md.


---------

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant