From 6583675fcf734dbeba65b830df108e81544cafdb Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Mon, 15 Jun 2026 15:53:45 -0400 Subject: [PATCH 01/22] ci: bump platform action SHA and enable dpop-nonce-challenge Update start-up-with-containers and start-additional-kas action SHAs to DSPX-3397-platform-service tip, and pass dpop-challenge-enabled: true so the DPoP nonce challenge tests are not skipped. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 48ed7fea..d22d582f 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,13 +297,14 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-up-with-containers@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true extra-keys: ${{ steps.load-extra-keys.outputs.EXTRA_KEYS }} log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} + dpop-challenge-enabled: true - name: Install uv uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 @@ -591,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: alpha @@ -603,7 +604,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: beta @@ -615,7 +616,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: gamma @@ -627,7 +628,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-port: 8484 @@ -639,7 +640,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -652,7 +653,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@11af44a5d4826ed281bf2e0e4e31d6ff6154b393 # pqc-enabled + uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: km2 From c579df349ff640edfb07651360bb5861c2ef806e Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 09:09:23 -0400 Subject: [PATCH 02/22] fix(js-shim): wire CLIENTID, CLIENTSECRET, and DPoP into cli.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Capture caller env vars before sourcing test.env (which unconditionally resets them), then restore them — allows pytest monkeypatch to override CLIENTID=opentdf-dpop for DPoP-specific tests. - Replace hardcoded --auth opentdf:secret with ${CLIENTID:-opentdf}:${CLIENTSECRET:-secret}. - Add XT_WITH_DPOP (algorithm, e.g. ES256) and XT_WITH_DPOP_KEY (PEM path) support, wired to --dpop / --dpop-key CLI flags. - Update _dpop_client_env fixture to also export XT_WITH_DPOP=ES256 so DPoP proof generation is actually exercised in test_dpop.py. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Dave Mihalcik --- xtest/sdk/js/cli.sh | 21 ++++++++++++++++++++- xtest/test_dpop.py | 6 ++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/xtest/sdk/js/cli.sh b/xtest/sdk/js/cli.sh index d3160ca3..163d8af4 100755 --- a/xtest/sdk/js/cli.sh +++ b/xtest/sdk/js/cli.sh @@ -18,6 +18,10 @@ # XT_WITH_ATTRIBUTES [string] - Attributes to be used for encryption # XT_WITH_MIME_TYPE [string] - MIME type for the encrypted file # XT_WITH_TARGET_MODE [string] - Target spec mode for the encrypted file +# XT_WITH_DPOP [string] - Enable DPoP token binding; value selects algorithm (e.g. ES256) +# XT_WITH_DPOP_KEY [string] - Path to PEM-encoded PKCS8 private key for DPoP signing +# CLIENTID [string] - Override OIDC client ID (default: opentdf) +# CLIENTSECRET [string] - Override OIDC client secret (default: secret) # SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) @@ -124,9 +128,17 @@ if [ "$XTEST_DIR" = "/" ]; then exit 1 fi +# Capture any caller-set overrides before test.env unconditionally resets them. +_pre_clientid="${CLIENTID:-}" +_pre_clientsecret="${CLIENTSECRET:-}" + # shellcheck disable=SC1091 source "$XTEST_DIR"/test.env +# Restore caller overrides (e.g. from pytest monkeypatch for DPoP client). +[ -n "$_pre_clientid" ] && CLIENTID="$_pre_clientid" +[ -n "$_pre_clientsecret" ] && CLIENTSECRET="$_pre_clientsecret" + src_file=$(realpath "$2") dst_file=$(realpath "$(dirname "$3")")/$(basename "$3") @@ -134,9 +146,16 @@ args=( --output "$dst_file" --kasEndpoint "$KASURL" --oidcEndpoint "$KCFULLURL" - --auth opentdf:secret + --auth "${CLIENTID:-opentdf}:${CLIENTSECRET:-secret}" ) +if [ -n "$XT_WITH_DPOP" ]; then + args+=(--dpop "$XT_WITH_DPOP") +fi +if [ -n "$XT_WITH_DPOP_KEY" ]; then + args+=(--dpop-key "$XT_WITH_DPOP_KEY") +fi + args+=(--containerType tdf3) if [ -n "$XT_WITH_ATTRIBUTES" ]; then diff --git a/xtest/test_dpop.py b/xtest/test_dpop.py index 085f71fc..beacb737 100644 --- a/xtest/test_dpop.py +++ b/xtest/test_dpop.py @@ -323,9 +323,11 @@ def _skip_unless_dpop_enabled(encrypt_sdk: tdfs.SDK, in_focus: set[tdfs.SDK]) -> @pytest.fixture(autouse=True) def _dpop_client_env(monkeypatch: pytest.MonkeyPatch) -> None: - # SDK CLI shims read CLIENTID from the environment; tests in this module - # must use the DPoP-bound client provisioned by `service provision keycloak`. + # SDK CLI shims read CLIENTID/XT_WITH_DPOP from the environment; tests in + # this module must use the DPoP-bound client provisioned by + # `service provision keycloak` and enable DPoP proof generation. monkeypatch.setenv("CLIENTID", "opentdf-dpop") + monkeypatch.setenv("XT_WITH_DPOP", "ES256") def test_dpop_happy_path_roundtrip( From 6141d7d19f47397f946c8aec04f75d3fdd051424 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 09:16:41 -0400 Subject: [PATCH 03/22] fix(ci): pass OTDFCTL_HEADS to all pytest steps so otdfctl.sh uses dist binary load_otdfctl() in conftest.py reads OTDFCTL_HEADS to resolve sdk/go/dist/{tag}/otdfctl.sh. Without it, every test step fell through to sdk/go/dist/main/otdfctl.sh or the non-dist sdk/go/otdfctl.sh, which falls back to go run github.com/opentdf/otdfctl@latest instead of the built branch binary. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index d22d582f..bfdb406b 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -687,6 +687,7 @@ jobs: KAS_DELTA_LOG_FILE: "../../${{ steps.kas-delta.outputs.log-file }}" KAS_KM1_LOG_FILE: "../../${{ steps.kas-km1.outputs.log-file }}" KAS_KM2_LOG_FILE: "../../${{ steps.kas-km2.outputs.log-file }}" + OTDFCTL_HEADS: ${{ steps.configure-go.outputs.heads }} - name: Sanitize sdk-version for artifact name id: artifact-name From b05e397a8e8e0f11483b59a1a2da058c8876a16e Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 09:55:49 -0400 Subject: [PATCH 04/22] ci: bump platform action SHA to pick up DPoP htu fix Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index bfdb406b..942e6895 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-up-with-containers@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: alpha @@ -604,7 +604,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: beta @@ -616,7 +616,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: gamma @@ -628,7 +628,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-port: 8484 @@ -640,7 +640,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -653,7 +653,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d0155a614e37de35bec0f47fe8bad18f346b9a60 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: km2 From c23d4678a977b6972bc083f4502d76e2fe79b9f6 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 10:17:46 -0400 Subject: [PATCH 05/22] ci: bump platform action SHA to pick up strict_htu feature flag Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 942e6895..9ed3381b 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-up-with-containers@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: alpha @@ -604,7 +604,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: beta @@ -616,7 +616,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: gamma @@ -628,7 +628,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-port: 8484 @@ -640,7 +640,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -653,7 +653,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@d55171e229915da2d43dbdc96057916d31be2710 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: km2 From f55e25d4f336a01339581b5f3f4c631be56637bf Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 11:57:15 -0400 Subject: [PATCH 06/22] feat(java-sdk): delegate dpop_nonce_challenge detection to binary Signed-off-by: Dave Mihalcik --- xtest/sdk/java/cli.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index cece9630..3b9fe545 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -118,8 +118,8 @@ if [ "$1" == "supports" ]; then exit $? ;; dpop_nonce_challenge) - echo "dpop_nonce_challenge not supported" - exit 1 + java -jar "$SCRIPT_DIR"/cmdline.jar supports dpop_nonce_challenge + exit $? ;; *) echo "Unknown feature: $2" From ccee061f9330be4d3c83bff73f39712907e88b98 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 12:15:44 -0400 Subject: [PATCH 07/22] fix(ci): correct SHA pin for platform action (24d7101c094b) Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 9ed3381b..0fbce5ea 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-up-with-containers@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: alpha @@ -604,7 +604,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: beta @@ -616,7 +616,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: gamma @@ -628,7 +628,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: ec-tdf-enabled: true kas-port: 8484 @@ -640,7 +640,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -653,7 +653,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c5c08867b1d74a8c86f49bd4aaef31f22 # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: km2 From d47082eb3d4c269a2c32e8a3ada3bb6688b63070 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 15:28:26 -0400 Subject: [PATCH 08/22] fix(java-cli): enable --verbose when available for silent failure diagnosis Signed-off-by: Dave Mihalcik --- xtest/sdk/java/cli.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index 3b9fe545..6e281197 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -197,5 +197,9 @@ if [ -n "$XT_WITH_TARGET_MODE" ]; then args+=(--with-target-mode "$XT_WITH_TARGET_MODE") fi +if java -jar "$SCRIPT_DIR"/cmdline.jar help decrypt | grep -q -- '--verbose'; then + args+=(--verbose) +fi + echo java -jar "$SCRIPT_DIR"/cmdline.jar "${args[@]}" --file="$2" ">" "$3" java -jar "$SCRIPT_DIR"/cmdline.jar "${args[@]}" --file="$2" >"$3" From 154f9eafb8b4625fab822b330789b1c08ade236e Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 17:36:42 -0400 Subject: [PATCH 09/22] fix(java-cli): check root help for --verbose (it is ScopeType.INHERIT on the parent command) Signed-off-by: Dave Mihalcik --- xtest/sdk/java/cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index 6e281197..da47de40 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -197,7 +197,7 @@ if [ -n "$XT_WITH_TARGET_MODE" ]; then args+=(--with-target-mode "$XT_WITH_TARGET_MODE") fi -if java -jar "$SCRIPT_DIR"/cmdline.jar help decrypt | grep -q -- '--verbose'; then +if java -jar "$SCRIPT_DIR"/cmdline.jar help | grep -q -- '--verbose'; then args+=(--verbose) fi From 140ac4a547672acc9968df538dd6e7d1c27bd96e Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 16 Jun 2026 20:40:42 -0400 Subject: [PATCH 10/22] fix(ci): update platform action SHA pins to 70cb173a (fix DPoP htm validation) Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 0fbce5ea..63782ead 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-up-with-containers@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: alpha @@ -604,7 +604,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: beta @@ -616,7 +616,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: gamma @@ -628,7 +628,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: ec-tdf-enabled: true kas-port: 8484 @@ -640,7 +640,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -653,7 +653,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@24d7101c094baff0c2ac31b852f929786942fded # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge with: ec-tdf-enabled: true kas-name: km2 From a44acfeb80db0c9de42f90a19c29edac8b88efee Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 18 Jun 2026 10:22:16 -0400 Subject: [PATCH 11/22] Cache java cli.sh help probes to cut JVM startup overhead Running 'java -jar cmdline.jar help' on every encrypt/decrypt paid 150-500ms of JVM startup per probe (kas-allowlist and --verbose checks). Add a jar_help() helper that caches help output to a tmpfile keyed by the jar's mtime, and discards stderr to keep JVM warnings out of test logs. Signed-off-by: Dave Mihalcik --- xtest/sdk/java/cli.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index da47de40..a9a25f10 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -42,6 +42,25 @@ else exit 1 fi +# Cache `java -jar cmdline.jar help [...]` output to avoid paying JVM startup +# (typically 150-500ms) for the capability probes on every encrypt/decrypt. +# Keyed by the jar's mtime so a reinstall invalidates the cache. stderr is +# discarded to keep JVM warnings (reflective-access, agent notices) out of logs. +jar_help() { + local jar="$SCRIPT_DIR/cmdline.jar" + local mtime + mtime=$(stat -c %Y "$jar" 2>/dev/null || stat -f %m "$jar" 2>/dev/null || echo 0) + local key + key=$(printf '%s' "$*" | tr -c 'a-zA-Z0-9' '_') + local uid + uid=$(id -u 2>/dev/null || echo default) + local cache="${TMPDIR:-/tmp}/xtest-java-help-${uid}-${mtime}-${key}" + if [ ! -f "$cache" ]; then + java -jar "$jar" help "$@" >"$cache" 2>/dev/null + fi + cat "$cache" +} + if [ "$1" == "supports" ]; then case "$2" in autoconfigure | ns_grants) @@ -135,7 +154,7 @@ args=( ) # when we added support for KAS allowlist, we changed the platform endpoint format to require scheme -if java -jar "$SCRIPT_DIR"/cmdline.jar help decrypt | grep kas-allowlist; then +if jar_help decrypt | grep -q kas-allowlist; then args+=("--platform-endpoint=$PLATFORMURL") else args+=("--platform-endpoint=$PLATFORMENDPOINT") @@ -197,7 +216,7 @@ if [ -n "$XT_WITH_TARGET_MODE" ]; then args+=(--with-target-mode "$XT_WITH_TARGET_MODE") fi -if java -jar "$SCRIPT_DIR"/cmdline.jar help | grep -q -- '--verbose'; then +if jar_help | grep -q -- '--verbose'; then args+=(--verbose) fi From 4d9e3f7bd89ced80cd58b60e6d05df142312f937 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 18 Jun 2026 12:37:20 -0400 Subject: [PATCH 12/22] Address PR review: atomic cache write, [[ ]] tests, redact auth secret - java cli.sh: write jar_help cache to a temp file then mv, so concurrent xdist workers never read a partially written cache (gemini/coderabbit). - java/js cli.sh: use [[ ]] for the new conditionals (SonarCloud SC2292). - js cli.sh: mask the --auth secret in echoed commands so CI logs don't capture client credentials (coderabbit). Signed-off-by: Dave Mihalcik --- xtest/sdk/java/cli.sh | 8 ++++++-- xtest/sdk/js/cli.sh | 26 ++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index a9a25f10..d8013a08 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -55,8 +55,12 @@ jar_help() { local uid uid=$(id -u 2>/dev/null || echo default) local cache="${TMPDIR:-/tmp}/xtest-java-help-${uid}-${mtime}-${key}" - if [ ! -f "$cache" ]; then - java -jar "$jar" help "$@" >"$cache" 2>/dev/null + if [[ ! -f "$cache" ]]; then + # Write to a process-unique temp file, then rename: concurrent xdist + # workers see either no cache or the complete file, never a partial read. + local tmp="${cache}.$$" + java -jar "$jar" help "$@" >"$tmp" 2>/dev/null + mv -f "$tmp" "$cache" fi cat "$cache" } diff --git a/xtest/sdk/js/cli.sh b/xtest/sdk/js/cli.sh index 163d8af4..11c1dfe1 100755 --- a/xtest/sdk/js/cli.sh +++ b/xtest/sdk/js/cli.sh @@ -136,8 +136,8 @@ _pre_clientsecret="${CLIENTSECRET:-}" source "$XTEST_DIR"/test.env # Restore caller overrides (e.g. from pytest monkeypatch for DPoP client). -[ -n "$_pre_clientid" ] && CLIENTID="$_pre_clientid" -[ -n "$_pre_clientsecret" ] && CLIENTSECRET="$_pre_clientsecret" +[[ -n "$_pre_clientid" ]] && CLIENTID="$_pre_clientid" +[[ -n "$_pre_clientsecret" ]] && CLIENTSECRET="$_pre_clientsecret" src_file=$(realpath "$2") dst_file=$(realpath "$(dirname "$3")")/$(basename "$3") @@ -204,6 +204,24 @@ if ! cd "$SCRIPT_DIR"; then exit 1 fi +# Echo a CLI invocation with the --auth secret masked, so CI logs never capture +# client credentials. The real (unmasked) args are still used for execution. +echo_redacted() { + local out=() a mask_next=0 + for a in "$@"; do + if [[ "$mask_next" == 1 ]]; then + out+=("${a%%:*}:***") + mask_next=0 + elif [[ "$a" == "--auth" ]]; then + out+=("$a") + mask_next=1 + else + out+=("$a") + fi + done + echo "${out[@]}" +} + if [ "$1" == "encrypt" ]; then if npx $CTL help | grep autoconfigure; then args+=(--policyEndpoint "$PLATFORMURL" --autoconfigure true) @@ -224,7 +242,7 @@ if [ "$1" == "encrypt" ]; then args+=(--tdfSpecVersion "$XT_WITH_TARGET_MODE") fi - echo npx $CTL encrypt "$src_file" "${args[@]}" + echo_redacted npx $CTL encrypt "$src_file" "${args[@]}" npx $CTL encrypt "$src_file" "${args[@]}" elif [ "$1" == "decrypt" ]; then if [ "$XT_WITH_VERIFY_ASSERTIONS" == 'false' ]; then @@ -246,7 +264,7 @@ elif [ "$1" == "decrypt" ]; then args+=(--ignoreAllowList) fi - echo npx $CTL decrypt "$src_file" "${args[@]}" + echo_redacted npx $CTL decrypt "$src_file" "${args[@]}" npx $CTL decrypt "$src_file" "${args[@]}" else echo "Incorrect argument provided" From 9dd5f37bf06bf935e336c4bd7368fe4e5efabb63 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 24 Jun 2026 10:01:33 -0400 Subject: [PATCH 13/22] fix(ci): remove duplicate OTDFCTL_HEADS env key in xtest.yml Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 63782ead..064fa4cb 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -687,7 +687,6 @@ jobs: KAS_DELTA_LOG_FILE: "../../${{ steps.kas-delta.outputs.log-file }}" KAS_KM1_LOG_FILE: "../../${{ steps.kas-km1.outputs.log-file }}" KAS_KM2_LOG_FILE: "../../${{ steps.kas-km2.outputs.log-file }}" - OTDFCTL_HEADS: ${{ steps.configure-go.outputs.heads }} - name: Sanitize sdk-version for artifact name id: artifact-name From 14d2361ce8dd8580c53bd9db0b7bb5cfa7a0a9d9 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 24 Jun 2026 17:39:33 -0400 Subject: [PATCH 14/22] ci(dpop): enforce require_nonce on additional KAS instances Pass dpop-challenge-enabled:true to the alpha/beta/gamma/delta/km1/km2 start-additional-kas steps and re-pin that action to the go-branch commit (2305c4ab) that adds the input. Fixes test_dpop_rejects_tampered_nonce, which targets the alpha KAS that previously never set require_nonce. Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 064fa4cb..6be843db 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: alpha @@ -600,11 +600,12 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} + dpop-challenge-enabled: true - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: beta @@ -612,11 +613,12 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} + dpop-challenge-enabled: true - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: gamma @@ -624,11 +626,12 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} + dpop-challenge-enabled: true - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-port: 8484 @@ -636,11 +639,12 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} + dpop-challenge-enabled: true - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -649,11 +653,12 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} + dpop-challenge-enabled: true - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: km2 @@ -662,6 +667,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} + dpop-challenge-enabled: true - name: Run attribute based configuration tests if: ${{ steps.multikas.outputs.supported == 'true' }} From a5b5508d29bbc27ce7c92c539e2aac1ec7b541cd Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 25 Jun 2026 06:28:22 -0400 Subject: [PATCH 15/22] ci(dpop): repin platform test actions to 4a19b297; make bearer test nonce-agnostic - Repin start-up-with-containers and all start-additional-kas steps to opentdf/platform DSPX-3397-platform-go-sdk @4a19b297 (adds dpop enforce alongside require_nonce on additional KAS). - test_dpop_bearer_scheme: route both rewrap calls through a new _post_rewrap_with_nonce_retry helper so the test passes whether or not the target KAS enforces require_nonce (satisfies the use_dpop_nonce challenge before asserting lenient 200 + WARN). Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 14 +++++----- xtest/test_dpop.py | 53 ++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 6be843db..32ebcc77 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@70cb173a4858f80ac9e8ac9b4949ceac6ac72b4b # dpop-nonce-challenge + uses: opentdf/platform/test/start-up-with-containers@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: alpha @@ -605,7 +605,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: beta @@ -618,7 +618,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: gamma @@ -631,7 +631,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-port: 8484 @@ -644,7 +644,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -658,7 +658,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@2305c4ab986b49774b5b4f50f19dce6e30c8bec4 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: km2 diff --git a/xtest/test_dpop.py b/xtest/test_dpop.py index beacb737..18844774 100644 --- a/xtest/test_dpop.py +++ b/xtest/test_dpop.py @@ -305,6 +305,37 @@ def _post_rewrap( ) +def _post_rewrap_with_nonce_retry( + call: RewrapCall, + key: DPoPKey, + *, + access_token: str, + auth_scheme: str = "DPoP", +) -> requests.Response: + """POST a rewrap, transparently satisfying a `require_nonce` challenge. + + Mints a fresh DPoP proof (no nonce) and sends it. If the KAS replies with a + `401` carrying a `DPoP-Nonce` header (the `use_dpop_nonce` challenge), the + proof is re-minted with that nonce and the request is retried once, keeping + the same `auth_scheme`. This makes callers agnostic to whether + `require_nonce` is enabled on the target KAS: with it off the first request + is returned as-is; with it on the second (nonce-bearing) request is. + """ + proof = key.sign_dpop_proof(htm="POST", htu=call.url, access_token=access_token) + response = _post_rewrap( + call, access_token=access_token, dpop_proof=proof, auth_scheme=auth_scheme + ) + nonce = response.headers.get("DPoP-Nonce") + if response.status_code == 401 and nonce: + proof = key.sign_dpop_proof( + htm="POST", htu=call.url, access_token=access_token, nonce=nonce + ) + response = _post_rewrap( + call, access_token=access_token, dpop_proof=proof, auth_scheme=auth_scheme + ) + return response + + def _assert_unauthorized(response: requests.Response) -> None: assert response.status_code == 401, response.text # Confirm the rejection is actually a DPoP-related challenge so a 401 @@ -428,16 +459,14 @@ def test_dpop_bearer_scheme_warns_but_accepted_for_dpop_token( dpop_access = _get_dpop_access_token() rewrap_call = _signed_rewrap_request(ct_file, dpop_access.key) - bearer_proof = dpop_access.key.sign_dpop_proof( - htm="POST", - htu=rewrap_call.url, - access_token=dpop_access.token, - ) + # Both calls go through the nonce-retry helper so the test passes whether or + # not the target KAS has `require_nonce` enabled: when it is, the lenient + # accept (and the WARN) only happen after the nonce challenge is satisfied. mark = audit_logs.mark("before_bearer_scheme_request") - bearer_response = _post_rewrap( + bearer_response = _post_rewrap_with_nonce_retry( rewrap_call, + dpop_access.key, access_token=dpop_access.token, - dpop_proof=bearer_proof, auth_scheme="Bearer", ) @@ -449,16 +478,10 @@ def test_dpop_bearer_scheme_warns_but_accepted_for_dpop_token( ) # Compliant path control: same proof key, same token, just the right scheme. - # Distinct jti via fresh proof so the server's replay cache doesn't reject it. - dpop_proof = dpop_access.key.sign_dpop_proof( - htm="POST", - htu=rewrap_call.url, - access_token=dpop_access.token, - ) - dpop_response = _post_rewrap( + dpop_response = _post_rewrap_with_nonce_retry( rewrap_call, + dpop_access.key, access_token=dpop_access.token, - dpop_proof=dpop_proof, auth_scheme="DPoP", ) assert dpop_response.status_code == 200, dpop_response.text From 424aed90abf27840d6ec4160c743fd3fa6f8c671 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 25 Jun 2026 07:39:01 -0400 Subject: [PATCH 16/22] ci(dpop): repin platform test actions to 8ccc608d (enforce now opt-in) Platform action split DPoP enforcement into a separate dpop-enforce-required input (default false); xtest only passes dpop-challenge-enabled, so require_nonce stays on and global enforce stays off, restoring the non-DPoP suite. Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 32ebcc77..1f5b484b 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk + uses: opentdf/platform/test/start-up-with-containers@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: alpha @@ -605,7 +605,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: beta @@ -618,7 +618,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: gamma @@ -631,7 +631,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-port: 8484 @@ -644,7 +644,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -658,7 +658,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@4a19b297a8962e4c58d542929fa9a3d0c9824e08 # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: km2 From 7bd3ad1693ef651d3bcd6f0ed09ef5f899fddf53 Mon Sep 17 00:00:00 2001 From: dmihalcik Date: Fri, 26 Jun 2026 10:32:35 -0400 Subject: [PATCH 17/22] test(dpop): entitle opentdf-dpop client in shared subject condition set (DSPX-3397) The DPoP tests authenticate as the opentdf-dpop client, but otdf_client_scs only entitled opentdf/opentdf-sdk, so the DPoP rewrap reached authorization and was denied (entitled:false -> 403). Add opentdf-dpop to the clientId allowlist so the DPoP-bound client is entitled like the other test clients. Signed-off-by: Dave Mihalcik --- xtest/fixtures/obligations.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xtest/fixtures/obligations.py b/xtest/fixtures/obligations.py index 953aee05..29a69cbd 100644 --- a/xtest/fixtures/obligations.py +++ b/xtest/fixtures/obligations.py @@ -17,7 +17,8 @@ def otdf_client_scs(otdfctl: OpentdfCommandLineTool) -> abac.SubjectConditionSet: """ Creates a standard subject condition set for OpenTDF clients. - This condition set matches client IDs 'opentdf' or 'opentdf-sdk'. + This condition set matches client IDs 'opentdf', 'opentdf-sdk', or + 'opentdf-dpop' (the DPoP-bound client used by the DPoP tests). Returns: abac.SubjectConditionSet: The created subject condition set @@ -32,7 +33,11 @@ def otdf_client_scs(otdfctl: OpentdfCommandLineTool) -> abac.SubjectConditionSet abac.Condition( subject_external_selector_value=".clientId", operator=abac.SubjectMappingOperatorEnum.IN, - subject_external_values=["opentdf", "opentdf-sdk"], + subject_external_values=[ + "opentdf", + "opentdf-sdk", + "opentdf-dpop", + ], ) ], ) From 6a2ecd66d3a5a949f8f50c0444c857b267aeaea5 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 30 Jun 2026 12:08:01 -0400 Subject: [PATCH 18/22] chore: repin platform actions to main (0612ea89) --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 1f5b484b..022e6e9b 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -297,7 +297,7 @@ jobs: ######## SPIN UP PLATFORM BACKEND ############# - name: Check out and start up platform with deps/containers id: run-platform - uses: opentdf/platform/test/start-up-with-containers@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk + uses: opentdf/platform/test/start-up-with-containers@0612ea897264e3c621ce076029a5e1e3f2d3971e # main with: platform-ref: ${{ fromJSON(needs.resolve-versions.outputs.platform-tag-to-sha)[matrix.platform-tag] }} ec-tdf-enabled: true @@ -592,7 +592,7 @@ jobs: - name: Start additional kas id: kas-alpha if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@0612ea897264e3c621ce076029a5e1e3f2d3971e # main: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: alpha @@ -605,7 +605,7 @@ jobs: - name: Start additional kas id: kas-beta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@0612ea897264e3c621ce076029a5e1e3f2d3971e # main: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: beta @@ -618,7 +618,7 @@ jobs: - name: Start additional kas id: kas-gamma if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@0612ea897264e3c621ce076029a5e1e3f2d3971e # main: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: gamma @@ -631,7 +631,7 @@ jobs: - name: Start additional kas id: kas-delta if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@0612ea897264e3c621ce076029a5e1e3f2d3971e # main: require_nonce on additional KAS with: ec-tdf-enabled: true kas-port: 8484 @@ -644,7 +644,7 @@ jobs: - name: Start additional KM kas (km1) id: kas-km1 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@0612ea897264e3c621ce076029a5e1e3f2d3971e # main: require_nonce on additional KAS with: ec-tdf-enabled: true key-management: ${{ steps.km-check.outputs.supported }} @@ -658,7 +658,7 @@ jobs: - name: Start additional KM kas (km2) id: kas-km2 if: ${{ steps.multikas.outputs.supported == 'true' }} - uses: opentdf/platform/test/start-additional-kas@8ccc608d2947f7f868d63e3741d3e78dc0ee88ce # DSPX-3397-platform-go-sdk: require_nonce on additional KAS + uses: opentdf/platform/test/start-additional-kas@0612ea897264e3c621ce076029a5e1e3f2d3971e # main: require_nonce on additional KAS with: ec-tdf-enabled: true kas-name: km2 From 64e64b037ae4bf69bb155f7145014b859e71e4ab Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 1 Jul 2026 10:03:23 -0400 Subject: [PATCH 19/22] xtest: add dpop-challenge boolean input, default false --- .github/workflows/xtest.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 022e6e9b..49cd4cd7 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -28,6 +28,11 @@ on: type: string default: all description: "SDK to focus on (go, js, java, all)" + dpop-challenge: + required: false + type: boolean + default: false + description: "Enable DPoP nonce challenge on KAS instances" workflow_call: inputs: platform-ref: @@ -50,6 +55,10 @@ on: required: false type: string default: all + dpop-challenge: + required: false + type: boolean + default: false schedule: - cron: "30 6 * * *" # 0630 UTC - cron: "0 5 * * 1,3" # 500 UTC (Monday, Wednesday) @@ -304,7 +313,7 @@ jobs: extra-keys: ${{ steps.load-extra-keys.outputs.EXTRA_KEYS }} log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Install uv uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 @@ -600,7 +609,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Start additional kas id: kas-beta @@ -613,7 +622,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Start additional kas id: kas-gamma @@ -626,7 +635,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Start additional kas id: kas-delta @@ -639,7 +648,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Start additional KM kas (km1) id: kas-km1 @@ -653,7 +662,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Start additional KM kas (km2) id: kas-km2 @@ -667,7 +676,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: true + dpop-challenge-enabled: ${{ inputs.dpop-challenge }} - name: Run attribute based configuration tests if: ${{ steps.multikas.outputs.supported == 'true' }} From 1e00adbb3545206bace98bd3cc1020298c260ee2 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 1 Jul 2026 10:39:26 -0400 Subject: [PATCH 20/22] xtest: fix dpop-challenge empty string on PR trigger --- .github/workflows/xtest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 49cd4cd7..70b35ed6 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -313,7 +313,7 @@ jobs: extra-keys: ${{ steps.load-extra-keys.outputs.EXTRA_KEYS }} log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Install uv uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 @@ -609,7 +609,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Start additional kas id: kas-beta @@ -622,7 +622,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Start additional kas id: kas-gamma @@ -635,7 +635,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Start additional kas id: kas-delta @@ -648,7 +648,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Start additional KM kas (km1) id: kas-km1 @@ -662,7 +662,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Start additional KM kas (km2) id: kas-km2 @@ -676,7 +676,7 @@ jobs: log-type: json pqc-enabled: ${{ steps.pqc-check.outputs.supported == 'true' }} root-key: ${{ steps.km-check.outputs.root_key }} - dpop-challenge-enabled: ${{ inputs.dpop-challenge }} + dpop-challenge-enabled: ${{ inputs.dpop-challenge || false }} - name: Run attribute based configuration tests if: ${{ steps.multikas.outputs.supported == 'true' }} From 7cbcc1327182e3224946494e7194670a60a170dd Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 1 Jul 2026 11:11:11 -0400 Subject: [PATCH 21/22] style(js/cli): use [[ ]] for all conditionals (SonarCloud SC2292) Convert remaining single-bracket [ ] test conditionals to [[ ]] throughout xtest/sdk/js/cli.sh to clear SonarCloud SC2292 findings and match the style already applied to the DPoP credential-restore checks. shellcheck and shfmt pass clean. --- xtest/sdk/js/cli.sh | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/xtest/sdk/js/cli.sh b/xtest/sdk/js/cli.sh index 11c1dfe1..7008b01b 100755 --- a/xtest/sdk/js/cli.sh +++ b/xtest/sdk/js/cli.sh @@ -30,7 +30,7 @@ if grep opentdf/cli "$SCRIPT_DIR/package.json"; then CTL=@opentdf/cli fi -if [ "$1" == "supports" ]; then +if [[ "$1" == "supports" ]]; then if ! cd "$SCRIPT_DIR"; then echo "failed: [cd $SCRIPT_DIR]" exit 1 @@ -116,14 +116,14 @@ if [ "$1" == "supports" ]; then fi XTEST_DIR=$SCRIPT_DIR -while [ "$XTEST_DIR" != "/" ]; do - if [ -f "$XTEST_DIR/pyproject.toml" ] && grep -q 'name = "xtest"' "$XTEST_DIR/pyproject.toml"; then +while [[ "$XTEST_DIR" != "/" ]]; do + if [[ -f "$XTEST_DIR/pyproject.toml" ]] && grep -q 'name = "xtest"' "$XTEST_DIR/pyproject.toml"; then break fi XTEST_DIR=$(dirname "$XTEST_DIR") done -if [ "$XTEST_DIR" = "/" ]; then +if [[ "$XTEST_DIR" = "/" ]]; then echo "xtest root (pyproject.toml with name = \"xtest\") not found." exit 1 fi @@ -149,18 +149,18 @@ args=( --auth "${CLIENTID:-opentdf}:${CLIENTSECRET:-secret}" ) -if [ -n "$XT_WITH_DPOP" ]; then +if [[ -n "$XT_WITH_DPOP" ]]; then args+=(--dpop "$XT_WITH_DPOP") fi -if [ -n "$XT_WITH_DPOP_KEY" ]; then +if [[ -n "$XT_WITH_DPOP_KEY" ]]; then args+=(--dpop-key "$XT_WITH_DPOP_KEY") fi args+=(--containerType tdf3) -if [ -n "$XT_WITH_ATTRIBUTES" ]; then +if [[ -n "$XT_WITH_ATTRIBUTES" ]]; then attributes="$XT_WITH_ATTRIBUTES" - if [ -f "$attributes" ]; then + if [[ -f "$attributes" ]]; then attributes=$(realpath "$attributes") echo "Attributes are a file: $attributes" args+=(--attributes "$attributes") @@ -171,13 +171,13 @@ if [ -n "$XT_WITH_ATTRIBUTES" ]; then fi fi -if [ -n "$XT_WITH_ASSERTIONS" ]; then +if [[ -n "$XT_WITH_ASSERTIONS" ]]; then assertions="$XT_WITH_ASSERTIONS" - if [ -f "$assertions" ]; then + if [[ -f "$assertions" ]]; then assertions=$(realpath "$assertions") echo "Assertions are a file: $assertions" args+=(--assertions "$assertions") - elif [ "$(echo "$assertions" | jq -e . >/dev/null 2>&1 && echo valid || echo invalid)" == "valid" ]; then + elif [[ "$(echo "$assertions" | jq -e . >/dev/null 2>&1 && echo valid || echo invalid)" == "valid" ]]; then # Assertions are plain json echo "Assertions are plain json: $assertions" args+=(--assertions "$assertions") @@ -187,9 +187,9 @@ if [ -n "$XT_WITH_ASSERTIONS" ]; then fi fi -if [ -n "$XT_WITH_ASSERTION_VERIFICATION_KEYS" ]; then +if [[ -n "$XT_WITH_ASSERTION_VERIFICATION_KEYS" ]]; then verification_keys="$XT_WITH_ASSERTION_VERIFICATION_KEYS" - if [ -f "$verification_keys" ]; then + if [[ -f "$verification_keys" ]]; then verification_keys=$(realpath "$verification_keys") echo "Verification keys are a file: $verification_keys" args+=(--assertionVerificationKeys "$verification_keys") @@ -222,39 +222,39 @@ echo_redacted() { echo "${out[@]}" } -if [ "$1" == "encrypt" ]; then +if [[ "$1" == "encrypt" ]]; then if npx $CTL help | grep autoconfigure; then args+=(--policyEndpoint "$PLATFORMURL" --autoconfigure true) fi - if [ -n "$XT_WITH_ECDSA_BINDING" ]; then - if [ "$XT_WITH_ECDSA_BINDING" == "true" ]; then + if [[ -n "$XT_WITH_ECDSA_BINDING" ]]; then + if [[ "$XT_WITH_ECDSA_BINDING" == "true" ]]; then args+=(--policyBinding ecdsa) fi fi - if [ "$XT_WITH_ECWRAP" == 'true' ]; then + if [[ "$XT_WITH_ECWRAP" == 'true' ]]; then args+=(--encapKeyType "ec:secp256r1") fi - if [ "$XT_WITH_PLAINTEXT_POLICY" == "true" ]; then + if [[ "$XT_WITH_PLAINTEXT_POLICY" == "true" ]]; then args+=(--policyType plaintext) fi - if [ -n "$XT_WITH_TARGET_MODE" ]; then + if [[ -n "$XT_WITH_TARGET_MODE" ]]; then args+=(--tdfSpecVersion "$XT_WITH_TARGET_MODE") fi echo_redacted npx $CTL encrypt "$src_file" "${args[@]}" npx $CTL encrypt "$src_file" "${args[@]}" -elif [ "$1" == "decrypt" ]; then - if [ "$XT_WITH_VERIFY_ASSERTIONS" == 'false' ]; then +elif [[ "$1" == "decrypt" ]]; then + if [[ "$XT_WITH_VERIFY_ASSERTIONS" == 'false' ]]; then args+=(--noVerifyAssertions) fi - if [ "$XT_WITH_ECWRAP" == 'true' ]; then + if [[ "$XT_WITH_ECWRAP" == 'true' ]]; then args+=(--rewrapKeyType "ec:secp256r1") fi - if [ -n "$XT_WITH_KAS_ALLOW_LIST" ]; then + if [[ -n "$XT_WITH_KAS_ALLOW_LIST" ]]; then args+=(--allowList "$XT_WITH_KAS_ALLOW_LIST") fi - if [ "$XT_WITH_IGNORE_KAS_ALLOWLIST" == "true" ]; then + if [[ "$XT_WITH_IGNORE_KAS_ALLOWLIST" == "true" ]]; then args+=(--ignoreAllowList) fi # only ignore allowlist if the kas allowlist fetching from kas registry has not been implemented From 984184d6f3788815ee79d1cc612e9208598ebfba Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 1 Jul 2026 11:16:10 -0400 Subject: [PATCH 22/22] style(cli): clear SonarCloud smells in js/java shims - js/cli.sh: add explicit `return 0` to echo_redacted() - js/cli.sh: merge redundant nested ECDSA-binding if (== "true" implies -n) - java/cli.sh: add explicit `return 0` to jar_help() All behavior-preserving. shellcheck and shfmt pass clean. --- xtest/sdk/java/cli.sh | 1 + xtest/sdk/js/cli.sh | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index d8013a08..aeca8ad3 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -63,6 +63,7 @@ jar_help() { mv -f "$tmp" "$cache" fi cat "$cache" + return 0 } if [ "$1" == "supports" ]; then diff --git a/xtest/sdk/js/cli.sh b/xtest/sdk/js/cli.sh index 7008b01b..7e6ded27 100755 --- a/xtest/sdk/js/cli.sh +++ b/xtest/sdk/js/cli.sh @@ -220,16 +220,15 @@ echo_redacted() { fi done echo "${out[@]}" + return 0 } if [[ "$1" == "encrypt" ]]; then if npx $CTL help | grep autoconfigure; then args+=(--policyEndpoint "$PLATFORMURL" --autoconfigure true) fi - if [[ -n "$XT_WITH_ECDSA_BINDING" ]]; then - if [[ "$XT_WITH_ECDSA_BINDING" == "true" ]]; then - args+=(--policyBinding ecdsa) - fi + if [[ "$XT_WITH_ECDSA_BINDING" == "true" ]]; then + args+=(--policyBinding ecdsa) fi if [[ "$XT_WITH_ECWRAP" == 'true' ]]; then args+=(--encapKeyType "ec:secp256r1")