Skip to content

test(test-utils): regenerate attestation fixtures with a retained signer key - #4104

Merged
pbeza merged 0 commit into
3787-sandbox-verified-attestationfrom
3787-regenerate-attestation-assets
Aug 10, 2026
Merged

test(test-utils): regenerate attestation fixtures with a retained signer key#4104
pbeza merged 0 commit into
3787-sandbox-verified-attestationfrom
3787-regenerate-attestation-assets

Conversation

@pbeza

@pbeza pbeza commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Closes #3787

Copilot AI lite review requested due to automatic review settings August 7, 2026 15:55
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR title type suggestion: This PR modifies source code files in addition to test files and fixtures, so the type prefix should probably be chore: instead of test:. The test type is reserved for test-only changes.

Suggested title: chore(test-utils): regenerate attestation fixtures with a retained signer key

Copilot AI 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.

Pull request overview

This PR refreshes the Dstack/TDX attestation fixtures and adjusts the sandbox testing/build plumbing so CI can exercise the full “Verified → store attestation” path end-to-end (including signing submit_participant_info with the fixture node key), while keeping production attestation verification strict by default.

Changes:

  • Regenerates test-utils attestation assets (quote/collateral/tcb_info/app-compose, keys) and updates VALID_ATTESTATION_TIMESTAMP.
  • Introduces an opt-in allow-pre-launch-script attestation feature and wires it through to a dedicated sandbox WASM feature (sandbox-test-attestation) used only by fixture-verifying tests.
  • Updates localnet rust-launcher scripts/READMEs to support injecting an in-guest prelaunch hook for exporting the in-enclave signer key during fixture collection, and un-ignores the Verified-path sandbox tests accordingly.

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
localnet/tee/scripts/rust-launcher/single-node.sh Adds PRELAUNCH_SCRIPT env var passthrough for fixture collection.
localnet/tee/scripts/rust-launcher/single-node-readme.md Documents PRELAUNCH_SCRIPT usage for single-node localnet runs.
localnet/tee/scripts/rust-launcher/README.md Adds detailed fixture-collection guidance and an example export hook.
deployment/cvm-deployment/deploy-launcher.sh Threads an optional --prelaunch-script into app-compose generation.
crates/test-utils/src/attestation.rs Updates fixture timestamp and switches fixture secret key loading to include_str!.
crates/test-utils/assets/tcb_info.json Updates fixture TCB info (incl. app-compose and measurements).
crates/test-utils/assets/README.md Documents that the fixture is test-only (contains pre-launch script + secret key).
crates/test-utils/assets/quote.json Updates the serialized quote fixture.
crates/test-utils/assets/near_p2p_public_key.pub Updates the fixture P2P public key.
crates/test-utils/assets/near_account_secret_key Adds the fixture NEAR signer secret key for sandbox tests.
crates/test-utils/assets/near_account_public_key.pub Updates the fixture NEAR signer public key.
crates/test-utils/assets/mpc_image_digest.txt Updates the fixture MPC image digest.
crates/test-utils/assets/launcher_image_compose.yaml Updates pinned launcher image + default image digest.
crates/test-utils/assets/create-assets.sh Fixes collateral extraction to hex-encode DER/signature fields.
crates/test-utils/assets/collateral.json Updates collateral fixture (now includes pck_certificate_chain).
crates/test-utils/assets/app_compose.json Updates app-compose fixture (now includes pre_launch_script).
crates/tee-verifier/tests/verify_quote.rs Updates expected report fields to match the regenerated fixture.
crates/mpc-attestation/Cargo.toml Forwards attestation/allow-pre-launch-script as a feature.
crates/contract/tests/sandbox/utils/contract_build.rs Adds a dedicated build artifact for sandbox-test-attestation.
crates/contract/tests/sandbox/tee_verifier.rs Un-ignores Verified-path tests and uses the fixture-accepting sandbox WASM.
crates/contract/tests/sandbox/common.rs Extends sandbox setup builder to select the attestation-relaxed sandbox WASM.
crates/contract/Cargo.toml Adds sandbox-test-attestation feature (test-only attestation relaxation).
crates/attestation/src/attestation.rs Refactors script-field validation and gates pre_launch_script by feature.
crates/attestation/Cargo.toml Adds allow-pre-launch-script feature and wires it into test-utils.
crates/attestation/assets/tcb_info.json Updates the attestation crate’s deserialization fixture in sync with test-utils.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +48 to +53
# Script executed inside the CVM before docker compose up, and the only way to
# run anything of your own in there. Needed when collecting test assets, to
# export the node's in-enclave signer key; the dev image has no sshd, and the
# guest agent serves only node info and container logs. See
# ../../../../localnet/tee/scripts/rust-launcher/README.md for a working script.
export PRELAUNCH_SCRIPT=/path/to/prelaunch.sh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Simplified to README.md in this directory. For the record the old path did resolve (four levels up from localnet/tee/scripts/rust-launcher/ is the repo root), it was just a needlessly long way to point at a sibling file.

Comment on lines +43 to +52
jq -r 'def tohex:
if type == "array" then
reduce .[] as $b (""; . + ("0123456789abcdef" | .[(($b / 16) | floor):(($b / 16) | floor) + 1])
+ ("0123456789abcdef" | .[($b % 16):($b % 16) + 1]))
else . end;
.tee_participant_info.Dstack.collateral
| .root_ca_crl |= tohex
| .pck_crl |= tohex
| .tcb_info_signature |= tohex
| .qe_identity_signature |= tohex' "$INPUT_FILE" > "$OUTPUT_DIR/collateral.json"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, and newly introduced — the previous fixture had no pck_certificate_chain at all. Extraction now strips the trailing NUL from all four PEM fields, and the borsh arg fixture is regenerated. Side effect worth knowing: with the NUL gone the blob's last byte is the PEM newline, which tripped editorconfig-checker, so .editorconfig no longer asserts that file's final byte either way.

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown

Pull request overview

Regenerates the TDX attestation fixtures from a fresh localnet CVM, this time retaining the node's in-enclave NEAR signer key so sandbox tests can sign submit_participant_info as the fixture node and exercise the Verified store path. Exporting that key requires an app-compose pre_launch_script, which production verification rejects as arbitrary root code, so a new attestation/allow-pre-launch-script feature (forwarded through mpc-attestation and mpc-contract/sandbox-test-attestation) relaxes exactly that one field for test builds. Three previously-#[ignore]d store-path tests are un-ignored, and one that #3787 asked for is dropped as unachievable. Along the way create-assets.sh is fixed to hex-encode the collateral DER/signature fields our own parser expects.

Changes:

  • New allow-pre-launch-script feature on attestation, forwarded by mpc-attestation and by a new mpc-contract/sandbox-test-attestation; validate_app_compose_config now delegates to scripts_absent/scripts_absent_with so the strict policy stays unit-testable under any feature set.
  • Sandbox harness gains with_sandbox_test_attestation() and a third contract wasm variant (contract-noabi-attestation), with an assertion that the two test feature sets are not combined.
  • Un-ignores should_store_attestation_on_verified_quote and should_reject_verified_quote_when_tls_key_owned_by_other_account; removes the resolve_verification out-of-gas test.
  • Regenerates every fixture (quote, collateral, tcb_info, app_compose, launcher compose, image digest, both key files) plus the tee-verifier borsh arg fixture and its hardcoded expected report; bumps VALID_ATTESTATION_TIMESTAMP.
  • Adds the fixture NEAR secret key asset and switches account_secret_key() from a runtime read to include_str!.
  • create-assets.sh hex-encodes root_ca_crl/pck_crl/tcb_info_signature/qe_identity_signature; deploy-launcher.sh and single-node.sh gain an optional PRELAUNCH_SCRIPT; READMEs document the export hook.

Reviewed changes

Per-file summary
File Description
crates/attestation/Cargo.toml Adds allow-pre-launch-script; makes test-utils imply it
crates/attestation/src/attestation.rs Splits script rejection into scripts_absent/scripts_absent_with; three new policy tests
crates/attestation/assets/tcb_info.json Regenerated to match the new fixture
crates/mpc-attestation/Cargo.toml Forwards allow-pre-launch-script
crates/contract/Cargo.toml New sandbox-test-attestation feature
crates/contract/tests/sandbox/common.rs Builder flag + mutual-exclusion assert for the new wasm variant
crates/contract/tests/sandbox/utils/contract_build.rs Builds/caches the sandbox-test-attestation wasm
crates/contract/tests/sandbox/tee_verifier.rs Un-ignores two store-path tests; drops the out-of-gas test; new setup helper
crates/tee-verifier/tests/verify_quote.rs Updated hardcoded mr_config_id, rt_mr3, report_data
crates/tee-verifier/tests/fixtures/verify_quote_args.borsh Regenerated borsh args
crates/test-utils/src/attestation.rs account_secret_key() becomes include_str!; new VALID_ATTESTATION_TIMESTAMP
crates/test-utils/assets/* Regenerated fixtures; new near_account_secret_key; collateral hex fix in create-assets.sh; README rewrite
deployment/cvm-deployment/deploy-launcher.sh Optional PRELAUNCH_SCRIPT passed as --prelaunch-script
localnet/tee/scripts/rust-launcher/* Passes PRELAUNCH_SCRIPT through; documents the export hook

I verified the feature plumbing does not reach production: test-utils is a dev-dependency at every site, nix/mpc-node.nix builds -p mpc-node --bin mpc-node with doCheck = false, and the contract wasm is built with --features abi. I also confirmed verify_report_data runs before verify_app_compose (crates/attestation/src/attestation.rs:143-147), so should_run_dcap_within_verifier_gas_budget still fails on report_data rather than on the new pre_launch_script when run against the production wasm.

Findings

Blocking (must fix before merge):

  • crates/test-utils/assets/near_account_secret_key:1 — the new file has no final newline, and it is not covered by the .editorconfig:25 exception list (app_compose.json, app_compose_with_services.json, mpc_image_digest.txt, *.pub). [*] insert_final_newline = true therefore applies, and editorconfig-checker runs as part of cargo make check-all-fast (Makefile.toml:7,103). Either add the file to the exception glob (consistent with its .pub siblings) or add a trailing newline — the latter is safe, since account_secret_key() already .trim()s.

  • docs/localnet/localnet.md:346 — doc drift from the regenerated fixture. The doc states the localnet verify_quote call "returns TCBInfo expired: … the fixture's collateral is time-expired against the live block clock". The new collateral has nextUpdate: 2026-09-06T08:49:47Z, so against a live localnet clock today it verifies instead. Reword to say the call returns a Verified report while the collateral is current, and TCBInfo expired once it passes nextUpdate.

  • crates/contract/tests/sandbox/tee_verifier.rs:366 and crates/test-utils/assets/create-assets.sh:73 — both still point at #3787, which this PR closes. Per CLAUDE.md a TODO(#N) must track an open issue. The comment at tee_verifier.rs:366 should just state that the submitter is not the fixture account (the permanent reason that test lands on the report_data error), and the script comment should drop the issue reference.

  • crates/contract/tests/sandbox/tee_verifier.rs:5-9 — the module doc still says the clock pin is needed because "the fixture collateral has expired against" sandbox wall-clock time. That is currently false for ~30 days. The pin is still the right call; restate the reason as "the fixture collateral is only valid inside a fixed window, and sandbox time is wall-clock and forward-only" so it does not need rewriting at every regeneration.

  • deployment/cvm-deployment/deploy-launcher-guide.md:109-162 / deployment/cvm-deployment/default.envPRELAUNCH_SCRIPT is a new operator-visible input to the production deploy script but appears in neither the guide's .env reference nor default.env. It matters more than a normal env var: deploy-launcher.sh:113-117 sources the .env under set -a, so a leftover PRELAUNCH_SCRIPT= line bakes arbitrary root code into a production CVM, and the only signal is echo "Including pre-launch script: $PRELAUNCH_SCRIPT" (deploy-launcher.sh:276). Please document it, and make that message a loud warning:

    echo "WARNING: baking pre-launch script '$PRELAUNCH_SCRIPT' into the app-compose."
    echo "         This CVM will FAIL attestation. Use only for fixture collection."

Non-blocking (nits, follow-ups, suggestions):

  • crates/test-utils/assets/create-assets.sh:74-79 — the missing-key warning is dead in the documented flow: near_account_secret_key is now committed and the script is run as create-assets.sh public_data.json . from inside the assets directory, so the file always exists. A regeneration therefore silently leaves a stale secret key next to a freshly extracted near_account_public_key.pub, and nothing catches it until the assertion at tee_verifier.rs:142. Printing the reminder unconditionally (or diffing the newly written .pub against the previous one) would close that gap.

  • crates/attestation/src/attestation.rs:418-427 — after the split, nothing exercises the pre_launch_script rejection through validate_app_compose_config; the new tests call scripts_absent_with directly, and the wired-in policy is always the relaxed one under cargo test (the crate's dev-dependency self-edge at Cargo.toml:36-40 enables test-utils). A single assertion covers the call site under either feature setting:

    assert_eq!(
        DstackAttestation::validate_app_compose_config(&app_compose),
        cfg!(feature = "allow-pre-launch-script")
    );
  • crates/attestation/Cargo.toml:10test-utils = ["allow-pre-launch-script"] couples a security-policy relaxation to a broad convenience feature. Nothing enables it in a shipped artifact today (verified above), but Cargo features are additive and unify per build invocation, so an accidental future edge would be silent. Enabling allow-pre-launch-script explicitly at the three sites that need it (crates/test-utils/Cargo.toml, and the attestation/mpc-attestation dev-dependency self-edges) would keep every enablement greppable.

  • crates/test-utils/assets/README.md:20-88 — this is the canonical "Updating Test Assets" runbook, but the new UPDATE_FIXTURES=1 cargo test -p tee-verifier … step and the hardcoded report values in verify_quote.rs are only documented in the rust-launcher README. Anyone following this file will leave tee-verifier tests failing.

  • crates/test-utils/assets/README.md:107--run-ignored all is now vestigial; no #[ignore] remains in tee_verifier.rs.

  • crates/attestation/src/attestation.rs:631,646,660 — the three new tests are named scripts_absent_with__rejects_… / …__accepts_…, omitting the should_ segment CLAUDE.md prescribes (<sut>__should_<assertion>). They match their neighbours in the file, so this is only worth fixing if you want the new ones to lead.

⚠️ Issues found

@pbeza

pbeza commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review, and rebased onto the updated #4085. Blocking items:

  • Trailing NUL in pck_certificate_chain — real and newly introduced (the old fixture had no such field). Extraction now strips it from all four PEM fields; collateral and the borsh arg fixture regenerated, verifier tests still verify.
  • Missing final newline on the key file — confirmed it would have failed editorconfig-checker as soon as todo-format stopped aborting the task list first. Added; the accessor already trims.
  • Doc drift: both runbooks and the sandbox module doc claimed the fixture collateral is expired, which stopped being true with this regeneration (nextUpdate: 2026-09-06). They now describe the DCAP verdict without depending on today's date, so the next regeneration does not invalidate them.
  • PRELAUNCH_SCRIPT operator safety — the sharpest catch. Documented in the deploy guide and default.env, and the informational echo is now a warning that the CVM will fail attestation.

Nits taken: unconditional regeneration reminder (the committed key is normally present but stale, which the old existence check could never catch), the borsh/report-value refresh step added to the canonical runbook, vestigial --run-ignored all dropped, the sibling-file link simplified, tests renamed to the should_ form, and a new assertion covering the compiled-in policy through validate_app_compose_config itself rather than only the helper.

Not taken, deliberately: splitting allow-pre-launch-script out of test-utils into per-site enablement. Keeping one place is simpler, production exclusion is verifiable (cargo tree -e features,no-dev --target wasm32-unknown-unknown shows zero references), and the new call-site assertion now fails loudly if the wired-in policy ever changes unintentionally.

Also confirmed the fixture key's provenance rather than asserting it: frodo.test.near returns UNKNOWN_ACCOUNT on both mainnet and testnet RPC, and that check is now written into the runbook for the next regeneration.

Locally: cargo make check-all-fast green, 11/11 sandbox tee_verifier tests, 83 tests across attestation/mpc-attestation/test-utils/tee-verifier, 14 in-process attestation tests. The earlier E2E failure was foreign_chain_configuration exhausting a 240s poll — unrelated to attestation and green on #4085 with identical wiring — so I am re-running it.

@pbeza
pbeza force-pushed the 3787-regenerate-attestation-assets branch 2 times, most recently from 213a577 to 79463d1 Compare August 10, 2026 10:54
@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

This PR includes source code changes and dependency updates beyond test files, so the type should probably be chore: instead of test:.

Suggested title: chore(test-utils): regenerate attestation fixtures with a retained signer key

@pbeza
pbeza force-pushed the 3787-regenerate-attestation-assets branch from 79463d1 to 64beae3 Compare August 10, 2026 11:42
@pbeza
pbeza merged commit 64beae3 into main Aug 10, 2026
@pbeza
pbeza deleted the 3787-regenerate-attestation-assets branch August 10, 2026 11:42
@pbeza

pbeza commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

This PR is marked merged, but nothing from it landed on main — please disregard the merged state.

What happened: its base was 3787-sandbox-verified-attestation, and while re-slicing the stack I force-pushed that branch with this PR's commits now inside it. That made this PR's head an ancestor of its base, so GitHub closed it as merged. The base branch was never main, so no code reached main, and GitHub does not allow reopening a merged PR.

The work continues in #4109 (fixtures, targeting main) and #4110 (cross-contract sandbox tests, stacked on it). Review comments here are still worth reading; every point raised was addressed, and the code moved to those two PRs.

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.

test(contract): exercise the Verified attestation store end-to-end in sandbox (fixture account key + measurements helper)

2 participants