From 76ea0f36cbeb7243eb53e6d2a64afaf14b4987eb Mon Sep 17 00:00:00 2001 From: David Baker Effendi Date: Tue, 29 Sep 2026 17:36:33 +0200 Subject: [PATCH 1/2] Capture v0.9 release controls in isolated execution roots --- .github/workflows/ci.yml | 5 + docs/v0.9.0-control-inventory.md | 32 +- .../v0.9.0/execution-v1/contract.json | 177 +- .../control-integration-validation.json | 142 ++ .../execution-v1/control-inventory.json | 495 +++-- .../opentaint-full-restoration.json | 21 + .../v0.9.0/execution-v1/runner-build.json | 77 + .../runtime-trees/opentaint-full.json | 1611 +++++++++++++++++ scripts/check-v090-execution-contract.py | 17 +- scripts/execute-release-controls-v090.py | 69 + scripts/prepare-release-root-v090.py | 171 +- scripts/probe-opentaint-product-v090.py | 624 +++++++ ...probe-python-modeling-load-bearing-v090.py | 577 ++++++ scripts/probe-warm-observability-v090.py | 83 +- scripts/release_control_attempt_v090.py | 508 ++++++ scripts/run-release-control-v090.py | 50 + scripts/run-release-group-v090.py | 17 +- scripts/test-execute-release-controls-v090.py | 42 + scripts/test-prepare-release-root-v090.py | 112 ++ scripts/test-probe-opentaint-product-v090.py | 260 +++ ...probe-python-modeling-load-bearing-v090.py | 262 +++ scripts/test-probe-warm-observability-v090.py | 21 + scripts/test-release-control-attempt-v090.py | 200 ++ scripts/test-run-release-control-v090.py | 38 + scripts/test-run-release-group-v090.py | 24 +- scripts/test-v090-execution-contract.py | 16 +- 26 files changed, 5483 insertions(+), 168 deletions(-) create mode 100644 reports/releases/v0.9.0/execution-v1/control-integration-validation.json create mode 100644 reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json create mode 100644 reports/releases/v0.9.0/execution-v1/runner-build.json create mode 100644 reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json create mode 100644 scripts/execute-release-controls-v090.py create mode 100644 scripts/probe-opentaint-product-v090.py create mode 100644 scripts/probe-python-modeling-load-bearing-v090.py create mode 100644 scripts/release_control_attempt_v090.py create mode 100644 scripts/run-release-control-v090.py create mode 100644 scripts/test-execute-release-controls-v090.py create mode 100644 scripts/test-probe-opentaint-product-v090.py create mode 100644 scripts/test-probe-python-modeling-load-bearing-v090.py create mode 100644 scripts/test-release-control-attempt-v090.py create mode 100644 scripts/test-run-release-control-v090.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9510f411a..1921f152c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,11 @@ jobs: python3 scripts/test-verify-release-attempt-v090.py python3 scripts/test-release-runtime-inventory-v090.py python3 scripts/test-probe-warm-observability-v090.py + python3 scripts/test-probe-python-modeling-load-bearing-v090.py + python3 scripts/test-probe-opentaint-product-v090.py + python3 scripts/test-release-control-attempt-v090.py + python3 scripts/test-run-release-control-v090.py + python3 scripts/test-execute-release-controls-v090.py python3 scripts/test-swift-common-v2.py - run: cargo fmt --check - run: cargo test diff --git a/docs/v0.9.0-control-inventory.md b/docs/v0.9.0-control-inventory.md index d40c77aba..b63504a16 100644 --- a/docs/v0.9.0-control-inventory.md +++ b/docs/v0.9.0-control-inventory.md @@ -7,9 +7,29 @@ runtime environment and deadlines derived from the retained run durations. These controls are not included in the 84 correctness groups or the 92 normalized report partitions. -Control execution remains disabled. The next prospective plan must finish -non-report capture integration and resolve the Python modeling supplemental -command inventory before final parent review. The warm-observability script -records advertised help surfaces only; it does not establish performance or -infer unsupported behavior from an absent option. Existing Swift query/control -compatibility is independently bound by the common-population plans. +Three supplementary controls retain the prior release's Python modeling, +warm-observability and shipped OpenTaint product scopes. The full OpenTaint +0.4.6 Darwin arm64 archive and every one of its 329 files have been restored +against the historical hashes. The restoration receipt records the 2 GiB +acquisition/extraction budget; the actual archive and extracted files total +approximately 327 MiB. Restoration is not an analyzer result. + +Each control receives an isolated checkout because the historical Java and +FlowDroid probes share an output path. The prospective storage budget includes +33 control checkouts (23.68 GiB of tracked files) plus the original 12 GiB of +matrix and staging space, rounded to 36 GiB. The launch floor is therefore +124 GiB: 40 GiB protected reserve, 24 GiB scratch, 24 GiB retained output, +and 36 GiB checkout/staging allowance. All operations share the analyzer lock. + +Warm commands perform two measurement repeats internally, and overhead +commands perform three. These are method repetitions, not retries; the outer +runner must invoke each command once and retain every repeat. Infrastructure +attempts remain separately bounded at two and are never selected automatically. + +Control execution remains disabled pending integration validation, the final +merged harness identity, an exclusive resource reservation and parent plan +review. Capturing a successful command does not establish that a model engaged +or that a tool is equivalent to another invocation surface. The help audit +does not establish performance or infer unsupported behavior from an absent +option. Existing Swift query/control compatibility remains independently bound +by the common-population plans. Historical runners and evidence are unchanged. diff --git a/reports/releases/v0.9.0/execution-v1/contract.json b/reports/releases/v0.9.0/execution-v1/contract.json index 5073d7225..af98d26a9 100644 --- a/reports/releases/v0.9.0/execution-v1/contract.json +++ b/reports/releases/v0.9.0/execution-v1/contract.json @@ -3,9 +3,44 @@ "path": "reports/releases/v0.9.0/execution-v1/codeql-compatibility/summary.json", "sha256": "7bc68a9bc11994525c8dd536fc9c419c795003228ee285e288ea3bc7e9dd6adc" }, + "control_execution_roots": { + "overhead-bifrost-python": "/private/tmp/dfb-v090-controls-01/overhead-bifrost-python", + "overhead-codeql-ruby": "/private/tmp/dfb-v090-controls-01/overhead-codeql-ruby", + "overhead-flowdroid-java": "/private/tmp/dfb-v090-controls-01/overhead-flowdroid-java", + "overhead-infer-c": "/private/tmp/dfb-v090-controls-01/overhead-infer-c", + "overhead-joern-java": "/private/tmp/dfb-v090-controls-01/overhead-joern-java", + "overhead-joern-php": "/private/tmp/dfb-v090-controls-01/overhead-joern-php", + "overhead-opentaint-kotlin": "/private/tmp/dfb-v090-controls-01/overhead-opentaint-kotlin", + "overhead-pysa-python": "/private/tmp/dfb-v090-controls-01/overhead-pysa-python", + "overhead-semgrep-kotlin": "/private/tmp/dfb-v090-controls-01/overhead-semgrep-kotlin", + "probe-bifrost-sanitizer-lowering": "/private/tmp/dfb-v090-controls-01/probe-bifrost-sanitizer-lowering", + "probe-bifrost-scan-native": "/private/tmp/dfb-v090-controls-01/probe-bifrost-scan-native", + "probe-flowdroid-modeling-load-bearing": "/private/tmp/dfb-v090-controls-01/probe-flowdroid-modeling-load-bearing", + "probe-flowdroid-native-shipped-surface": "/private/tmp/dfb-v090-controls-01/probe-flowdroid-native-shipped-surface", + "probe-infer-modeling-partition": "/private/tmp/dfb-v090-controls-01/probe-infer-modeling-partition", + "probe-infer-native-activation": "/private/tmp/dfb-v090-controls-01/probe-infer-native-activation", + "probe-infer-native-silence": "/private/tmp/dfb-v090-controls-01/probe-infer-native-silence", + "probe-java-modeling-load-bearing": "/private/tmp/dfb-v090-controls-01/probe-java-modeling-load-bearing", + "probe-javascript-modeling-load-bearing": "/private/tmp/dfb-v090-controls-01/probe-javascript-modeling-load-bearing", + "probe-joern-scan-native": "/private/tmp/dfb-v090-controls-01/probe-joern-scan-native", + "probe-opentaint-modeling-surface": "/private/tmp/dfb-v090-controls-01/probe-opentaint-modeling-surface", + "probe-opentaint-native-activation": "/private/tmp/dfb-v090-controls-01/probe-opentaint-native-activation", + "probe-opentaint-primitive-tracking": "/private/tmp/dfb-v090-controls-01/probe-opentaint-primitive-tracking", + "probe-opentaint-product-v090": "/private/tmp/dfb-v090-controls-01/probe-opentaint-product-v090", + "probe-opentaint-scan-activation": "/private/tmp/dfb-v090-controls-01/probe-opentaint-scan-activation", + "probe-opentaint-value-kind": "/private/tmp/dfb-v090-controls-01/probe-opentaint-value-kind", + "probe-pysa-callee-resolution": "/private/tmp/dfb-v090-controls-01/probe-pysa-callee-resolution", + "probe-pysa-modeling-load-bearing": "/private/tmp/dfb-v090-controls-01/probe-pysa-modeling-load-bearing", + "probe-pysa-native-activation": "/private/tmp/dfb-v090-controls-01/probe-pysa-native-activation", + "probe-python-modeling-load-bearing": "/private/tmp/dfb-v090-controls-01/probe-python-modeling-load-bearing", + "probe-semgrep-jsjava-native": "/private/tmp/dfb-v090-controls-01/probe-semgrep-jsjava-native", + "probe-warm-observability": "/private/tmp/dfb-v090-controls-01/probe-warm-observability", + "warm-joern-java": "/private/tmp/dfb-v090-controls-01/warm-joern-java", + "warm-semgrep-java": "/private/tmp/dfb-v090-controls-01/warm-semgrep-java" + }, "control_inventory": { "path": "reports/releases/v0.9.0/execution-v1/control-inventory.json", - "sha256": "f282ecaecd0db93864722d95828b40e0589c51d0222d3b739e5ee4bf38db0842" + "sha256": "4179a3617cd95bb950417171e1a5c9e8490577f98d559d340000a821789d8d6f" }, "controls": [ { @@ -912,9 +947,9 @@ } ], "controls_pending_review": [ - "Existing warm-observability script is v071 hardcoded and needs versioned exact tool paths if refreshed", - "Python modeling supplemental commands referenced by prior plan need explicit inventory, no inferred missing control success", - "Swift activation/revalidation must bind new common population without rewriting historical receipts" + "Control capture and isolated root integration passed 78 focused tests; exact-head CI remains required.", + "Final merged control harness identity and resource reservation must be bound.", + "Parent final executable-plan review remains required." ], "environment_implementation": [ { @@ -14436,14 +14471,18 @@ "populations/v0.9.0.json": "d05be4f2d9effe7b14a2ce5544238364f171e1fb763db2f79ff486526ca7c890", "reports/releases/v0.9.0/execution-v1/codeql-compatibility/summary.json": "7bc68a9bc11994525c8dd536fc9c419c795003228ee285e288ea3bc7e9dd6adc", "reports/releases/v0.9.0/execution-v1/command-parsing-check.json": "73b9849ac390763dd65e26d829148323ec79f24fbae34f0572ae63a64bb306ab", - "reports/releases/v0.9.0/execution-v1/control-inventory.json": "f282ecaecd0db93864722d95828b40e0589c51d0222d3b739e5ee4bf38db0842", + "reports/releases/v0.9.0/execution-v1/control-integration-validation.json": "2767dd4588bd447dcd686179bc5cfd85ae73a98a7d2274826cff3e6db4d359a4", + "reports/releases/v0.9.0/execution-v1/control-inventory.json": "4179a3617cd95bb950417171e1a5c9e8490577f98d559d340000a821789d8d6f", "reports/releases/v0.9.0/execution-v1/dependency-version-witnesses.json": "50d7d8566cb24d1fdee789bfc7932463f3d014c8702f69822101db46ba916480", "reports/releases/v0.9.0/execution-v1/held-tool-digests.json": "9d748428932fdf7922ae62a066304226166a8f7105ebd5a8be9a42c15a4ffab2", "reports/releases/v0.9.0/execution-v1/native-codeql-packs.json": "7ab00154d9b4b16197a4103ea25d16a69f5bd0a0a2623301249fe3182649a9d4", "reports/releases/v0.9.0/execution-v1/non-swift-codeql-packs.json": "3594d6486c40c55b51c86369553c08017a2bb12f325f87d1e88c724a16569e3e", + "reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json": "3fcb4894b8544d4f1764b5755f5def28f2161d34c09e0ae31ed159bba858fd22", + "reports/releases/v0.9.0/execution-v1/runner-build.json": "133f917b39ca39001793297d57a711eb0a6713f82c01c13a8e616fc475e50403", "reports/releases/v0.9.0/execution-v1/runtime-trees/bifrost.json": "690b40f65d6b0635e3fd9e06d37470f48a09594e2e15003ce5fd88c98c1bf86c", "reports/releases/v0.9.0/execution-v1/runtime-trees/flowdroid.json": "7dc0d4b5c82674b96ccc70693b0b2a472300b0758e10e1be13da33de35d4661e", "reports/releases/v0.9.0/execution-v1/runtime-trees/infer.json": "f88687f03e98358896cbfe4974978b33da2260af42fe72cf8613fb11270aec76", + "reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json": "5626b2c49642fe379eff20ead1ea13ec6fa1e8b5f24280dce5d7432107d006e6", "reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint.json": "93cd3cfd102c3ad3577400512237f1b7dae6ca9624a5937882a5047365c090db", "reports/releases/v0.9.0/execution-v1/runtime-trees/python.json": "d87a9446e1ab2ff323dcab65a0645773413f7d4c3dcee7e4abccb2648c330bdb", "reports/releases/v0.9.0/execution-v1/runtime-trees/semgrep.json": "6da0b1ebc6d2686302dec7e4f15c60ace682e552cc64e138c0d00a21c941dd7f", @@ -14451,7 +14490,34 @@ "reports/releases/v0.9.0/execution-v1/semgrep-runtime-witness.json": "9d5462470bf89f162b4c9764203000e48f53726a3cd6c5775ffe5464caf7df15", "reports/releases/v0.9.0/execution-v1/swift-query-checks/receipt.json": "1040dc8b6f9878d7b83264f3fb7da5647df8f08d099edc492ae3fa15e5c4d8bd", "reports/releases/v0.9.0/plan.json": "540846ca9c0a0bd1c257c51f3e64bb6186cd9d3dd47c52106483fb183636efbf", - "scripts/release_environment_v090.py": "344ed79eb774134c4e04e4e243bf05c20279a52d0e050e674a9c79ddb0f16ebd" + "scripts/execute-release-controls-v090.py": "4808f3721fde76a42e24a979257bcba8babe788955140768361cf59e3419cb66", + "scripts/prepare-release-root-v090.py": "e53dabc4133d8eb4ff976e4a2e1447be90e745060b48b46aa29de7b868256b76", + "scripts/probe-bifrost-sanitizer-lowering.sh": "d6790e3a245ca568ba0c12bd09012a41831704e43e45f5567f978d725abbfb03", + "scripts/probe-bifrost-scan-native.sh": "40c95ed69d820c624e6524a200684544ef99f70d65b65a1c043071e94c38e312", + "scripts/probe-flowdroid-modeling-load-bearing.sh": "62075cc2af9ab50f274d29ea80c85f169746fe4237901f9635c93742e831928c", + "scripts/probe-flowdroid-native-shipped-surface.sh": "e19b888159f5e67a2c7419df4d59cf59e38046353e0c882588d438942872ed4a", + "scripts/probe-infer-modeling-partition.sh": "9b3c523f6d6524beef18dc877dfdeb5344cbed1e5bcd99e19bfe870b06c8bb23", + "scripts/probe-infer-native-activation.sh": "d10617b106bea4104dd2887c9d468b0312af0647e23f3a07928a62c78774c064", + "scripts/probe-infer-native-silence.sh": "7897a032da1569ca85d6286fc5a32509ff2eee623515059873a6ce2700b98666", + "scripts/probe-java-modeling-load-bearing.sh": "635fcfa58241a83ddd2afd349f1fd1ba40c5f875bffe2d1a72585ff9a66a7724", + "scripts/probe-javascript-modeling-load-bearing.sh": "e3611a71a410db0bd44c90277c29bfa8db7acd72d5248f239fc510679930314c", + "scripts/probe-joern-scan-native.sh": "f2584fa97d92bc932dbc514e84db6c89db7da8630effea92d8f244cab9bed3d5", + "scripts/probe-opentaint-modeling-surface.sh": "0415d7838cc0d0cd9a3e338cdd9d6e11f3dfc8ca8703f38347e87044c4e71de7", + "scripts/probe-opentaint-native-activation.sh": "5400cc43cded6c84b17544a7affeefdff103f8ad70df39602ccd3b865c18328f", + "scripts/probe-opentaint-primitive-tracking.sh": "88592a10525c2fd1c7de3c0e37e3e56f9a82a20b7b2ae72b2f6d9c7b92caba6c", + "scripts/probe-opentaint-product-v090.py": "0eab04452ecb434701400529b1a44bb21ff6596078d0c83295757055de09fced", + "scripts/probe-opentaint-scan-activation.sh": "d8c7074105c644ec9990d96eb281288e9795fd76f7a01ebddafe7f97ea9d384b", + "scripts/probe-opentaint-value-kind.sh": "1f1aae2c22a238011cc7061e9faf47cd075acad783264a1564582389de9fe22a", + "scripts/probe-pysa-callee-resolution.sh": "ec4501bda36ad2a017af84d609eedfe8bfd706f7c5de3c37565116750f225cbe", + "scripts/probe-pysa-modeling-load-bearing.sh": "315b8a46993392bd93ab211bad1b90318e3fcfdd313652229c15bc5285d9e15a", + "scripts/probe-pysa-native-activation.sh": "cd55635af84a86806ddd21e5726b19315330349e1cb122a09417fa36d8e43871", + "scripts/probe-python-modeling-load-bearing-v090.py": "0331eba3b003d455f1cee163c4f47d3bd648551f07473c5b7eeb3ad0e2900073", + "scripts/probe-semgrep-jsjava-native.sh": "d09e3bab859030d4679af455a341e7a155119fd48cba248abdc11b0fdf8d44f2", + "scripts/probe-warm-observability-v090.py": "c2c31b70154f25287488f262a3bd23e5d029e784d1c7c60173212a113b7346a8", + "scripts/release_control_attempt_v090.py": "96d95db99791276a68c467ab2e406360b731c30ebdae1942927dc2ab9c8ceac0", + "scripts/release_environment_v090.py": "344ed79eb774134c4e04e4e243bf05c20279a52d0e050e674a9c79ddb0f16ebd", + "scripts/run-release-control-v090.py": "8cf9785659f8319a08115422c6229162883a858dd766e128c906431ed96c7d0d", + "scripts/run-release-group-v090.py": "a19ae44adefe386f7f71b006ece268e1d3e3ea0ee54d94247737a0b1eb6b1ac4" }, "minimum_required_scope": { "controls_groups": 30, @@ -14487,14 +14553,15 @@ }, "release": "v0.9.0", "resource_budget": { - "incremental_acquisition_gib": 0, + "control_checkout_budget_basis": "33 isolated control checkouts at 0.718 GiB tracked source/evidence each (23.68 GiB total), plus original 12 GiB matrix/staging allowance, rounded to 36 GiB. No source/evidence deletion is assumed.", + "incremental_acquisition_gib": 2, "max_attempts_per_group": 2, "minimum_free_after_restore_gib": 64, - "minimum_launch_free_gib": 100, + "minimum_launch_free_gib": 124, "observed_codeql_swift_retention_gib": 5.0, "observed_joern_swift_retention_gib": 0.161, "proposed_total_retention_ceiling_gib": 24, - "proposed_working_copy_and_staging_gib": 12, + "proposed_working_copy_and_staging_gib": 36, "protected_reserve_gib": 40, "scratch_gib": 24, "serial_analyzers": 1, @@ -14510,6 +14577,83 @@ "corrected runner defect with a new prospective contract and parent review" ] }, + "runner_build": { + "binary_path": "/private/tmp/dfb-v090-target/release/dataflowbench", + "binary_sha256": "1412c3c6a1f6bfccb26652af6c78de260dde5707760a1b281f82c07fd2089b08", + "built_utc": "2026-09-29T14:08:41.726506+00:00", + "cargo": "cargo 1.97.1 (c980f4866 2026-06-30)", + "command": [ + "cargo", + "build", + "--release", + "--locked", + "-j", + "2" + ], + "environment_overrides": { + "CARGO_TARGET_DIR": "/private/tmp/dfb-v090-target" + }, + "rustc": "rustc 1.97.1 (8bab26f4f 2026-07-14)", + "schema": "release-runner-build/v1", + "source_commit": "1e8b871cc207af7995d9ad13c77a1e0d58d0c4df", + "source_files": { + "Cargo.lock": "1b02175cfa099803e999364b30f145e7dd218c94ebabc7f5312338543c7cc5b1", + "Cargo.toml": "91ecd4bffc145c2f70590eca7c5dbb3976f26194b80e3f225ffda41fc4006876", + "src/adapters/bifrost.rs": "9d89a68fac4ed09bef19a93ce269f0b2e94bbf4320b1faa7e6df0d814ac14cb0", + "src/adapters/codeql.rs": "43589ecb4062d0ef690175da66c7f3c9624f27b58f5e9a7bcc1f2365a7b01b7e", + "src/adapters/codeql/swift.rs": "554fe23d08f35db6bda567ce6abd0879c94acbbeb1c02253ed9c4b8a0bf9614f", + "src/adapters/flowdroid.rs": "aba04ea96ce63090381c8ef2783d0aded803d0e66c8242da96b31865479069bd", + "src/adapters/infer.rs": "6be54e8bc60aa532e0279fa754483459fd45f1927f9f9a0baf1b3f39024f1525", + "src/adapters/joern.rs": "5545993c88a9d91f0e1379893c3725597d839c966bbf934f27f6560a9a852469", + "src/adapters/joern/swift.rs": "14ed6b93580b274b73b450fc82b37be4ea4a49f01edbc9efcdf0315fcc4776d7", + "src/adapters/mod.rs": "19e5b9a43481f994adf6dd364bbe974bb14d0212187c4033f526c631387cecbd", + "src/adapters/opentaint.rs": "df3db7f6b5cfd35cff40a19faabd3778a55f2afdcd89114c88862e55251ae555", + "src/adapters/pysa.rs": "e036345d7c297d5abee75adef71043cf7cbc3e3e61662b85bcb4cf7bcb68c2a0", + "src/adapters/semgrep.rs": "314666cb5bd2444eb44cb5678a9347b85d453101b860e157bd0b40bb71c5fabd", + "src/adapters/swift_v2.rs": "7455857429471c901548dbd1de09ada79961caf6e997407de093c274a7654042", + "src/batch.rs": "d38ce47c270160d54dc107d01f5ace38969d682b98b5d592e2df5a513bb0a5f4", + "src/cases.rs": "56e62c24eff5ad93e68e7ab981d1f1479f29189f924201668c1f225e5b901b44", + "src/evidence.rs": "4cd815b189b5d4538e03effac82e3f8747586faf14a0b2d3f73c8ec26149fe93", + "src/freeze.rs": "81f50c7584303cabf266ce7521c325be9394173c3af53f395c402e72613b0526", + "src/latency.rs": "d3fbeb0b6e5c2e21ce2208f53a5e4848d5f0df91bcce7c2f3c750647aaaa780e", + "src/main.rs": "1a6fb436cc62d27dd34d2429eca22bce454ccd9f90856e57d3358e01cbb1c02d", + "src/modeling.rs": "faf6497d9a13aac4a7a2c159c3b5077ac79288fc1b841eb9a922bd9c7e8337e8", + "src/native.rs": "c364175afbac4e8b65a0a4f458e99fc6dfbb61ca67c6818c09df09a60cff658c", + "src/population.rs": "fe90146270bcb7f972669fc8b7f211437f793f98e027be8b9551a588ee5f2dbe", + "src/real_project.rs": "10cf064375ea5d43e82b5e5de44ad5c35f23e3dc36c4fcbe40d087ac1e37fefb", + "src/report.rs": "b81dfeaf720cbf2fa3fc21cc7eede7372561595e7f222fb99268e5babcd83bf7", + "src/report_diff.rs": "4a3c135f08d999a5ba00936b188aa1a33c0d56a7cd4e4582aa031f9f1f8fb75e", + "src/results.rs": "20e96b62900daa3cdd0dd34c2a3cd6a24b2de42b7be313ff99114c8338d3d753", + "src/runtime.rs": "9e7c4e03a9b07cf643183bb36db7a126f49e18d93e7705181284d70d5fcad83b", + "src/templates.rs": "45f98c6a1bdad748d134a54902b26e796b76ef5ff0f29d3606ba81a2b6f258ed", + "src/tests/adapters/bifrost.rs": "819babd0246f254d02c5dd245c505ae40d78d5e50d77c2930de493bf800e1167", + "src/tests/adapters/codeql.rs": "06afdd6b8b9070c00ff97df4a7ab52726bdaacd7f3a594ee8f2e80dcf27f617c", + "src/tests/adapters/flowdroid.rs": "b006c79666dcef11a3426120fde2cc94fc31e2a1bc41943b63cdfdd999f8b137", + "src/tests/adapters/infer.rs": "1af844f9e1d6562f8afb2a2f7eaef81b02bfe421f5454c5f4a206e6dd40e2da2", + "src/tests/adapters/joern.rs": "6644069351e4d5b86c77f9b6b7969fa87a451465c8a7ab7111847e470936b0fa", + "src/tests/adapters/mod.rs": "68696c95c8d65c0d3a87a4fe6caa475302ef3e26a68453b04cb2b179b9d7d9cd", + "src/tests/adapters/opentaint.rs": "7fd615d232aef29526c472747288b625bee4eadc06b66eb917017ca5e9605694", + "src/tests/adapters/pysa.rs": "d38962d7a9b3a4470b88d21a562aafc075689945e033f4bb28c42c5aaa5b4201", + "src/tests/adapters/semgrep.rs": "2ae16c34b18114c702aa37f8773896c787fb142ea21a5f776b1878aafd8bb98f", + "src/tests/batch.rs": "688b074d1567f8701ed2337f51ea7904b37e5d70160f3ba19d54c7012ef86c10", + "src/tests/cases.rs": "0373e91bae2bf0290a50b7c3413409ca69010d80220dbbe5975660f45aa8b072", + "src/tests/evidence.rs": "215b213cf61fb72e249d85e7da42fab95e9c0b32e4d4d83727161ee0c7d1cb63", + "src/tests/freeze.rs": "d4c7efc34454f736eb71125e11a463f08c57961515a3346654053f17f3737cf5", + "src/tests/latency.rs": "c658d6c19f825bd313259ebe19d03554a1d1100e1813a1f75a16c0ec7b2ac516", + "src/tests/mod.rs": "58e68bb79191af28e346aae04f3db59f77113ce44c3d21f84cc06d3dcbe32c35", + "src/tests/modeling.rs": "fa0c8c9a79c2d04a5c454e173308ab22a866f44d412e73fe2c7adbfd4cffe023", + "src/tests/native.rs": "5104dd99445fff97d34e21c5c92460fe3c228d4c2bc9c23273bf424944b0b3a2", + "src/tests/real_project.rs": "abeb4c84816191342e653b0e789625b79221d2164dfb34fbfbfe383903bcfd6b", + "src/tests/report.rs": "7da3a3f575b80a3ccbe307ff44c37ee9b71dfaf8a83126612ec677a38faeaf0d", + "src/tests/report_diff.rs": "cc1d3e5e09398cc654574e71af000520ae603671e317d29145738383c775b8d4", + "src/tests/results.rs": "56c15f583df998175621d9c0f81f9e09ef05d3657bee4894357a11a131bcda05", + "src/tests/runtime.rs": "6e8400e6741ca0233c4485c8e5aab3ce864526fda9288b11673644eba0cf2951", + "src/tests/support.rs": "e4b26d1e69f151542bfa091b58b9c68c73dce7067c0b5c48585188c0604b4583", + "src/tests/templates.rs": "4d31dd4614654681e166dd5aa50f59661bd291b6f453865179fff37f4ba599a5" + }, + "status": "success", + "warning": "Pre-existing unused WARM_SUPERSEDED_ROOT constant; no suppression added." + }, "runtime_tree_scope": "Exact held distribution trees and external interpreter executable links. OS libraries remain host dependencies; Swift plans separately bind CodeQL, compiler, SDK and Joern/JDK inventories.", "runtime_trees": [ { @@ -14535,6 +14679,10 @@ { "path": "reports/releases/v0.9.0/execution-v1/runtime-trees/semgrep.json", "sha256": "6da0b1ebc6d2686302dec7e4f15c60ace682e552cc64e138c0d00a21c941dd7f" + }, + { + "path": "reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json", + "sha256": "5626b2c49642fe379eff20ead1ea13ec6fa1e8b5f24280dce5d7432107d006e6" } ], "schema": "release-execution-contract/v1", @@ -14623,6 +14771,11 @@ "path": "/Users/dave/.cache/dataflowbench-tools/opentaint-v0.4.6/opentaint-rules-v0.3.0.tar.gz", "sha256": "3d789c9986479fec792333329abe737eccb15bc06fc59a978a58810118ca1d21" }, + "opentaint-wrapper": { + "path": "/private/tmp/dfb-v090-opentaint-full/opentaint", + "sha256": "6de7bc497c84d8b0e02b721ba10e61c4bf9f99388763df793a92ad19dbb6d8f8", + "version": "0.4.6" + }, "pyre": { "path": "/Users/dave/.cache/dataflowbench-tools/v0.8.0-python-tools/bin/pyre", "sha256": "fc6c949941f5bec4653852d43f836bb4b5f29be4db1c5e8e85b783e5a9e80c8a" @@ -14637,7 +14790,8 @@ }, "runner": { "path": "/private/tmp/dfb-v090-target/release/dataflowbench", - "status": "build-and-hash-after-merged-harness" + "sha256": "1412c3c6a1f6bfccb26652af6c78de260dde5707760a1b281f82c07fd2089b08", + "status": "merged-rust-source-build-verified-control-harness-pending" }, "semgrep": { "path": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin/semgrep", @@ -14650,8 +14804,7 @@ }, "total_wall_budget_seconds": 102960, "unresolved": [ - "exact merged harness and binary provenance", - "non-report control capture and Python supplemental command registration", + "final merged control harness and refreshed binary provenance", "final quiet-window resource reservation", "parent final executable plan review" ] diff --git a/reports/releases/v0.9.0/execution-v1/control-integration-validation.json b/reports/releases/v0.9.0/execution-v1/control-integration-validation.json new file mode 100644 index 000000000..4bf46a61b --- /dev/null +++ b/reports/releases/v0.9.0/execution-v1/control-integration-validation.json @@ -0,0 +1,142 @@ +{ + "base_commit": "1e8b871cc207af7995d9ad13c77a1e0d58d0c4df", + "captured_utc": "2026-09-29T15:35:38.468781+00:00", + "execution_authorized": false, + "live_read_only_checks": [ + "Exact OpenTaint archive hash and329-file membership verified", + "Python probe held tool digests and full CodeQL CLI and pack inventory verified", + "Current execution_authorized=false rejects both new probes before output creation" + ], + "schema": "release-control-integration-validation/v1", + "scope": "Synthetic and mocked control/recorder tests only; no analyzer control or matrix result.", + "test_count": 78, + "test_scripts": 14, + "tests": [ + { + "command": [ + "/usr/bin/python3", + "scripts/test-execute-release-controls-v090.py" + ], + "exit_code": 0, + "output": "....\n----------------------------------------------------------------------\nRan 4 tests in 0.002s\n\nOK\n", + "seconds": 0.076 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-execute-release-v090.py" + ], + "exit_code": 0, + "output": "....\n----------------------------------------------------------------------\nRan 4 tests in 0.006s\n\nOK\n", + "seconds": 0.054 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-prepare-release-root-v090.py" + ], + "exit_code": 0, + "output": ".Cloning into '/private/var/folders/t1/k_27wjcd4095w9w121dm92_w0000gn/T/tmpzfrvjhcb/controls/java-modeling'...\ndone.\nHEAD is now at a3900db exact control harness\n...Cloning into '/private/var/folders/t1/k_27wjcd4095w9w121dm92_w0000gn/T/tmpw2tfxq49/execution'...\ndone.\nHEAD is now at da7efc5 harness\n..\n----------------------------------------------------------------------\nRan 6 tests in 1.155s\n\nOK\n", + "seconds": 1.201 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-probe-opentaint-product-v090.py" + ], + "exit_code": 0, + "output": ".....\n----------------------------------------------------------------------\nRan 5 tests in 0.480s\n\nOK\n", + "seconds": 0.546 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-probe-python-modeling-load-bearing-v090.py" + ], + "exit_code": 0, + "output": ".....\n----------------------------------------------------------------------\nRan 5 tests in 0.065s\n\nOK\n", + "seconds": 0.119 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-probe-warm-observability-v090.py" + ], + "exit_code": 0, + "output": ".....\n----------------------------------------------------------------------\nRan 5 tests in 0.008s\n\nOK\n", + "seconds": 0.063 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-release-attempt-v090.py" + ], + "exit_code": 0, + "output": "..........\n----------------------------------------------------------------------\nRan 10 tests in 0.891s\n\nOK\n", + "seconds": 0.934 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-release-control-attempt-v090.py" + ], + "exit_code": 0, + "output": "........\n----------------------------------------------------------------------\nRan 8 tests in 2.631s\n\nOK\n", + "seconds": 2.678 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-release-environment-v090.py" + ], + "exit_code": 0, + "output": "..\n----------------------------------------------------------------------\nRan 2 tests in 0.004s\n\nOK\n", + "seconds": 0.046 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-release-runtime-inventory-v090.py" + ], + "exit_code": 0, + "output": "..\n----------------------------------------------------------------------\nRan 2 tests in 0.004s\n\nOK\n", + "seconds": 0.042 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-run-release-control-v090.py" + ], + "exit_code": 0, + "output": "..\n----------------------------------------------------------------------\nRan 2 tests in 0.001s\n\nOK\n", + "seconds": 0.064 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-run-release-group-v090.py" + ], + "exit_code": 0, + "output": ".........\n----------------------------------------------------------------------\nRan 9 tests in 0.014s\n\nOK\n", + "seconds": 0.076 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-v090-execution-contract.py" + ], + "exit_code": 0, + "output": "...........\n----------------------------------------------------------------------\nRan 11 tests in 0.123s\n\nOK\n", + "seconds": 0.165 + }, + { + "command": [ + "/usr/bin/python3", + "scripts/test-verify-release-attempt-v090.py" + ], + "exit_code": 0, + "output": ".....\n----------------------------------------------------------------------\nRan 5 tests in 0.502s\n\nOK\n", + "seconds": 0.55 + } + ] +} diff --git a/reports/releases/v0.9.0/execution-v1/control-inventory.json b/reports/releases/v0.9.0/execution-v1/control-inventory.json index 5c735b9db..004928f4e 100644 --- a/reports/releases/v0.9.0/execution-v1/control-inventory.json +++ b/reports/releases/v0.9.0/execution-v1/control-inventory.json @@ -12,17 +12,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-bifrost-sanitizer-lowering/reports/raw/control-scratch/probe-bifrost-sanitizer-lowering" }, "historical_attempt": "probe-bifrost-sanitizer-lowering-attempt-01", "historical_seconds": 152.580192, "id": "probe-bifrost-sanitizer-lowering", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a9-bifrost-sanitizer" + "reports/raw/amendment-a9-bifrost-sanitizer", + "reports/raw/control-scratch/probe-bifrost-sanitizer-lowering" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-bifrost-sanitizer-lowering.sh", @@ -30,7 +34,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -44,17 +48,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-bifrost-scan-native/reports/raw/control-scratch/probe-bifrost-scan-native" }, "historical_attempt": "probe-bifrost-scan-native-attempt-05", "historical_seconds": 429.333981, "id": "probe-bifrost-scan-native", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a32-bifrost-scan-native" + "reports/raw/amendment-a32-bifrost-scan-native", + "reports/raw/control-scratch/probe-bifrost-scan-native" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-bifrost-scan-native.sh", @@ -62,7 +70,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -84,17 +92,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-flowdroid-modeling-load-bearing/reports/raw/control-scratch/probe-flowdroid-modeling-load-bearing" }, "historical_attempt": "probe-flowdroid-modeling-load-bearing-attempt-01", "historical_seconds": 12.852858, "id": "probe-flowdroid-modeling-load-bearing", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/load-bearing-java-modeling" + "reports/raw/load-bearing-java-modeling", + "reports/raw/control-scratch/probe-flowdroid-modeling-load-bearing" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-flowdroid-modeling-load-bearing.sh", @@ -102,7 +114,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -120,17 +132,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-flowdroid-native-shipped-surface/reports/raw/control-scratch/probe-flowdroid-native-shipped-surface" }, "historical_attempt": "probe-flowdroid-native-shipped-surface-attempt-01", "historical_seconds": 19.713252, "id": "probe-flowdroid-native-shipped-surface", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a29-flowdroid-shipped-surface" + "reports/raw/amendment-a29-flowdroid-shipped-surface", + "reports/raw/control-scratch/probe-flowdroid-native-shipped-surface" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-flowdroid-native-shipped-surface.sh", @@ -138,7 +154,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -154,17 +170,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-infer-modeling-partition/reports/raw/control-scratch/probe-infer-modeling-partition" }, "historical_attempt": "probe-infer-modeling-partition-attempt-01", "historical_seconds": 88.429977, "id": "probe-infer-modeling-partition", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a13-infer-partition" + "reports/raw/amendment-a13-infer-partition", + "reports/raw/control-scratch/probe-infer-modeling-partition" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-infer-modeling-partition.sh", @@ -172,7 +192,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -188,17 +208,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-infer-native-activation/reports/raw/control-scratch/probe-infer-native-activation" }, "historical_attempt": "probe-infer-native-activation-attempt-01", "historical_seconds": 49.116346, "id": "probe-infer-native-activation", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a28-infer-native-activation" + "reports/raw/amendment-a28-infer-native-activation", + "reports/raw/control-scratch/probe-infer-native-activation" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-infer-native-activation.sh", @@ -206,7 +230,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -222,17 +246,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-infer-native-silence/reports/raw/control-scratch/probe-infer-native-silence" }, "historical_attempt": "probe-infer-native-silence-attempt-01", "historical_seconds": 44.059198, "id": "probe-infer-native-silence", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a14-infer-native-silence" + "reports/raw/amendment-a14-infer-native-silence", + "reports/raw/control-scratch/probe-infer-native-silence" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-infer-native-silence.sh", @@ -240,7 +268,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -260,17 +288,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-java-modeling-load-bearing/reports/raw/control-scratch/probe-java-modeling-load-bearing" }, "historical_attempt": "probe-java-modeling-load-bearing-attempt-02", "historical_seconds": 143.255322, "id": "probe-java-modeling-load-bearing", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/load-bearing-java-modeling" + "reports/raw/load-bearing-java-modeling", + "reports/raw/control-scratch/probe-java-modeling-load-bearing" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-java-modeling-load-bearing.sh", @@ -278,7 +310,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -298,17 +330,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-javascript-modeling-load-bearing/reports/raw/control-scratch/probe-javascript-modeling-load-bearing" }, "historical_attempt": "probe-javascript-modeling-load-bearing-attempt-01", "historical_seconds": 77.470296, "id": "probe-javascript-modeling-load-bearing", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/load-bearing-javascript-modeling" + "reports/raw/load-bearing-javascript-modeling", + "reports/raw/control-scratch/probe-javascript-modeling-load-bearing" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-javascript-modeling-load-bearing.sh", @@ -316,7 +352,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -332,17 +368,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-joern-scan-native/reports/raw/control-scratch/probe-joern-scan-native" }, "historical_attempt": "probe-joern-scan-native-attempt-02", "historical_seconds": 218.538322, "id": "probe-joern-scan-native", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a26-joern-scan-native" + "reports/raw/amendment-a26-joern-scan-native", + "reports/raw/control-scratch/probe-joern-scan-native" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-joern-scan-native.sh", @@ -350,7 +390,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -370,17 +410,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-opentaint-modeling-surface/reports/raw/control-scratch/probe-opentaint-modeling-surface" }, "historical_attempt": "probe-opentaint-modeling-surface-attempt-01", "historical_seconds": 107.256705, "id": "probe-opentaint-modeling-surface", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/opentaint-modeling-surface-probe" + "reports/raw/opentaint-modeling-surface-probe", + "reports/raw/control-scratch/probe-opentaint-modeling-surface" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-opentaint-modeling-surface.sh", @@ -388,7 +432,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -408,17 +452,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-opentaint-native-activation/reports/raw/control-scratch/probe-opentaint-native-activation" }, "historical_attempt": "probe-opentaint-native-activation-attempt-01", "historical_seconds": 4.60874, "id": "probe-opentaint-native-activation", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/opentaint-native-activation-probe" + "reports/raw/opentaint-native-activation-probe", + "reports/raw/control-scratch/probe-opentaint-native-activation" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-opentaint-native-activation.sh", @@ -426,7 +474,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -444,17 +492,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-opentaint-primitive-tracking/reports/raw/control-scratch/probe-opentaint-primitive-tracking" }, "historical_attempt": "probe-opentaint-primitive-tracking-attempt-01", "historical_seconds": 7.993503, "id": "probe-opentaint-primitive-tracking", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/opentaint-primitive-tracking-probe" + "reports/raw/opentaint-primitive-tracking-probe", + "reports/raw/control-scratch/probe-opentaint-primitive-tracking" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-opentaint-primitive-tracking.sh", @@ -462,7 +514,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -484,17 +536,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-opentaint-scan-activation/reports/raw/control-scratch/probe-opentaint-scan-activation" }, "historical_attempt": "probe-opentaint-scan-activation-attempt-01", "historical_seconds": 156.376784, "id": "probe-opentaint-scan-activation", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a25-opentaint-scan-activation" + "reports/raw/amendment-a25-opentaint-scan-activation", + "reports/raw/control-scratch/probe-opentaint-scan-activation" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-opentaint-scan-activation.sh", @@ -502,7 +558,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -520,17 +576,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-opentaint-value-kind/reports/raw/control-scratch/probe-opentaint-value-kind" }, "historical_attempt": "probe-opentaint-value-kind-attempt-01", "historical_seconds": 4.575929, "id": "probe-opentaint-value-kind", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/opentaint-value-kind-probe" + "reports/raw/opentaint-value-kind-probe", + "reports/raw/control-scratch/probe-opentaint-value-kind" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-opentaint-value-kind.sh", @@ -538,7 +598,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -556,17 +616,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-pysa-callee-resolution/reports/raw/control-scratch/probe-pysa-callee-resolution" }, "historical_attempt": "probe-pysa-callee-resolution-attempt-01", "historical_seconds": 2.885217, "id": "probe-pysa-callee-resolution", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/pysa-callee-resolution-probe" + "reports/raw/pysa-callee-resolution-probe", + "reports/raw/control-scratch/probe-pysa-callee-resolution" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-pysa-callee-resolution.sh", @@ -574,7 +638,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -592,17 +656,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-pysa-modeling-load-bearing/reports/raw/control-scratch/probe-pysa-modeling-load-bearing" }, "historical_attempt": "probe-pysa-modeling-load-bearing-attempt-01", "historical_seconds": 74.052594, "id": "probe-pysa-modeling-load-bearing", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a16-pysa-modeling" + "reports/raw/amendment-a16-pysa-modeling", + "reports/raw/control-scratch/probe-pysa-modeling-load-bearing" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-pysa-modeling-load-bearing.sh", @@ -610,7 +678,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -628,17 +696,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-pysa-native-activation/reports/raw/control-scratch/probe-pysa-native-activation" }, "historical_attempt": "probe-pysa-native-activation-attempt-01", "historical_seconds": 6.270934, "id": "probe-pysa-native-activation", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a17-pysa-native" + "reports/raw/amendment-a17-pysa-native", + "reports/raw/control-scratch/probe-pysa-native-activation" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-pysa-native-activation.sh", @@ -646,7 +718,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -660,17 +732,21 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-semgrep-jsjava-native/reports/raw/control-scratch/probe-semgrep-jsjava-native" }, "historical_attempt": "probe-semgrep-jsjava-native-attempt-01", "historical_seconds": 60.174151, "id": "probe-semgrep-jsjava-native", "maximum_attempts": 2, + "measurement_repeats": 1, "output_roots": [ - "reports/raw/amendment-a27-semgrep-jsjava-native" + "reports/raw/amendment-a27-semgrep-jsjava-native", + "reports/raw/control-scratch/probe-semgrep-jsjava-native" ], + "repeat_mechanism": "single-control-series", "script_identity": [ { "path": "scripts/probe-semgrep-jsjava-native.sh", @@ -678,7 +754,7 @@ } ], "stage": "script_probes", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -702,20 +778,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/warm-joern-java/reports/raw/control-scratch/warm-joern-java" }, "historical_attempt": "warm-joern-java-attempt-01", "historical_seconds": 344.041654, "id": "warm-joern-java", "maximum_attempts": 2, + "measurement_repeats": 2, "output_roots": [ - "reports/raw/warm-latency/joern-java-kernel" + "reports/raw/warm-latency/joern-java-kernel", + "reports/raw/control-scratch/warm-joern-java" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "warm", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -739,20 +819,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/warm-semgrep-java/reports/raw/control-scratch/warm-semgrep-java" }, "historical_attempt": "warm-semgrep-java-attempt-02", "historical_seconds": 13.541532, "id": "warm-semgrep-java", "maximum_attempts": 2, + "measurement_repeats": 2, "output_roots": [ - "reports/raw/warm-latency/semgrep-java-kernel" + "reports/raw/warm-latency/semgrep-java-kernel", + "reports/raw/control-scratch/warm-semgrep-java" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "warm", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -806,20 +890,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-bifrost-python/reports/raw/control-scratch/overhead-bifrost-python" }, "historical_attempt": "overhead-bifrost-python-attempt-01", "historical_seconds": 0.477327, "id": "overhead-bifrost-python", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/bifrost-python" + "reports/raw/invocation-overhead/bifrost-python", + "reports/raw/control-scratch/overhead-bifrost-python" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -873,20 +961,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-codeql-ruby/reports/raw/control-scratch/overhead-codeql-ruby" }, "historical_attempt": "overhead-codeql-ruby-attempt-01", "historical_seconds": 61.863348, "id": "overhead-codeql-ruby", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/codeql-ruby" + "reports/raw/invocation-overhead/codeql-ruby", + "reports/raw/control-scratch/overhead-codeql-ruby" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -940,20 +1032,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-flowdroid-java/reports/raw/control-scratch/overhead-flowdroid-java" }, "historical_attempt": "overhead-flowdroid-java-attempt-01", "historical_seconds": 4.302558, "id": "overhead-flowdroid-java", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/flowdroid-java" + "reports/raw/invocation-overhead/flowdroid-java", + "reports/raw/control-scratch/overhead-flowdroid-java" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -1007,20 +1103,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-infer-c/reports/raw/control-scratch/overhead-infer-c" }, "historical_attempt": "overhead-infer-c-attempt-01", "historical_seconds": 1.897071, "id": "overhead-infer-c", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/infer-c" + "reports/raw/invocation-overhead/infer-c", + "reports/raw/control-scratch/overhead-infer-c" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -1074,20 +1174,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-joern-php/reports/raw/control-scratch/overhead-joern-php" }, "historical_attempt": "overhead-joern-php-attempt-01", "historical_seconds": 15.72857, "id": "overhead-joern-php", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/joern-php" + "reports/raw/invocation-overhead/joern-php", + "reports/raw/control-scratch/overhead-joern-php" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -1141,20 +1245,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-joern-java/reports/raw/control-scratch/overhead-joern-java" }, "historical_attempt": "overhead-joern-java-attempt-01", "historical_seconds": 26.781594, "id": "overhead-joern-java", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/joern-java" + "reports/raw/invocation-overhead/joern-java", + "reports/raw/control-scratch/overhead-joern-java" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -1208,20 +1316,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-opentaint-kotlin/reports/raw/control-scratch/overhead-opentaint-kotlin" }, "historical_attempt": "overhead-opentaint-kotlin-attempt-02", "historical_seconds": 19.844688, "id": "overhead-opentaint-kotlin", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/opentaint-kotlin" + "reports/raw/invocation-overhead/opentaint-kotlin", + "reports/raw/control-scratch/overhead-opentaint-kotlin" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -1275,20 +1387,24 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-pysa-python/reports/raw/control-scratch/overhead-pysa-python" }, "historical_attempt": "overhead-pysa-python-attempt-01", "historical_seconds": 8.03495, "id": "overhead-pysa-python", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/pysa-python" + "reports/raw/invocation-overhead/pysa-python", + "reports/raw/control-scratch/overhead-pysa-python" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" }, { "argv": [ @@ -1342,29 +1458,162 @@ "HOME": "/Users/dave", "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", "LANG": "en_US.UTF-8", - "PATH": "/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", "PYTHONDONTWRITEBYTECODE": "1", - "TMPDIR": "/private/tmp" + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/overhead-semgrep-kotlin/reports/raw/control-scratch/overhead-semgrep-kotlin" }, "historical_attempt": "overhead-semgrep-kotlin-attempt-01", "historical_seconds": 3.568445, "id": "overhead-semgrep-kotlin", "maximum_attempts": 2, + "measurement_repeats": 3, "output_roots": [ - "reports/raw/invocation-overhead/semgrep-kotlin" + "reports/raw/invocation-overhead/semgrep-kotlin", + "reports/raw/control-scratch/overhead-semgrep-kotlin" ], + "repeat_mechanism": "inside-harness-command", "script_identity": [], "stage": "overhead", - "status": "registered-pending-control-capture-integration" + "status": "registered-pending-final-executable-plan-review" + }, + { + "argv": [ + "/usr/bin/python3", + "scripts/probe-python-modeling-load-bearing-v090.py" + ], + "controls": [ + "Bifrost source/sink with/without declaration over positive and undeclared sibling negative", + "CodeQL opaque propagator with committed model/removal query", + "Joern sanitizer with/without declaration and unmodeled P/O", + "Semgrep source/sink with/without over positive/negative plus safe-function Z selectivity contrast" + ], + "deadline_basis": "Bounded nested command count times per-command limit, including cleanup allowance; prospective ceiling, not a timing prediction.", + "deadline_seconds": 18000, + "environment": { + "HOME": "/Users/dave", + "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", + "LANG": "en_US.UTF-8", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PYTHONDONTWRITEBYTECODE": "1", + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-python-modeling-load-bearing/reports/raw/control-scratch/probe-python-modeling-load-bearing" + }, + "historical_script_sha256": "5d67a11981af360138ffe4b158461893e80358fbeffed389aea95e8c90f175bf", + "id": "probe-python-modeling-load-bearing", + "maximum_attempts": 2, + "measurement_repeats": 1, + "output_roots": [ + "reports/raw/load-bearing-python-modeling-v090", + "reports/raw/control-scratch/probe-python-modeling-load-bearing" + ], + "repeat_mechanism": "single-control-series", + "script_identity": [ + { + "path": "scripts/probe-python-modeling-load-bearing-v090.py", + "sha256": "0331eba3b003d455f1cee163c4f47d3bd648551f07473c5b7eeb3ad0e2900073" + } + ], + "stage": "supplemental", + "status": "registered-pending-final-executable-plan-review" + }, + { + "argv": [ + "/usr/bin/python3", + "scripts/probe-warm-observability-v090.py" + ], + "deadline_basis": "Bounded nested command count times per-command limit, including cleanup allowance; prospective ceiling, not a timing prediction.", + "deadline_seconds": 900, + "environment": { + "HOME": "/Users/dave", + "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", + "LANG": "en_US.UTF-8", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PYTHONDONTWRITEBYTECODE": "1", + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-warm-observability/reports/raw/control-scratch/probe-warm-observability" + }, + "historical_script_sha256": "caca962dd1d737aadd9c24317fbedeedf6e6ba4dd6c38d592215615703a2e78e", + "id": "probe-warm-observability", + "maximum_attempts": 2, + "measurement_repeats": 1, + "output_roots": [ + "reports/raw/warm-observability-v090", + "reports/raw/control-scratch/probe-warm-observability" + ], + "repeat_mechanism": "single-control-series", + "scope": "Current CLI observations plus preregistered same-work controls; no declined capability inferred merely from help absence.", + "script_identity": [ + { + "path": "scripts/probe-warm-observability-v090.py", + "sha256": "c2c31b70154f25287488f262a3bd23e5d029e784d1c7c60173212a113b7346a8" + } + ], + "stage": "supplemental", + "status": "registered-pending-final-executable-plan-review" + }, + { + "argv": [ + "/usr/bin/python3", + "scripts/probe-opentaint-product-v090.py" + ], + "controls": [ + "12 unchanged native Java fixtures", + "servlet source/Runtime.exec positive", + "same sink constant negative" + ], + "deadline_basis": "31 nested commands bounded at 300 seconds each plus 900 seconds for validation, capture and cleanup; prospective ceiling, not timing prediction.", + "deadline_seconds": 10200, + "environment": { + "HOME": "/Users/dave", + "JAVA_HOME": "/Users/dave/.sdkman/candidates/java/21.0.8-tem", + "LANG": "en_US.UTF-8", + "PATH": "/Users/dave/.cache/dataflowbench-tools/semgrep-1.177.0-venv/bin:/Users/dave/.sdkman/candidates/java/21.0.8-tem/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:/usr/bin:/bin:/usr/sbin:/sbin", + "PYTHONDONTWRITEBYTECODE": "1", + "SEMGREP_ENABLE_VERSION_CHECK": "0", + "TMPDIR": "/private/tmp/dfb-v090-controls-01/probe-opentaint-product-v090/reports/raw/control-scratch/probe-opentaint-product-v090" + }, + "help_provenance": "Fresh root and scan help captured in this probe; previous help was tool output, not a named historical artifact.", + "historical_script_sha256": "0bc8da2636b0331274758729693ab3b3cdaf07871a151a1706b368f71ac18369", + "id": "probe-opentaint-product-v090", + "identity_record": "reports/releases/v0.7.1/opentaint-full-bundle-identity.json", + "maximum_attempts": 2, + "measurement_repeats": 1, + "normalization": "None; actual output and engagement must be audited before native decisions or wrapper equivalence.", + "output_roots": [ + "reports/raw/opentaint-product-v090", + "reports/raw/control-scratch/probe-opentaint-product-v090" + ], + "prerequisite": "Extract and hash complete verified full release, including bundled JRE, at untimed DFB boundary before activation; prior base comparison preserved separately.", + "repeat_mechanism": "single-control-series", + "restoration_record": "reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json", + "runtime": "Wrapper subprocess JAVA_HOME and PATH select full bundle jre; fixture javac retains recorded Temurin compiler environment. Exact environment is recorded per nested command.", + "script_identity": [ + { + "path": "scripts/probe-opentaint-product-v090.py", + "sha256": "0eab04452ecb434701400529b1a44bb21ff6596078d0c83295757055de09fced" + } + ], + "stage": "supplemental", + "status": "registered-pending-final-executable-plan-review" } ], "execution_authorized": false, "release": "v0.9.0", "remaining": [ - "Control recorder must capture non-report outputs with immutable attempt receipts.", - "Python modeling supplemental command membership must be resolved from prior release amendment evidence.", - "Warm-observability help capture is separate from qualified warm performance." + "Final merged control harness and refreshed binary provenance are required.", + "Warm and overhead require a qualified quiet-host window.", + "Parent final executable-plan review is required." ], "schema": "release-control-inventory/v1", + "supplemental_control_ids": [ + "probe-python-modeling-load-bearing", + "probe-warm-observability", + "probe-opentaint-product-v090" + ], + "supplemental_source": { + "path": "reports/releases/v0.7.1/supplemental-plan.json", + "sha256": "f1d31e75eab362b0f4def38a7d2f47048f8ebdedb3d8e2c533a2b65a267085c8" + }, "warm_observability_script": "scripts/probe-warm-observability-v090.py" } diff --git a/reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json b/reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json new file mode 100644 index 000000000..b5e0922c6 --- /dev/null +++ b/reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json @@ -0,0 +1,21 @@ +{ + "after_free_bytes": 182272000000, + "archive_bytes": 111028216, + "archive_path": "/private/tmp/dfb-v090-opentaint-acquisition/opentaint-full_darwin_arm64.tar.gz", + "archive_sha256": "95c14073cb94b3a942488531c74d7812653bc7f684d1efa53f422d5ba22d3d92", + "before_free_bytes": 182272000000, + "budget_bytes": 2147483648, + "bundle_root": "/private/tmp/dfb-v090-opentaint-full", + "captured_utc": "2026-09-29T14:07:10.732624+00:00", + "exact_file_membership": true, + "expanded_bytes": 231502028, + "minimum_free_after_bytes": 107374182400, + "reference": { + "path": "reports/releases/v0.7.1/opentaint-full-bundle-identity.json", + "sha256": "591c5a8d598148bdb18fb74353d863b18753f00ab30149e9095a9ef7c1ab2d63" + }, + "runtime_executed": false, + "schema": "runtime-restoration/v1", + "source_url": "https://github.com/seqra/opentaint/releases/download/v0.4.6/opentaint-full_darwin_arm64.tar.gz", + "verified_files": 329 +} diff --git a/reports/releases/v0.9.0/execution-v1/runner-build.json b/reports/releases/v0.9.0/execution-v1/runner-build.json new file mode 100644 index 000000000..0248a81e1 --- /dev/null +++ b/reports/releases/v0.9.0/execution-v1/runner-build.json @@ -0,0 +1,77 @@ +{ + "binary_path": "/private/tmp/dfb-v090-target/release/dataflowbench", + "binary_sha256": "1412c3c6a1f6bfccb26652af6c78de260dde5707760a1b281f82c07fd2089b08", + "built_utc": "2026-09-29T14:08:41.726506+00:00", + "cargo": "cargo 1.97.1 (c980f4866 2026-06-30)", + "command": [ + "cargo", + "build", + "--release", + "--locked", + "-j", + "2" + ], + "environment_overrides": { + "CARGO_TARGET_DIR": "/private/tmp/dfb-v090-target" + }, + "rustc": "rustc 1.97.1 (8bab26f4f 2026-07-14)", + "schema": "release-runner-build/v1", + "source_commit": "1e8b871cc207af7995d9ad13c77a1e0d58d0c4df", + "source_files": { + "Cargo.lock": "1b02175cfa099803e999364b30f145e7dd218c94ebabc7f5312338543c7cc5b1", + "Cargo.toml": "91ecd4bffc145c2f70590eca7c5dbb3976f26194b80e3f225ffda41fc4006876", + "src/adapters/bifrost.rs": "9d89a68fac4ed09bef19a93ce269f0b2e94bbf4320b1faa7e6df0d814ac14cb0", + "src/adapters/codeql.rs": "43589ecb4062d0ef690175da66c7f3c9624f27b58f5e9a7bcc1f2365a7b01b7e", + "src/adapters/codeql/swift.rs": "554fe23d08f35db6bda567ce6abd0879c94acbbeb1c02253ed9c4b8a0bf9614f", + "src/adapters/flowdroid.rs": "aba04ea96ce63090381c8ef2783d0aded803d0e66c8242da96b31865479069bd", + "src/adapters/infer.rs": "6be54e8bc60aa532e0279fa754483459fd45f1927f9f9a0baf1b3f39024f1525", + "src/adapters/joern.rs": "5545993c88a9d91f0e1379893c3725597d839c966bbf934f27f6560a9a852469", + "src/adapters/joern/swift.rs": "14ed6b93580b274b73b450fc82b37be4ea4a49f01edbc9efcdf0315fcc4776d7", + "src/adapters/mod.rs": "19e5b9a43481f994adf6dd364bbe974bb14d0212187c4033f526c631387cecbd", + "src/adapters/opentaint.rs": "df3db7f6b5cfd35cff40a19faabd3778a55f2afdcd89114c88862e55251ae555", + "src/adapters/pysa.rs": "e036345d7c297d5abee75adef71043cf7cbc3e3e61662b85bcb4cf7bcb68c2a0", + "src/adapters/semgrep.rs": "314666cb5bd2444eb44cb5678a9347b85d453101b860e157bd0b40bb71c5fabd", + "src/adapters/swift_v2.rs": "7455857429471c901548dbd1de09ada79961caf6e997407de093c274a7654042", + "src/batch.rs": "d38ce47c270160d54dc107d01f5ace38969d682b98b5d592e2df5a513bb0a5f4", + "src/cases.rs": "56e62c24eff5ad93e68e7ab981d1f1479f29189f924201668c1f225e5b901b44", + "src/evidence.rs": "4cd815b189b5d4538e03effac82e3f8747586faf14a0b2d3f73c8ec26149fe93", + "src/freeze.rs": "81f50c7584303cabf266ce7521c325be9394173c3af53f395c402e72613b0526", + "src/latency.rs": "d3fbeb0b6e5c2e21ce2208f53a5e4848d5f0df91bcce7c2f3c750647aaaa780e", + "src/main.rs": "1a6fb436cc62d27dd34d2429eca22bce454ccd9f90856e57d3358e01cbb1c02d", + "src/modeling.rs": "faf6497d9a13aac4a7a2c159c3b5077ac79288fc1b841eb9a922bd9c7e8337e8", + "src/native.rs": "c364175afbac4e8b65a0a4f458e99fc6dfbb61ca67c6818c09df09a60cff658c", + "src/population.rs": "fe90146270bcb7f972669fc8b7f211437f793f98e027be8b9551a588ee5f2dbe", + "src/real_project.rs": "10cf064375ea5d43e82b5e5de44ad5c35f23e3dc36c4fcbe40d087ac1e37fefb", + "src/report.rs": "b81dfeaf720cbf2fa3fc21cc7eede7372561595e7f222fb99268e5babcd83bf7", + "src/report_diff.rs": "4a3c135f08d999a5ba00936b188aa1a33c0d56a7cd4e4582aa031f9f1f8fb75e", + "src/results.rs": "20e96b62900daa3cdd0dd34c2a3cd6a24b2de42b7be313ff99114c8338d3d753", + "src/runtime.rs": "9e7c4e03a9b07cf643183bb36db7a126f49e18d93e7705181284d70d5fcad83b", + "src/templates.rs": "45f98c6a1bdad748d134a54902b26e796b76ef5ff0f29d3606ba81a2b6f258ed", + "src/tests/adapters/bifrost.rs": "819babd0246f254d02c5dd245c505ae40d78d5e50d77c2930de493bf800e1167", + "src/tests/adapters/codeql.rs": "06afdd6b8b9070c00ff97df4a7ab52726bdaacd7f3a594ee8f2e80dcf27f617c", + "src/tests/adapters/flowdroid.rs": "b006c79666dcef11a3426120fde2cc94fc31e2a1bc41943b63cdfdd999f8b137", + "src/tests/adapters/infer.rs": "1af844f9e1d6562f8afb2a2f7eaef81b02bfe421f5454c5f4a206e6dd40e2da2", + "src/tests/adapters/joern.rs": "6644069351e4d5b86c77f9b6b7969fa87a451465c8a7ab7111847e470936b0fa", + "src/tests/adapters/mod.rs": "68696c95c8d65c0d3a87a4fe6caa475302ef3e26a68453b04cb2b179b9d7d9cd", + "src/tests/adapters/opentaint.rs": "7fd615d232aef29526c472747288b625bee4eadc06b66eb917017ca5e9605694", + "src/tests/adapters/pysa.rs": "d38962d7a9b3a4470b88d21a562aafc075689945e033f4bb28c42c5aaa5b4201", + "src/tests/adapters/semgrep.rs": "2ae16c34b18114c702aa37f8773896c787fb142ea21a5f776b1878aafd8bb98f", + "src/tests/batch.rs": "688b074d1567f8701ed2337f51ea7904b37e5d70160f3ba19d54c7012ef86c10", + "src/tests/cases.rs": "0373e91bae2bf0290a50b7c3413409ca69010d80220dbbe5975660f45aa8b072", + "src/tests/evidence.rs": "215b213cf61fb72e249d85e7da42fab95e9c0b32e4d4d83727161ee0c7d1cb63", + "src/tests/freeze.rs": "d4c7efc34454f736eb71125e11a463f08c57961515a3346654053f17f3737cf5", + "src/tests/latency.rs": "c658d6c19f825bd313259ebe19d03554a1d1100e1813a1f75a16c0ec7b2ac516", + "src/tests/mod.rs": "58e68bb79191af28e346aae04f3db59f77113ce44c3d21f84cc06d3dcbe32c35", + "src/tests/modeling.rs": "fa0c8c9a79c2d04a5c454e173308ab22a866f44d412e73fe2c7adbfd4cffe023", + "src/tests/native.rs": "5104dd99445fff97d34e21c5c92460fe3c228d4c2bc9c23273bf424944b0b3a2", + "src/tests/real_project.rs": "abeb4c84816191342e653b0e789625b79221d2164dfb34fbfbfe383903bcfd6b", + "src/tests/report.rs": "7da3a3f575b80a3ccbe307ff44c37ee9b71dfaf8a83126612ec677a38faeaf0d", + "src/tests/report_diff.rs": "cc1d3e5e09398cc654574e71af000520ae603671e317d29145738383c775b8d4", + "src/tests/results.rs": "56c15f583df998175621d9c0f81f9e09ef05d3657bee4894357a11a131bcda05", + "src/tests/runtime.rs": "6e8400e6741ca0233c4485c8e5aab3ce864526fda9288b11673644eba0cf2951", + "src/tests/support.rs": "e4b26d1e69f151542bfa091b58b9c68c73dce7067c0b5c48585188c0604b4583", + "src/tests/templates.rs": "4d31dd4614654681e166dd5aa50f59661bd291b6f453865179fff37f4ba599a5" + }, + "status": "success", + "warning": "Pre-existing unused WARM_SUPERSEDED_ROOT constant; no suppression added." +} diff --git a/reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json b/reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json new file mode 100644 index 000000000..0d65071a7 --- /dev/null +++ b/reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json @@ -0,0 +1,1611 @@ +{ + "entries": { + "jre": { + "directory": true, + "mode": 493 + }, + "jre/NOTICE": { + "mode": 420, + "sha256": "c02756bcd9fa8191bf0fda4451bc018414dd44ee35bf09922c24377a475e4b5a" + }, + "jre/bin": { + "directory": true, + "mode": 493 + }, + "jre/bin/java": { + "mode": 493, + "sha256": "9be1d0a740ff6502df1a762145e62860f5de4b7e17658d9cb9498da3acf9d16c" + }, + "jre/bin/jfr": { + "mode": 493, + "sha256": "0dc4445b9482b480ca5a5e9b6e171f8c9a04a310a19273c323b71753abba7a0e" + }, + "jre/bin/jrunscript": { + "mode": 493, + "sha256": "a273976003d20d1f5b2c3fedfce894683b3252c77858e0aad4a2b50b785f0897" + }, + "jre/bin/jwebserver": { + "mode": 493, + "sha256": "83b47b44c21784db3f19650b1ecce2e36f76e56755c20aaba9c2d91e9735f40e" + }, + "jre/bin/keytool": { + "mode": 493, + "sha256": "17b50cf49c688935080bb39b3196494827577735d9240ef9178598df6dbe0911" + }, + "jre/bin/rmiregistry": { + "mode": 493, + "sha256": "0fd9e9bec1badb4ae0aae451fb93ab2cd62f7ae7c1236d82a65f7c9927c77090" + }, + "jre/conf": { + "directory": true, + "mode": 493 + }, + "jre/conf/jaxp.properties": { + "mode": 420, + "sha256": "7d95c49465d0c836d608d02856d3b097934dfb5dc4bd2279affaf337d045b708" + }, + "jre/conf/logging.properties": { + "mode": 420, + "sha256": "b62d2733ab99556b108a1951d894c5a8d76b1ac7a00c02c388f9eb9be046c56f" + }, + "jre/conf/management": { + "directory": true, + "mode": 493 + }, + "jre/conf/management/jmxremote.access": { + "mode": 420, + "sha256": "0c25d26ee212ca1e8c33f67c3c460d43fe849c3a1d23dbe341148517602b280c" + }, + "jre/conf/management/jmxremote.password.template": { + "mode": 420, + "sha256": "0273b6a6b9e20e6ce54c5aee70164028e0395063b2b7d39060a40b6495543dbf" + }, + "jre/conf/management/management.properties": { + "mode": 420, + "sha256": "07dffdd85b01c19bf46ca320a699aba48dd6b01043eb0bd6a9528c7993312bad" + }, + "jre/conf/net.properties": { + "mode": 420, + "sha256": "2e070ed1d97052f0ce8771ed2ef74a38d7c260a45fde7a3682c8844e5fdf58a4" + }, + "jre/conf/security": { + "directory": true, + "mode": 493 + }, + "jre/conf/security/java.policy": { + "mode": 420, + "sha256": "d51bcab7ed301caeff3779a5e777e6019864cecee5e2abc102ef991b0de77af2" + }, + "jre/conf/security/java.security": { + "mode": 420, + "sha256": "cf62c92ef0260bd0adca121fea4067aa7ac00675ab4ddccd75e62370165523d6" + }, + "jre/conf/security/policy": { + "directory": true, + "mode": 493 + }, + "jre/conf/security/policy/README.txt": { + "mode": 420, + "sha256": "6da0747334b0fea7592fd92614b2bbc8b126535e129b1fee483774d914e98eb5" + }, + "jre/conf/security/policy/limited": { + "directory": true, + "mode": 493 + }, + "jre/conf/security/policy/limited/default_US_export.policy": { + "mode": 420, + "sha256": "758b930a526fc670ab7537f8c26321527050a31f5f42149a2dda623c56a0a1a9" + }, + "jre/conf/security/policy/limited/default_local.policy": { + "mode": 420, + "sha256": "2b2627548e61316150d47ffc3e6cad465ca05b3cccd4785eb7d21aa7baa0f441" + }, + "jre/conf/security/policy/limited/exempt_local.policy": { + "mode": 420, + "sha256": "8c3d7648abcd95a272ce12db870082937f4d7f6878d730d83cb7fbb31eb8b2c9" + }, + "jre/conf/security/policy/unlimited": { + "directory": true, + "mode": 493 + }, + "jre/conf/security/policy/unlimited/default_US_export.policy": { + "mode": 420, + "sha256": "758b930a526fc670ab7537f8c26321527050a31f5f42149a2dda623c56a0a1a9" + }, + "jre/conf/security/policy/unlimited/default_local.policy": { + "mode": 420, + "sha256": "8d8a318e6d90dfd7e26612d2b6385aa704f686ca6134c551f8928418d92b851a" + }, + "jre/conf/sound.properties": { + "mode": 420, + "sha256": "299c2360b6155eb28990ec49cd21753f97e43442fe8fab03e04f3e213df43a66" + }, + "jre/legal": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.base": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.base/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.base/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.base/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.base/aes.md": { + "mode": 292, + "sha256": "45c6d4da48325edfbff3dcf71c704e504c057904435ed23c6d57046d551eb69d" + }, + "jre/legal/java.base/asm.md": { + "mode": 292, + "sha256": "683be15695bd248272d60f5b7fbe5e126a935ea6bf231a624a9aa164733e1d1d" + }, + "jre/legal/java.base/c-libutl.md": { + "mode": 292, + "sha256": "bef40679922d6fdfb7e4ddb223ad6722300f6054ba737bbf6188d60fcec517f9" + }, + "jre/legal/java.base/cldr.md": { + "mode": 292, + "sha256": "19515e14a240e022640e95b61b5095127fa9690755950b4a2b0a02e783e08163" + }, + "jre/legal/java.base/icu.md": { + "mode": 292, + "sha256": "1bf28459c6e0af9f3429f4f8becd1668d6544055f8df240277456bc4b3d8a752" + }, + "jre/legal/java.base/public_suffix.md": { + "mode": 292, + "sha256": "861de1e4992a7d28f1de25c73cd50d26a94779ebd749c0643fecfa56442c3429" + }, + "jre/legal/java.base/siphash.md": { + "mode": 292, + "sha256": "5a792b5a74ad2a5f3d6a7ad8b7a841116e58a772c18bc6e392320a365b222c76" + }, + "jre/legal/java.base/unicode.md": { + "mode": 292, + "sha256": "6f72f10d166b2c2e8a395e03e734c5afc852b59aeca73ced124f6b9c96268d53" + }, + "jre/legal/java.base/zlib.md": { + "mode": 292, + "sha256": "c9c70c36a1f0325e8788771906202d42ba6ab4205caca44ec0f665707e83226b" + }, + "jre/legal/java.compiler": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.compiler/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.compiler/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.compiler/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.datatransfer": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.datatransfer/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.datatransfer/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.datatransfer/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.desktop": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.desktop/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.desktop/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.desktop/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.desktop/colorimaging.md": { + "mode": 292, + "sha256": "04d61e3e8e71dd452ebe52008af5378d9f6640d14578aeb515dc5375973b0189" + }, + "jre/legal/java.desktop/freetype.md": { + "mode": 292, + "sha256": "553e804baecb7de7129d77c7259a6b70037d1893f1c600b41b35201695fb7758" + }, + "jre/legal/java.desktop/giflib.md": { + "mode": 292, + "sha256": "206aab89ec75968e48a6a1ed6df7d8b9bfb7aea12cbe4c9345d93e5d97460a25" + }, + "jre/legal/java.desktop/harfbuzz.md": { + "mode": 292, + "sha256": "5ec7cba7f8e7fc3a089213140aab6fa1b3bd5bf8a91bf8bddb3b8d315fc5f7d0" + }, + "jre/legal/java.desktop/jpeg.md": { + "mode": 292, + "sha256": "c1dfb9719a71ad9f861f8728550542d681e25c8ef40e6393606e6e2a0c1d653a" + }, + "jre/legal/java.desktop/lcms.md": { + "mode": 292, + "sha256": "843f9f6fa9c3f8109fcf4d5fe74caf7f2ae8e9bcd92238e4aff2d428c932b24b" + }, + "jre/legal/java.desktop/libpng.md": { + "mode": 292, + "sha256": "f73afb394792f3aeecd959e89487603677a430e56b44eba596d8b572accfd540" + }, + "jre/legal/java.desktop/mesa3d.md": { + "mode": 292, + "sha256": "63f4e6f75caebbccb95d903fb43e46ac7111b3624d0a34f146b276d7d9e7b152" + }, + "jre/legal/java.desktop/pipewire.md": { + "mode": 292, + "sha256": "56a8fb1652c70ac204d13bb52ca4d678162e7d21a97d10209c2a633de8082de0" + }, + "jre/legal/java.desktop/xwd.md": { + "mode": 292, + "sha256": "1d4ffa93c87f35084b02a7aa90a21084b4019db4fe1003c2e5ce775b4a384f59" + }, + "jre/legal/java.instrument": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.instrument/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.instrument/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.instrument/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.logging": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.logging/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.logging/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.logging/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.management": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.management.rmi": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.management.rmi/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.management.rmi/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.management.rmi/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.management/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.management/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.management/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.naming": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.naming/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.naming/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.naming/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.net.http": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.net.http/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.net.http/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.net.http/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.prefs": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.prefs/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.prefs/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.prefs/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.rmi": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.rmi/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.rmi/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.rmi/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.scripting": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.scripting/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.scripting/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.scripting/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.se": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.se/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.se/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.se/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.security.jgss": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.security.jgss/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.security.jgss/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.security.jgss/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.security.sasl": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.security.sasl/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.security.sasl/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.security.sasl/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.smartcardio": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.smartcardio/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.smartcardio/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.smartcardio/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.smartcardio/pcsclite.md": { + "mode": 292, + "sha256": "b39ce363c281ed36e937f9e6c03311d7dbf0b20d3614dde084130c2a10909692" + }, + "jre/legal/java.sql": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.sql.rowset": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.sql.rowset/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.sql.rowset/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.sql.rowset/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.sql/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.sql/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.sql/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.transaction.xa": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.transaction.xa/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.transaction.xa/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.transaction.xa/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.xml": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.xml.crypto": { + "directory": true, + "mode": 493 + }, + "jre/legal/java.xml.crypto/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.xml.crypto/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.xml.crypto/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.xml.crypto/santuario.md": { + "mode": 292, + "sha256": "d08690d25f660f145c73365d260ed522cd0aace8a0e1396706551b7a1ae8c9a3" + }, + "jre/legal/java.xml/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/java.xml/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/java.xml/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/java.xml/bcel.md": { + "mode": 292, + "sha256": "cd578186ad93de46cca29ec8ace2b11f9da751987f3f140e6095c528596cc8c1" + }, + "jre/legal/java.xml/dom.md": { + "mode": 292, + "sha256": "6686e8877667584a3a7c07344baadca1a03e29f677162d87c3c0811e990d1148" + }, + "jre/legal/java.xml/jcup.md": { + "mode": 292, + "sha256": "8d5dcfdf50455a3c34c753a98f21e953248af200415a9084e3f102cb6c43b8bf" + }, + "jre/legal/java.xml/xalan.md": { + "mode": 292, + "sha256": "c27eb875da4be683d4d7422be986e5e30f636ede31958ff1d39f9cd6109e7a00" + }, + "jre/legal/java.xml/xerces.md": { + "mode": 292, + "sha256": "4a6bf6b367193ee68681cb2d9fed30ffc5d62dd2d477bd62e0271707d71b3244" + }, + "jre/legal/jdk.accessibility": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.accessibility/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.accessibility/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.accessibility/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.charsets": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.charsets/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.charsets/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.charsets/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.crypto.cryptoki": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.crypto.cryptoki/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.crypto.cryptoki/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.crypto.cryptoki/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.crypto.cryptoki/pkcs11cryptotoken.md": { + "mode": 292, + "sha256": "1f36ff1342a581142c858f90064e20633d43529ac82adb85345bd902a14e18b2" + }, + "jre/legal/jdk.crypto.cryptoki/pkcs11wrapper.md": { + "mode": 292, + "sha256": "371974b1fca3744a3892c7ee1fcc593b8b4281fc218f4cafd2f709e9df5fd81d" + }, + "jre/legal/jdk.crypto.ec": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.crypto.ec/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.crypto.ec/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.crypto.ec/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.dynalink": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.dynalink/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.dynalink/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.dynalink/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.dynalink/dynalink.md": { + "mode": 292, + "sha256": "17312591cabee3ef6c34ed8897d92e4e361ba9cea41ec00dcd61a322a8fc2cdb" + }, + "jre/legal/jdk.httpserver": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.httpserver/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.httpserver/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.httpserver/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.incubator.vector": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.incubator.vector/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.incubator.vector/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.incubator.vector/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.internal.vm.ci": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.internal.vm.ci/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.internal.vm.ci/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.internal.vm.ci/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.internal.vm.compiler": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.internal.vm.compiler.management": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.internal.vm.compiler.management/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.internal.vm.compiler.management/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.internal.vm.compiler.management/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.internal.vm.compiler/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.internal.vm.compiler/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.internal.vm.compiler/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.jdwp.agent": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.jdwp.agent/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.jdwp.agent/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.jdwp.agent/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.jfr": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.jfr/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.jfr/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.jfr/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.jsobject": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.jsobject/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.jsobject/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.jsobject/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.localedata": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.localedata/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.localedata/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.localedata/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.localedata/cldr.md": { + "mode": 292, + "sha256": "19515e14a240e022640e95b61b5095127fa9690755950b4a2b0a02e783e08163" + }, + "jre/legal/jdk.localedata/thaidict.md": { + "mode": 292, + "sha256": "c326144a2351c9608fa708b5d7d3c5a3da03e82b66479b128e9db4969539824a" + }, + "jre/legal/jdk.management": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.management.agent": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.management.agent/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.management.agent/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.management.agent/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.management.jfr": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.management.jfr/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.management.jfr/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.management.jfr/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.management/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.management/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.management/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.naming.dns": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.naming.dns/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.naming.dns/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.naming.dns/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.naming.rmi": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.naming.rmi/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.naming.rmi/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.naming.rmi/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.net": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.net/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.net/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.net/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.nio.mapmode": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.nio.mapmode/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.nio.mapmode/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.nio.mapmode/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.sctp": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.sctp/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.sctp/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.sctp/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.security.auth": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.security.auth/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.security.auth/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.security.auth/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.security.jgss": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.security.jgss/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.security.jgss/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.security.jgss/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.unsupported": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.unsupported/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.unsupported/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.unsupported/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.xml.dom": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.xml.dom/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.xml.dom/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.xml.dom/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/legal/jdk.zipfs": { + "directory": true, + "mode": 493 + }, + "jre/legal/jdk.zipfs/ADDITIONAL_LICENSE_INFO": { + "mode": 292, + "sha256": "a69bce275ba7a3570af6579cb0f55682cd75fedfcd49e0e8e9022270c447c916" + }, + "jre/legal/jdk.zipfs/ASSEMBLY_EXCEPTION": { + "mode": 292, + "sha256": "75292f03bf23d3db7c985aecc191029b93883200721ed23ed34a2e601463df33" + }, + "jre/legal/jdk.zipfs/LICENSE": { + "mode": 292, + "sha256": "4b9abebc4338048a7c2dc184e9f800deb349366bdf28eb23c2677a77b4c87726" + }, + "jre/lib": { + "directory": true, + "mode": 493 + }, + "jre/lib/classlist": { + "mode": 420, + "sha256": "01b9c997aa616618b9cd713dda43b66fbcf5f25976cfe12240415d0641db801a" + }, + "jre/lib/fontconfig.bfc": { + "mode": 420, + "sha256": "5409952a218126c5c11cf0e4963665711aeeab50f5648264d70866fca705a950" + }, + "jre/lib/fontconfig.properties.src": { + "mode": 420, + "sha256": "c8d20a4a39240665252306bdd3dbc8d71e1e246c8cd8c7985b32e4101658a148" + }, + "jre/lib/jfr": { + "directory": true, + "mode": 493 + }, + "jre/lib/jfr/default.jfc": { + "mode": 420, + "sha256": "4fed4b508c5f1b69578293df84f94a63752f86b4259d97e12884e3b2ec56ef50" + }, + "jre/lib/jfr/profile.jfc": { + "mode": 420, + "sha256": "485fb90dbecee9a950c45247464351162b1eb0c35387fbb929f002c600807251" + }, + "jre/lib/jrt-fs.jar": { + "mode": 420, + "sha256": "45fc41e80103af6ac9e9dd1403c9228271b77a9719f262e96a800486b9646952" + }, + "jre/lib/jspawnhelper": { + "mode": 493, + "sha256": "16feacbfe3ef61c58067ef1f5ca0608b073e3fa779be464e3361da08dd2e9ffe" + }, + "jre/lib/jvm.cfg": { + "mode": 420, + "sha256": "aa9efb969444c1484e29adecab55a122458090616e766b2f1230ef05bc3867e0" + }, + "jre/lib/libawt.dylib": { + "mode": 420, + "sha256": "0209133afcc1e9a52dec1a6762d9996e4bf01584d093011c7ce7ebda9c1368f5" + }, + "jre/lib/libawt_lwawt.dylib": { + "mode": 420, + "sha256": "b19d0c418be5a240df513b22524e2e6faa3279227630e554f2c4e50691fee656" + }, + "jre/lib/libdt_socket.dylib": { + "mode": 420, + "sha256": "616c53abe89aefb69160964fcc8fae77e87d3387db1f5a0d275be628fa71a11f" + }, + "jre/lib/libextnet.dylib": { + "mode": 420, + "sha256": "9fd80625d872c6ad48eb0ea42c2594dc53fc6a1998c1f2eb11611d565c8cb181" + }, + "jre/lib/libfontmanager.dylib": { + "mode": 420, + "sha256": "333c72c46c10c81efd2e57467c47ba6bffc3c2698c5cdbac6ee5157517893378" + }, + "jre/lib/libfreetype.dylib": { + "mode": 420, + "sha256": "c4d6b43539785d8522459bf67f2808f14f6c4361ee25a34c7e218a96e54a3a0c" + }, + "jre/lib/libinstrument.dylib": { + "mode": 420, + "sha256": "e4a474c3a92c8da215a1dbdf1bdf8eb7c9850fa9587230d27a99b7374d4863f2" + }, + "jre/lib/libj2gss.dylib": { + "mode": 420, + "sha256": "277488fa07b59692902c5102b3f18bcbcef883a21d156718a621dd3586b6c733" + }, + "jre/lib/libj2pcsc.dylib": { + "mode": 420, + "sha256": "145419ebea46f8982ce0605da082b480ee946e2dcb7f2d56b406ae1c72ec428f" + }, + "jre/lib/libj2pkcs11.dylib": { + "mode": 420, + "sha256": "f741fdca41f808a8e5239c99e846009a9f1f476041dc85314fe46ff9f14552b0" + }, + "jre/lib/libjaas.dylib": { + "mode": 420, + "sha256": "bd5cb0642aa7ad86162333215ab395997039fed8e0b4d0aed1f38f50189c8a3f" + }, + "jre/lib/libjava.dylib": { + "mode": 420, + "sha256": "30ed8d3f55fce957cd51ce59783115ee3574283c34b52e1b19c4c5382337722d" + }, + "jre/lib/libjavajpeg.dylib": { + "mode": 420, + "sha256": "0cdad00d7ee91725a6976ae18dfbe138ee06521dceefe6b7be73d6254b5c74b0" + }, + "jre/lib/libjawt.dylib": { + "mode": 420, + "sha256": "1b0ab5d66a0e69f204456c4c984fbcbe419f8b17a9c2049e4cc9f1c06e96ec63" + }, + "jre/lib/libjdwp.dylib": { + "mode": 420, + "sha256": "d756a720d09af729180674301750d13d592a166552bca1f078095efbd1089f67" + }, + "jre/lib/libjimage.dylib": { + "mode": 420, + "sha256": "55a40284a581585d3cf8ecea9b82ea487ae0c31e0ba1f735a10954dfd0f7f50f" + }, + "jre/lib/libjli.dylib": { + "mode": 420, + "sha256": "10eaa148c303ead9a749bb3217534023f22af6a2fbac616c9c4439b322d7f34a" + }, + "jre/lib/libjsig.dylib": { + "mode": 420, + "sha256": "78fcd0c817a4ebc5a65393c82059b72d72a99072071a3216af13d65493db521a" + }, + "jre/lib/libjsound.dylib": { + "mode": 420, + "sha256": "d3e7f5451524b9baeedeb67a547870b3dd0d577ffad5f25cbe404b4db15d6119" + }, + "jre/lib/liblcms.dylib": { + "mode": 420, + "sha256": "121f2f3e90697b2c5afca9320d86a9a3b87fae078c36f5a504b39ca8dc028023" + }, + "jre/lib/libmanagement.dylib": { + "mode": 420, + "sha256": "202b22a451d59b872785600490c07c70b661a0792aa17aa87153e900ee040a68" + }, + "jre/lib/libmanagement_agent.dylib": { + "mode": 420, + "sha256": "9d7d55ba15791761d916b8a82783ccccf5751477a1a3f4bac8238658316a5e0d" + }, + "jre/lib/libmanagement_ext.dylib": { + "mode": 420, + "sha256": "820e33e6dab905b65a4efee4fefe69a0a8bfe72ac83d008728073302962756ce" + }, + "jre/lib/libmlib_image.dylib": { + "mode": 420, + "sha256": "ae7063852da21fcce99f05c136d3ef6588ec39fa507c30c7530d26066f7acb8f" + }, + "jre/lib/libnet.dylib": { + "mode": 420, + "sha256": "faf87c1231613754fb494756d8a9ed621b89a6d8a550cfcbe705547d96cb28ce" + }, + "jre/lib/libnio.dylib": { + "mode": 420, + "sha256": "e723dc5463efe7426653e404228ebd0bd0fe89e9bc1dfc67c4d1e6ca6bf56a77" + }, + "jre/lib/libosx.dylib": { + "mode": 420, + "sha256": "803d5eea4ac7cbc8cae0ccabd12762f89968fe232ddfe7c1a96bc75734e7b78e" + }, + "jre/lib/libosxapp.dylib": { + "mode": 420, + "sha256": "b83a2aff33398ba454df761a5253ea81fa87b81417fb8039e9e7d6215de8b575" + }, + "jre/lib/libosxkrb5.dylib": { + "mode": 420, + "sha256": "7837c48e9e209ecfeff61585bfb5378b0dbe485ed649929f554061e667a61a1e" + }, + "jre/lib/libosxsecurity.dylib": { + "mode": 420, + "sha256": "2d3d93ac8d5d3793379567e9a1924df2b18ba781325be032a66b31a2906f910f" + }, + "jre/lib/libosxui.dylib": { + "mode": 420, + "sha256": "03ac7a0d0b3118c5aa5092573092776368dbd17681c9c81aff2ddc459e9e79fd" + }, + "jre/lib/libprefs.dylib": { + "mode": 420, + "sha256": "25470f88ca4f06ada7d92cc926eacc3048162e9c9630711c807e8c490af21fd9" + }, + "jre/lib/librmi.dylib": { + "mode": 420, + "sha256": "aa96921fec715c02936dc67326b272ff4307bf9f67bc24d05597f5e5ef4dc85c" + }, + "jre/lib/libsplashscreen.dylib": { + "mode": 420, + "sha256": "18bc46fe4dc47017dcf4dd5322843f8c74bce2f4d6de740d54ba892a3955e3f8" + }, + "jre/lib/libsyslookup.dylib": { + "mode": 420, + "sha256": "85aecafd0c15353cca1d780b1e624b6044d0c4e9b14b5e36e50d364832aa4541" + }, + "jre/lib/libverify.dylib": { + "mode": 420, + "sha256": "5c6a9c3006b9dbee4f6da465b4b8fc18a9e468c46e1d4c646a9175ff12c5d483" + }, + "jre/lib/libzip.dylib": { + "mode": 420, + "sha256": "46e21dc95a453fd5c14050ebf75b1e7a23a75ab3964fca43d69d9dfc1d35df45" + }, + "jre/lib/modules": { + "mode": 420, + "sha256": "46e18cac03a6cc80b86fdcec7c5e5121bb36bc1b711ebfba7450b5501198621a" + }, + "jre/lib/psfont.properties.ja": { + "mode": 420, + "sha256": "5a4bd51b969bf187ff86d94f4a71fdfbfa602762975fa3c73d264b4575f7c78f" + }, + "jre/lib/psfontj2d.properties": { + "mode": 420, + "sha256": "780c565d5af3ee6f68b887b75c041cdf46a0592f67012f12eeb691283e92630a" + }, + "jre/lib/security": { + "directory": true, + "mode": 493 + }, + "jre/lib/security/blocked.certs": { + "mode": 420, + "sha256": "96572f243f31c2ef81a6e627542e596f6a9295cff3c7ae095c1b595cb1457ded" + }, + "jre/lib/security/cacerts": { + "mode": 420, + "sha256": "98d34a90fca2688ef5674862d2a484e09e5ca25d2f07c5d654aa28f1581f3419" + }, + "jre/lib/security/default.policy": { + "mode": 420, + "sha256": "2bd418aab30b091b136962f80be7ddf39dcba85f082a558a6993848ae65366ae" + }, + "jre/lib/security/public_suffix_list.dat": { + "mode": 420, + "sha256": "16b42002aa6f83a7763c4cdbd399ecf738460377d447e362884495545afd60a3" + }, + "jre/lib/server": { + "directory": true, + "mode": 493 + }, + "jre/lib/server/classes.jsa": { + "mode": 292, + "sha256": "b5f4b20ab562230df35499fd0262585411681cc76e8b3e0e0a593b59dbcbc42b" + }, + "jre/lib/server/classes_nocoops.jsa": { + "mode": 292, + "sha256": "1bcc5d073775593fa55c11e42f56e423d02364ed62a340e01e5ec1b83e3e4229" + }, + "jre/lib/server/libjsig.dylib": { + "mode": 420, + "sha256": "78fcd0c817a4ebc5a65393c82059b72d72a99072071a3216af13d65493db521a" + }, + "jre/lib/server/libjvm.dylib": { + "mode": 420, + "sha256": "629b4f2a124e373f7eec14e0bdd3248ae6e817e20e2fb85c881f96a0233bbeec" + }, + "jre/lib/shaders.metallib": { + "mode": 420, + "sha256": "5bba5cc7c25aa23f90ae7b0bf72d4835bfdfd9760b279f7a8f018670a5f77deb" + }, + "jre/lib/tzdb.dat": { + "mode": 420, + "sha256": "5906b44e25a8a730131d7ba1beb5d15af27043f6536f1a169ca8a5d8b8bed8f8" + }, + "jre/release": { + "mode": 420, + "sha256": "25090aa2833d7c13d195480f0cb53ac9c0759bac1cdfa8c249952782dcc014b1" + }, + "lib": { + "directory": true, + "mode": 493 + }, + "lib/.versions": { + "mode": 420, + "sha256": "e6015c91a4bacf9519e3cef5029575eeca164350569f2f7c6662e31d676625be" + }, + "lib/opentaint-project-analyzer.jar": { + "mode": 420, + "sha256": "2ca93b6c33462bdbc23ceccdc5375e1a900682b33371cd906e5214dc7c48f569" + }, + "lib/opentaint-project-auto-builder.jar": { + "mode": 420, + "sha256": "99dafc53acd8148aedebf809160064b297d68bfc0d26c56d18b3bfdbef197b8a" + }, + "lib/rules": { + "directory": true, + "mode": 493 + }, + "lib/rules/go": { + "directory": true, + "mode": 493 + }, + "lib/rules/go/lib": { + "directory": true, + "mode": 493 + }, + "lib/rules/go/lib/cmdi-sinks.yaml": { + "mode": 420, + "sha256": "ca7249462753cd42b9fbd84a02f62860c351080c0b15decee4ec8fa5a92920aa" + }, + "lib/rules/go/lib/http-sources-header-index.yaml": { + "mode": 420, + "sha256": "6b103aa8b154d193e91bd15c7e34574b59e0d9c9ca42a87755232c6f755caf2a" + }, + "lib/rules/go/lib/http-sources-requesturi.yaml": { + "mode": 420, + "sha256": "d43ccd94c9568e5b1f3d624286e792e688329081bfcb24ec1297c8b490591ab7" + }, + "lib/rules/go/lib/http-sources.yaml": { + "mode": 420, + "sha256": "7e62c7007a730d11137752c95ec0210d0e2461b4a652620e387e354247457ce9" + }, + "lib/rules/go/lib/path-sinks.yaml": { + "mode": 420, + "sha256": "ebccf806678a169e29bd77897ba7d0d441ec7e2ae88729692df2f7838730de30" + }, + "lib/rules/go/lib/sql-sinks.yaml": { + "mode": 420, + "sha256": "931b2ab86f59c1a78d789d5eee10fa43f0a7c2d28b41ab9b0a53c4932aaf4974" + }, + "lib/rules/go/lib/ssrf-sinks.yaml": { + "mode": 420, + "sha256": "92484446a8b0871f51fcb67bb2cbc5f909be2a365a7ce3ff56c3af14ac80dbd3" + }, + "lib/rules/go/lib/ssti-sinks.yaml": { + "mode": 420, + "sha256": "dea210bdd21ddbabd1324c6c6799009bf15a772bd56da210fa8c78c473dc34b1" + }, + "lib/rules/go/lib/xss-sinks.yaml": { + "mode": 420, + "sha256": "c26cb84ba8b5bc3611903b03856bf4a5d2da452bb00abd976f0cab571d867d57" + }, + "lib/rules/go/security": { + "directory": true, + "mode": 493 + }, + "lib/rules/go/security/cmdinj.yaml": { + "mode": 420, + "sha256": "67e569171866a5e1a08350e1b48facab3c56f860dcccbed62f78431642522cc1" + }, + "lib/rules/go/security/path-traversal.yaml": { + "mode": 420, + "sha256": "8caf0137ede8fa50ed5c2eef816e0a26d691eda13460bcb86b2cd5cdf18a29d5" + }, + "lib/rules/go/security/sql-injection.yaml": { + "mode": 420, + "sha256": "8acb09af826ad43ba6ccdbfb5e336d3bf564fb89110e713a02be662e8b1317f1" + }, + "lib/rules/go/security/ssrf.yaml": { + "mode": 420, + "sha256": "9f27da42b04e175bb421f81f58768c44bf0473307fadc324e28d8b27ad16af44" + }, + "lib/rules/go/security/ssti.yaml": { + "mode": 420, + "sha256": "592cefb4c585e3b820750b8123c9cfcf8cf4fb0bfd1a0716d65624842381477f" + }, + "lib/rules/go/security/trust-boundary.yaml": { + "mode": 420, + "sha256": "f129579635d7b108c2f870dfe981541f7366b29922500832c75a10462be16ac6" + }, + "lib/rules/go/security/weak-crypto.yaml": { + "mode": 420, + "sha256": "7a9b0ec4b43b254319b24ce8192a1543b50e431e90615c4d773fc7f8a7f99f38" + }, + "lib/rules/go/security/weak-hash.yaml": { + "mode": 420, + "sha256": "022cd6ebf641632af284a3f1afb6acbcf22ae88ba4043d590bdbed89f6aa52d9" + }, + "lib/rules/go/security/weak-random.yaml": { + "mode": 420, + "sha256": "9fcfc5e4d57ff2d020f433ef2205e7016bbc6b270514d4a75c4fd9b75811966d" + }, + "lib/rules/go/security/xss.yaml": { + "mode": 420, + "sha256": "7598386eb5811a411a62edde1d8350838c97410cc95ad6a2d5b2e8668b0107b5" + }, + "lib/rules/java": { + "directory": true, + "mode": 493 + }, + "lib/rules/java/lib": { + "directory": true, + "mode": 493 + }, + "lib/rules/java/lib/generic": { + "directory": true, + "mode": 493 + }, + "lib/rules/java/lib/generic/code-injection-sinks.yaml": { + "mode": 420, + "sha256": "1101ec32d1a6722397a1c53895d952e459427ea2cc5f7ab18987d66b01422636" + }, + "lib/rules/java/lib/generic/command-injection-sinks.yaml": { + "mode": 420, + "sha256": "67f37b222626eae40e13f81a1ec6d4471fbe47168baeab243f6838f6057349f2" + }, + "lib/rules/java/lib/generic/data-query-injection-sinks.yaml": { + "mode": 420, + "sha256": "e8e63caccfcca5488655d9194f4f76eb4cef1c6ed2dd80393063f5311bfeb615" + }, + "lib/rules/java/lib/generic/http-response-splitting-sinks.yaml": { + "mode": 420, + "sha256": "76dd49a15d0b0ac6c58ee056384bec4e629574b983f74dc2bd62e02b03110772" + }, + "lib/rules/java/lib/generic/ldap-injection-sinks.yaml": { + "mode": 420, + "sha256": "e5e068218413861108eb80c2524d6176990be2cc103750b69dfed832df0a9b40" + }, + "lib/rules/java/lib/generic/logging-sinks.yaml": { + "mode": 420, + "sha256": "a7554cb818d49214d54d87ae9cfffa3ed68fea45a0b2b7165efc52ba3f8550ae" + }, + "lib/rules/java/lib/generic/path-traversal-sinks.yaml": { + "mode": 420, + "sha256": "29acb711e906924ae3be42dc244b2128301b8d687786d68857f37796e38f17d5" + }, + "lib/rules/java/lib/generic/seam-untrusted-data-source.yaml": { + "mode": 420, + "sha256": "12820f4420fb27227b58bef4ac5b36698fb0f12db97cd2e56e8056594dfc796b" + }, + "lib/rules/java/lib/generic/servlet-response-injection-sinks.yaml": { + "mode": 420, + "sha256": "bfc7f1a9b6b7c9424b99487e9c842d790596f746cf91989159eb364d2256e578" + }, + "lib/rules/java/lib/generic/servlet-untrusted-data-source.yaml": { + "mode": 420, + "sha256": "00642a5e61861ef23a83082ab7a875bbc88620dcedb9bfa32797bf84914d1664" + }, + "lib/rules/java/lib/generic/servlet-unvalidated-redirect-sinks.yaml": { + "mode": 420, + "sha256": "7c7b293c6fbd0c536b38a5b5b2f0d9c1936727502150ce41a133b30d8a388e9d" + }, + "lib/rules/java/lib/generic/servlet-xss-html-response-sinks.yaml": { + "mode": 420, + "sha256": "0f78b1c1b33e3e66a005e223998077f67c9aed2cb7d65b7d85d3e34de621bc1c" + }, + "lib/rules/java/lib/generic/smtp-injection-sinks.yaml": { + "mode": 420, + "sha256": "e694e2a6981d2e48d57b54016edcb53883fd860f746e0ab1dec71412e0a024e6" + }, + "lib/rules/java/lib/generic/ssrf-sinks.yaml": { + "mode": 420, + "sha256": "cb8f90f79330c87936febeff68b68e84c90517279bf30fdd1b223f480dc41d09" + }, + "lib/rules/java/lib/generic/template-injection-sinks.yaml": { + "mode": 420, + "sha256": "a68af1682c2894035b7725dade0d227996a85cc34dccc13cde402e7146f7d3db" + }, + "lib/rules/java/lib/generic/unsafe-deserialization-sinks.yaml": { + "mode": 420, + "sha256": "5114624ff344be989434e6bec58e29b4decc92ad5d0916ac509872ec0398dc80" + }, + "lib/rules/java/lib/generic/unsafe-reflection.yaml": { + "mode": 420, + "sha256": "c745378c5cd944fc4c924f8e06a58da49e0a5cdda91c07c02125d974d78561e2" + }, + "lib/rules/java/lib/generic/xxe-sinks.yaml": { + "mode": 420, + "sha256": "cd5545038cde3dfae636a749f80afe6a60f52a1550e4c0760301a4cea9fbddac" + }, + "lib/rules/java/lib/spring": { + "directory": true, + "mode": 493 + }, + "lib/rules/java/lib/spring/jdbc-sqli-sinks.yaml": { + "mode": 420, + "sha256": "aacd5f40f39fd013ab54d45f5ee8ab2419c8c253c93f9b91efe4c7ffa19d4428" + }, + "lib/rules/java/lib/spring/spel-injection-sinks.yaml": { + "mode": 420, + "sha256": "c31dd8b324cf087edcba8fc56ebd7b58a12d10f2d5557fa4a545dc85cc3ede37" + }, + "lib/rules/java/lib/spring/spring-response-injection-sinks.yaml": { + "mode": 420, + "sha256": "e27d52882b44101acd171b176e5695e708791e16eff3382bef9c952750d22fa8" + }, + "lib/rules/java/lib/spring/spring-xss-html-response-sinks.yaml": { + "mode": 420, + "sha256": "d800cbf481144bb2fd88302de6c8efa1cf29dcd79aef893c07dbbaaa246ba4d9" + }, + "lib/rules/java/lib/spring/untrusted-data-source.yaml": { + "mode": 420, + "sha256": "b5ae0afb99a08cc5027e07719e3ea3b50a9298ef63015770ab9c7181fe8ca939" + }, + "lib/rules/java/lib/spring/untrusted-path-source.yaml": { + "mode": 420, + "sha256": "c2a843351290642aaa786aeba51526e657f603c45ddd0f02b90693f91221c3e9" + }, + "lib/rules/java/lib/spring/unvalidated-redirect-sinks.yaml": { + "mode": 420, + "sha256": "5083debd67545ed89c3f289454e15ea20cd67d337d83746cade9370264b864ac" + }, + "lib/rules/java/security": { + "directory": true, + "mode": 493 + }, + "lib/rules/java/security/code-injection.yaml": { + "mode": 420, + "sha256": "73077427a94629495988060d0cd0b1a26e55782c51b2581f09d4fefcb8165b0a" + }, + "lib/rules/java/security/command-injection.yaml": { + "mode": 420, + "sha256": "804a75ae2f8b883c07c4cd9a404bcecf75d9cdcbb4a9a3c708eabb03548adcd1" + }, + "lib/rules/java/security/crlf-injection.yaml": { + "mode": 420, + "sha256": "0a109f14d723786bcce630e7bf783aa7fd803caa68db3c40f6d27689bcbd9deb" + }, + "lib/rules/java/security/crypto.yaml": { + "mode": 420, + "sha256": "79648c02b3d67d8c972707f8550c9b48018260ff42b457f7d7250006e1f1d19f" + }, + "lib/rules/java/security/csrf.yaml": { + "mode": 420, + "sha256": "4a8cb0db5cb44f84a4f8d5a0e01aa913eee96f0309658a3b8ec7c2b9b23f3c03" + }, + "lib/rules/java/security/data-query-injection.yaml": { + "mode": 420, + "sha256": "bd38c6af52d5c276b69e7d23a4ff14e49c8c86487d6e232a2a18f2cefc57669a" + }, + "lib/rules/java/security/external-configuration-control.yaml": { + "mode": 420, + "sha256": "0fb31d16d5827b564ebc8df4f297c266697be0a52cea470b8e0df248de03895f" + }, + "lib/rules/java/security/hardcoded-credentials.yaml": { + "mode": 420, + "sha256": "ff8a876daf7e08c3842b09278e68be7cb4dac7aff26c550e6c6f011823a28110" + }, + "lib/rules/java/security/insecure-design.yaml": { + "mode": 420, + "sha256": "ce7c8fb6e32bfa085351db4bb3ddff3f8995035c40ce61587bf4a6bcc0ccdf3b" + }, + "lib/rules/java/security/ldap.yaml": { + "mode": 420, + "sha256": "f39361cf23a5e986602e10cf7ce7199eb9e3cd278ddbc8ecb7d87bda35bc0171" + }, + "lib/rules/java/security/log-injection.yaml": { + "mode": 420, + "sha256": "b5eb915a3828660830e08c1c6bb35ba593acc712129567bee0e03e779e0b8138" + }, + "lib/rules/java/security/path-traversal.yaml": { + "mode": 420, + "sha256": "b071603f0fb92b7903e2f4649f43298c5d2a3cd7d7a01d692505fc0e67b35e2d" + }, + "lib/rules/java/security/permissions.yaml": { + "mode": 420, + "sha256": "4578204fe67bfa7fcdbb96058109acc3e8b9025d15fb7fc14c03138545f36afd" + }, + "lib/rules/java/security/sensitive-data-exposure.yaml": { + "mode": 420, + "sha256": "8c35c3c7ab65095c290259f953e37060c9289a004a673a447cf16fc3b90a3b55" + }, + "lib/rules/java/security/sqli.yaml": { + "mode": 420, + "sha256": "2a1ce48f69f717296a01eae6fecc9d2dab1a955d6114a374486217863911f5e0" + }, + "lib/rules/java/security/ssrf.yaml": { + "mode": 420, + "sha256": "9b5204a03592aadd8405467cd79492eb255aab8f1d033f7e5ddbf62a44f8fe9a" + }, + "lib/rules/java/security/strings.yaml": { + "mode": 420, + "sha256": "5867ddb9a4fab026f2bdd56b8efe98ca8d333a3752282d201f2dc99b702f791e" + }, + "lib/rules/java/security/unsafe-deserialization.yaml": { + "mode": 420, + "sha256": "31e519dabf260d520593463c606ef3d34b5b87c8a501c7d4c90daf14364547d3" + }, + "lib/rules/java/security/unvalidated-redirect.yaml": { + "mode": 420, + "sha256": "de2496a8c9844aa052825301912f7ab9bb56e679ac2f9dfa18619765a9f6ebf6" + }, + "lib/rules/java/security/weak-authentication.yaml": { + "mode": 420, + "sha256": "a639264fe629eb8712d69864d70987eea5825c832d1ee227b74354f1b166b345" + }, + "lib/rules/java/security/xss.yaml": { + "mode": 420, + "sha256": "19bc0b9483c88e46fb68315dad42ef2bd5906821e08ef7143f0e80ffddbbe9c8" + }, + "lib/rules/java/security/xxe.yaml": { + "mode": 420, + "sha256": "5f77b067d65f0d90f4c13cc9ca20b9f7ce553a009339568a18476e2508618d53" + }, + "opentaint": { + "mode": 493, + "sha256": "6de7bc497c84d8b0e02b721ba10e61c4bf9f99388763df793a92ad19dbb6d8f8" + } + }, + "external_files": {}, + "root": "/private/tmp/dfb-v090-opentaint-full", + "schema": "release-runtime-tree/v1" +} diff --git a/scripts/check-v090-execution-contract.py b/scripts/check-v090-execution-contract.py index 8f3bfc5fa..df14b3b0d 100644 --- a/scripts/check-v090-execution-contract.py +++ b/scripts/check-v090-execution-contract.py @@ -85,8 +85,23 @@ def validate(root=ROOT): require(budget['serial_analyzers'] == 1 and budget['protected_reserve_gib'] >= 40, 'resource boundary weakened') require(budget['minimum_launch_free_gib'] >= sum(budget[k] for k in ['protected_reserve_gib', 'scratch_gib', 'proposed_total_retention_ceiling_gib', 'proposed_working_copy_and_staging_gib']), 'unbudgeted launch capacity') require(len(plan['controls']) == 30 and len({c['id'] for c in plan['controls']}) == 30, 'existing control inventory changed') + inventory = json.loads(bound(root, plan['control_inventory']).read_text()) + controls = inventory['controls'] + controls_by_id = {c['id']: c for c in controls} + require(len(controls_by_id) == len(controls), 'duplicate control identity') + legacy_ids = {c['id'] for c in plan['controls']} + require(legacy_ids.issubset(controls_by_id), 'historical control omitted') + supplemental = {'probe-python-modeling-load-bearing', 'probe-warm-observability', 'probe-opentaint-product-v090'} + require(set(controls_by_id) == legacy_ids | supplemental, 'supplemental control membership changed') + require(set(plan['control_execution_roots']) == set(controls_by_id), 'control root membership changed') + require(len(set(plan['control_execution_roots'].values())) == len(controls), 'control roots must be isolated') + for control in controls: + require(control.get('maximum_attempts') == 2, 'control retry bound changed') + expected_repeats = {'warm': 2, 'overhead': 3}.get(control['stage'], 1) + require(control.get('measurement_repeats') == expected_repeats, 'control measurement repeats changed') + require(control.get('repeat_mechanism') == ('inside-harness-command' if control['stage'] in ('warm', 'overhead') else 'single-control-series'), 'control repeat mechanism changed') require(plan['unresolved'], 'planning blocker list missing') - return {'groups': len(groups), 'controls': len(plan['controls']), 'executable': False} + return {'groups': len(groups), 'controls': len(controls), 'executable': False} if __name__ == '__main__': diff --git a/scripts/execute-release-controls-v090.py b/scripts/execute-release-controls-v090.py new file mode 100644 index 000000000..de12a013f --- /dev/null +++ b/scripts/execute-release-controls-v090.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Prepare and run isolated controls serially; never retry an incomplete control.""" +from __future__ import annotations +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys + +CONTRACT = 'reports/releases/v0.9.0/execution-v1/contract.json' + + +def reviewed_inventory(source, plan_commit, contract_path): + if not re.fullmatch(r'[0-9a-f]{40}', plan_commit): + raise ValueError('exact reviewed plan commit required') + def read(relative): + path = Path(relative) + if path.is_absolute() or '..' in path.parts: + raise ValueError('unsafe reviewed input path') + return subprocess.check_output(['git', '-C', str(source), 'show', plan_commit + ':' + relative]) + raw = read(contract_path) + contract = json.loads(raw) + reference = contract['control_inventory'] + inventory_raw = read(reference['path']) + if hashlib.sha256(inventory_raw).hexdigest() != reference['sha256']: + raise ValueError('control inventory differs from reviewed commit') + inventory = json.loads(inventory_raw) + controls = inventory['controls'] + ids = [c['id'] for c in controls] + if len(ids) != len(set(ids)) or set(ids) != set(contract['control_execution_roots']): + raise ValueError('control membership and designated roots differ') + return contract, controls + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--source', type=Path, required=True) + parser.add_argument('--plan-commit', required=True) + parser.add_argument('--contract', default=CONTRACT) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + source = args.source.resolve() + contract, controls = reviewed_inventory(source, args.plan_commit, args.contract) + if not args.execute: + print(json.dumps({'mode': 'list-only', 'controls': [c['id'] for c in controls], + 'execution_authorized': contract.get('execution_authorized') is True}, indent=2)) + return + if contract.get('execution_authorized') is not True or contract.get('unresolved'): + raise ValueError('controls have not passed final executable-plan review') + for control in controls: + root = Path(contract['control_execution_roots'][control['id']]) + # Any existing checkout requires explicit receipt review. Automatic + # reconstruction could otherwise discard a failed run or reset attempts. + if root.exists(): + raise ValueError('existing control root requires explicit resume review: ' + str(root)) + subprocess.run([sys.executable, str(source/'scripts/prepare-release-root-v090.py'), + '--source', str(source), '--destination', str(root), + '--plan-commit', args.plan_commit, '--contract', args.contract, + '--control', control['id']], check=True) + subprocess.run([sys.executable, str(root/'scripts/run-release-control-v090.py'), + '--root', str(root), '--contract', args.contract, + '--control', control['id'], '--execute'], check=True) + print(json.dumps({'completed_control': control['id']}), flush=True) + + +if __name__ == '__main__': + main() diff --git a/scripts/prepare-release-root-v090.py b/scripts/prepare-release-root-v090.py index f04bd8b1f..cb0d3359b 100644 --- a/scripts/prepare-release-root-v090.py +++ b/scripts/prepare-release-root-v090.py @@ -103,14 +103,183 @@ def prepare_root(source, destination, plan_commit, contract_path): return receipt +def prepare_control_root(source, destination, plan_commit, contract_path, control_id): + """Prepare a fresh execution checkout for exactly one reviewed control.""" + source = Path(source).resolve() + requested_destination = Path(destination).expanduser().absolute() + if requested_destination.exists() or requested_destination.is_symlink(): + raise ValueError('execution destination already exists; attempts must not be reset') + destination = requested_destination.resolve() + if not SHA.fullmatch(plan_commit): + raise ValueError('exact reviewed plan commit required') + safe_relative(contract_path) + if not contract_path.startswith('reports/releases/v0.9.0/execution-v1/'): + raise ValueError('control contract must be an execution-v1 overlay') + raw_contract = git(source, 'show', plan_commit + ':' + contract_path) + contract = json.loads(raw_contract) + harness = contract.get('harness_commit', '') + if not SHA.fullmatch(harness): + raise ValueError('exact merged harness commit required') + subprocess.run(['git', '-C', str(source), 'merge-base', '--is-ancestor', harness, plan_commit], check=True) + + inventory_ref = contract.get('control_inventory') + if not isinstance(inventory_ref, dict): + raise ValueError('hash-bound control inventory required') + inventory_path = safe_relative(inventory_ref.get('path', '')) + inventory_sha = inventory_ref.get('sha256', '') + if not re.fullmatch(r'[0-9a-f]{64}', inventory_sha): + raise ValueError('hash-bound control inventory required') + if inventory_path.parts[:4] != ('reports', 'releases', 'v0.9.0', 'execution-v1'): + raise ValueError('control inventory must be an execution-v1 overlay') + if str(inventory_path) == contract_path: + raise ValueError('control contract and inventory must be distinct files') + raw_inventory = git(source, 'show', plan_commit + ':' + str(inventory_path)) + if hashlib.sha256(raw_inventory).hexdigest() != inventory_sha: + raise ValueError('control inventory digest mismatch') + inventory = json.loads(raw_inventory) + controls = inventory.get('controls') + if not isinstance(controls, list) or not controls: + raise ValueError('control inventory must contain controls') + control_by_id = {} + for control in controls: + if not isinstance(control, dict) or not isinstance(control.get('id'), str) or not control['id']: + raise ValueError('invalid control inventory entry') + if control['id'] in control_by_id: + raise ValueError('duplicate control inventory id: ' + control['id']) + control_by_id[control['id']] = control + if not isinstance(control_id, str) or control_id not in control_by_id: + raise ValueError('control id is not in the reviewed inventory: ' + str(control_id)) + + roots = contract.get('control_execution_roots') + if not isinstance(roots, dict) or set(roots) != set(control_by_id): + raise ValueError('every control must designate one execution root') + normalized_roots = {} + for identity, root in roots.items(): + if not isinstance(root, str) or not root: + raise ValueError('invalid designated execution root for control: ' + identity) + root_path = Path(root).expanduser() + if not root_path.is_absolute(): + raise ValueError('designated control execution roots must be absolute') + normalized_roots[identity] = str(root_path.resolve()) + if len(set(normalized_roots.values())) != len(normalized_roots): + raise ValueError('control execution roots must be unique') + root_paths = [Path(root) for root in normalized_roots.values()] + for index, root in enumerate(root_paths): + if any(root in other.parents or other in root.parents for other in root_paths[index + 1:]): + raise ValueError('control execution roots must not overlap') + if normalized_roots[control_id] != str(destination): + raise ValueError('destination does not match the control designated execution root') + + # All plan-bound identities must match their reviewed digest. Mutable + # overlays are restricted to execution-v1; all other inputs must be byte + # identical to the exact harness checkout. + identities = contract.get('input_identities') + if not isinstance(identities, dict): + raise ValueError('reviewed input identities required') + overlay = {contract_path: raw_contract, str(inventory_path): raw_inventory} + bound_paths = set(overlay) + for relative, digest in identities.items(): + safe_relative(relative) + data = git(source, 'show', plan_commit + ':' + relative) + if hashlib.sha256(data).hexdigest() != digest: + raise ValueError('reviewed input digest mismatch: ' + relative) + if not relative.startswith('reports/releases/v0.9.0/execution-v1/'): + original = git(source, 'show', harness + ':' + relative) + if original != data: + raise ValueError('reviewed overlay changes immutable harness input: ' + relative) + overlay[relative] = data + bound_paths.add(relative) + + # Scripts named by the inventory are identities too. Check all registered + # control scripts so the reviewed inventory cannot smuggle a plan-only + # script into any isolated control run. + for control in controls: + script_identities = control.get('script_identity', []) + if not isinstance(script_identities, list): + raise ValueError('invalid control script identities: ' + control['id']) + for identity in script_identities: + if not isinstance(identity, dict): + raise ValueError('invalid control script identity: ' + control['id']) + relative = identity.get('path', '') + safe_relative(relative) + data = git(source, 'show', plan_commit + ':' + relative) + if hashlib.sha256(data).hexdigest() != identity.get('sha256'): + raise ValueError('control script digest mismatch: ' + relative) + if git(source, 'show', harness + ':' + relative) != data: + raise ValueError('control script differs from exact harness checkout: ' + relative) + bound_paths.add(relative) + + selected = control_by_id[control_id] + selected_roots = selected.get('output_roots') + if (not isinstance(selected_roots, list) or not selected_roots + or any(not isinstance(value, str) or not value for value in selected_roots)): + raise ValueError('control must declare output_roots: ' + control_id) + output_roots = sorted(set(selected_roots), key=lambda value: (-len(PurePosixPath(value).parts), value)) + for relative in output_roots: + path = safe_relative(relative) + if path.parts[0] != 'reports' or len(path.parts) < 2: + raise ValueError('output must be a narrow reports path') + for bound in bound_paths: + other = PurePosixPath(bound) + if path == other or path in other.parents or other in path.parents: + raise ValueError('output overlaps registered input') + + destination.parent.mkdir(parents=True, exist_ok=True) + subprocess.run(['git', '-C', str(source), 'clone', '--shared', '--no-checkout', str(source), str(destination)], check=True, stdout=subprocess.DEVNULL) + subprocess.run(['git', '-C', str(destination), 'checkout', '--detach', harness], check=True, stdout=subprocess.DEVNULL) + + # Inspect every path before deleting any parent output root. In particular, + # do not let a parent output hide a symlink in a nested scratch root. + for relative in output_roots: + current = destination + for part in PurePosixPath(relative).parts: + current /= part + if current.is_symlink(): + raise ValueError('symlink in isolated output path') + removed = [] + for relative in output_roots: + path = destination / relative + if path.is_dir(): + shutil.rmtree(path) + removed.append(relative) + elif path.exists(): + path.unlink() + removed.append(relative) + + for relative, data in overlay.items(): + target = destination / relative + if not target.resolve().is_relative_to(destination): + raise ValueError('overlay escapes isolated root') + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(data) + receipt = {'schema': 'release-root-preparation/v1', 'source': str(source), + 'execution_root': str(destination), 'harness_commit': harness, + 'plan_commit': plan_commit, 'contract_path': contract_path, + 'contract_sha256': hashlib.sha256(raw_contract).hexdigest(), + 'control_id': control_id, 'control_inventory': str(inventory_path), + 'control_inventory_sha256': inventory_sha, + 'removed_isolated_output_roots': removed, + 'input_identities': identities, + 'source_modified': False, 'automatic_retry': False} + target = destination/'reports/releases/v0.9.0/root-preparation.json' + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(json.dumps(receipt, indent=2)+'\n') + return receipt + + def main(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('--source', type=Path, required=True) p.add_argument('--destination', type=Path, required=True) p.add_argument('--plan-commit', required=True) p.add_argument('--contract', default='reports/releases/v0.9.0/execution-v1/contract.json') + p.add_argument('--control') args = p.parse_args() - print(json.dumps(prepare_root(args.source, args.destination, args.plan_commit, args.contract), sort_keys=True)) + if args.control: + receipt = prepare_control_root(args.source, args.destination, args.plan_commit, args.contract, args.control) + else: + receipt = prepare_root(args.source, args.destination, args.plan_commit, args.contract) + print(json.dumps(receipt, sort_keys=True)) if __name__ == '__main__': diff --git a/scripts/probe-opentaint-product-v090.py b/scripts/probe-opentaint-product-v090.py new file mode 100644 index 000000000..2d7122df0 --- /dev/null +++ b/scripts/probe-opentaint-product-v090.py @@ -0,0 +1,624 @@ +#!/usr/bin/env python3 +"""Capture the v0.9.0 OpenTaint product activation controls, fail closed.""" + +from __future__ import annotations + +import argparse +import datetime as dt +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +import signal +import subprocess +import sys +import time +from typing import Callable, Mapping, Sequence + + +ROOT = Path(__file__).resolve().parents[1] +CONTRACT_REL = Path("reports/releases/v0.9.0/execution-v1/contract.json") +RESTORATION_REL = Path("reports/releases/v0.9.0/execution-v1/opentaint-full-restoration.json") +RUNTIME_TREE_REL = Path("reports/releases/v0.9.0/execution-v1/runtime-trees/opentaint-full.json") +OUTPUT_REL = Path("reports/raw/opentaint-product-v090") +SCRIPT_REL = Path("scripts/probe-opentaint-product-v090.py") +COMMAND_TIMEOUT_SECONDS = 300 +TERM_GRACE_SECONDS = 3 +NATIVE_FIXTURE_COUNT = 12 +MAX_NESTED_COMMANDS = 3 + 2 * (NATIVE_FIXTURE_COUNT + 2) +CLEANUP_ALLOWANCE_SECONDS = 60 +SERVLET_STUBS = { + "jakarta/servlet/http/HttpServlet.java": ( + "package jakarta.servlet.http; public abstract class HttpServlet { " + "protected void doGet(HttpServletRequest req,HttpServletResponse resp) " + "throws java.io.IOException {} }" + ), + "jakarta/servlet/http/HttpServletRequest.java": ( + "package jakarta.servlet.http; public interface HttpServletRequest { " + "String getParameter(String name); }" + ), + "jakarta/servlet/http/HttpServletResponse.java": ( + "package jakarta.servlet.http; public interface HttpServletResponse {}" + ), +} + + +class ProbeError(ValueError): + """A reviewed contract or input identity does not authorize this probe.""" + + +class ProbeInterrupted(BaseException): + """The outer controller interrupted this probe; active child is cleaned up.""" + + +def _sha_bytes(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def _sha_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as source: + for block in iter(lambda: source.read(1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def _read_json(path: Path, description: str) -> tuple[dict, bytes]: + raw = path.read_bytes() + value = json.loads(raw) + if not isinstance(value, dict): + raise ProbeError(f"{description} must be a JSON object") + return value, raw + + +def _rooted(root: Path, value: str | Path, description: str) -> Path: + path = Path(value) + if path.is_absolute(): + return path + if ".." in PurePosixPath(path.as_posix()).parts: + raise ProbeError(f"{description} path escapes the repository root") + return root / path + + +def _require_digest(value: object, description: str) -> str: + if not isinstance(value, str) or len(value) != 64 or any(c not in "0123456789abcdef" for c in value): + raise ProbeError(f"{description} must be a lowercase SHA-256") + return value + + +def _verify_pinned_file(path_text: object, digest_value: object, description: str) -> Path: + if not isinstance(path_text, str) or not Path(path_text).is_absolute(): + raise ProbeError(f"{description} must have an absolute path") + path = Path(path_text) + expected = _require_digest(digest_value, f"{description} SHA-256") + if not path.is_file() or path.is_symlink(): + raise ProbeError(f"{description} is missing or is not a regular file: {path}") + if _sha_file(path) != expected: + raise ProbeError(f"{description} bytes differ from the reviewed v0.9 contract: {path}") + return path + + +def _tree_entries(root: Path) -> dict[str, dict[str, object]]: + entries: dict[str, dict[str, object]] = {} + for directory, names, files in os.walk(root, followlinks=False): + base = Path(directory) + for name in sorted(names + files): + path = base / name + relative = path.relative_to(root).as_posix() + info = path.lstat() + mode = info.st_mode & 0o777 + if path.is_symlink(): + raise ProbeError(f"unexpected symlink in exact OpenTaint runtime tree: {relative}") + if path.is_dir(): + entries[relative] = {"directory": True, "mode": mode} + elif path.is_file(): + entries[relative] = {"mode": mode, "sha256": _sha_file(path)} + else: + raise ProbeError(f"non-regular member in exact OpenTaint runtime tree: {relative}") + return entries + + +def _verify_v090_contract(root: Path, contract_path: Path, probe_script_path: Path) -> dict: + contract, contract_raw = _read_json(contract_path, "v0.9.0 execution contract") + if contract.get("schema") != "release-execution-contract/v1" or contract.get("release") != "v0.9.0": + raise ProbeError("contract is not the v0.9.0 release-execution-contract/v1") + # This check intentionally precedes all output creation and bundle work. + if contract.get("execution_authorized") is not True: + raise ProbeError("reviewed v0.9.0 contract must set execution_authorized to true") + unresolved = contract.get("unresolved") + if not isinstance(unresolved, list) or unresolved: + raise ProbeError("v0.9.0 contract unresolved prerequisites must be cleared") + + tools = contract.get("tools") + if not isinstance(tools, dict): + raise ProbeError("v0.9.0 contract tools must be an object") + wrapper_record = tools.get("opentaint-wrapper") + if not isinstance(wrapper_record, dict) or wrapper_record.get("version") != "0.4.6": + raise ProbeError("contract must register tools.opentaint-wrapper at version 0.4.6") + javac_record = tools.get("javac") + if not isinstance(javac_record, dict): + raise ProbeError("contract must register the javac compiler") + + identities = contract.get("input_identities") + if not isinstance(identities, dict): + raise ProbeError("contract input_identities must bind release inputs") + control_ref = contract.get("control_inventory") + control_rel = "reports/releases/v0.9.0/execution-v1/control-inventory.json" + if not isinstance(control_ref, dict) or control_ref.get("path") != control_rel: + raise ProbeError("contract must bind the v0.9 control inventory") + control_inventory, control_inventory_raw = _read_json(root / control_rel, "v0.9 control inventory") + control_sha = _require_digest(control_ref.get("sha256"), "control inventory SHA-256") + if _sha_bytes(control_inventory_raw) != control_sha or identities.get(control_rel) != control_sha: + raise ProbeError("control inventory differs from its contract-bound SHA-256") + control_rows = [row for row in control_inventory.get("controls", []) + if isinstance(row, dict) and row.get("id") == "probe-opentaint-product-v090"] + if len(control_rows) != 1: + raise ProbeError("control inventory must register this probe exactly once") + control_record = control_rows[0] + if control_record.get("argv") != ["/usr/bin/python3", SCRIPT_REL.as_posix()]: + raise ProbeError("control inventory must bind the exact versioned probe command") + script_rows = control_record.get("script_identity") + if not isinstance(script_rows, list) or len(script_rows) != 1 or script_rows[0].get("path") != SCRIPT_REL.as_posix(): + raise ProbeError("control inventory must bind this probe's script identity") + expected_script_sha = _require_digest(script_rows[0].get("sha256"), "probe script SHA-256") + if not probe_script_path.is_file() or _sha_file(probe_script_path) != expected_script_sha: + raise ProbeError("probe bytes differ from control inventory script_identity") + outer_deadline = control_record.get("deadline_seconds") + if not isinstance(outer_deadline, int) or isinstance(outer_deadline, bool): + raise ProbeError("control inventory must bind a positive integer outer deadline") + required_deadline = MAX_NESTED_COMMANDS * COMMAND_TIMEOUT_SECONDS + CLEANUP_ALLOWANCE_SECONDS + if outer_deadline < required_deadline: + raise ProbeError( + f"outer deadline {outer_deadline}s is below the nested-command bound " + f"{MAX_NESTED_COMMANDS}*{COMMAND_TIMEOUT_SECONDS}s + {CLEANUP_ALLOWANCE_SECONDS}s" + ) + if control_record.get("output_roots") != [ + OUTPUT_REL.as_posix(), "reports/raw/control-scratch/probe-opentaint-product-v090"]: + raise ProbeError("control inventory must declare product output and isolated scratch roots") + control_roots = contract.get("control_execution_roots") + if not isinstance(control_roots, dict) or not isinstance(control_roots.get("probe-opentaint-product-v090"), str): + raise ProbeError("contract must bind the isolated control execution root") + control_env = control_record.get("environment") + if not isinstance(control_env, dict) or any(not isinstance(k, str) or not isinstance(v, str) for k, v in control_env.items()): + raise ProbeError("control inventory must bind an explicit string environment") + expected_tmpdir = Path(control_roots["probe-opentaint-product-v090"]) / "reports/raw/control-scratch/probe-opentaint-product-v090" + if control_env.get("TMPDIR") != str(expected_tmpdir): + raise ProbeError("control TMPDIR must use the recorder-designated isolated scratch root") + if not expected_tmpdir.is_dir(): + raise ProbeError("recorder-created isolated control TMPDIR must already exist") + + restoration_path = root / RESTORATION_REL + restoration, restoration_raw = _read_json(restoration_path, "OpenTaint full-bundle restoration record") + if identities.get(RESTORATION_REL.as_posix()) != _sha_bytes(restoration_raw): + raise ProbeError("restoration record is not bound by contract input_identities") + if (restoration.get("schema") != "runtime-restoration/v1" or + restoration.get("exact_file_membership") is not True or + restoration.get("runtime_executed") is not False or + restoration.get("verified_files") != 329): + raise ProbeError("restoration record lacks exact, unexecuted 329-file verification") + historical_ref = restoration.get("reference") + if not isinstance(historical_ref, dict): + raise ProbeError("restoration record lacks its historical identity reference") + historical_rel = historical_ref.get("path") + if not isinstance(historical_rel, str) or Path(historical_rel).is_absolute() or ".." in Path(historical_rel).parts: + raise ProbeError("historical identity reference must be repository-relative") + historical_path = root / historical_rel + historical, historical_raw = _read_json(historical_path, "historical OpenTaint bundle identity") + historical_sha = _require_digest(historical_ref.get("sha256"), "historical identity reference SHA-256") + if _sha_bytes(historical_raw) != historical_sha: + raise ProbeError("historical OpenTaint identity differs from the restoration reference") + if historical.get("archive_sha256") != restoration.get("archive_sha256"): + raise ProbeError("restored OpenTaint archive identity differs from historical identity") + historical_files = historical.get("files") + if not isinstance(historical_files, list) or len(historical_files) != 329: + # The historical distribution identity is exactly 329 files. + raise ProbeError("historical OpenTaint identity must list exactly 329 files") + historical_by_path: dict[str, str] = {} + for entry in historical_files: + if not isinstance(entry, dict) or not isinstance(entry.get("relative_path"), str): + raise ProbeError("historical OpenTaint file identity has an invalid member") + relative = PurePosixPath(entry["relative_path"]) + if relative.is_absolute() or ".." in relative.parts or relative.as_posix() in historical_by_path: + raise ProbeError("historical OpenTaint identity has an unsafe or duplicate member") + historical_by_path[relative.as_posix()] = _require_digest(entry.get("sha256"), "historical file SHA-256") + if len(historical_by_path) != 329: + raise ProbeError("historical OpenTaint file membership is not exactly 329 files") + + bundle_text = restoration.get("bundle_root") + if not isinstance(bundle_text, str) or not Path(bundle_text).is_absolute(): + raise ProbeError("restoration record must name an absolute bundle root") + bundle = Path(bundle_text) + wrapper = _verify_pinned_file(wrapper_record.get("path"), wrapper_record.get("sha256"), "OpenTaint wrapper") + if wrapper != bundle / "opentaint" or historical_by_path.get("opentaint") != wrapper_record["sha256"]: + raise ProbeError("wrapper path/digest differs from restored and historical bundle identity") + _verify_pinned_file(javac_record.get("path"), javac_record.get("sha256"), "contract javac") + + runtime_refs = contract.get("runtime_trees") + if not isinstance(runtime_refs, list): + raise ProbeError("contract runtime_trees must be a list") + runtime_matches = [ref for ref in runtime_refs if isinstance(ref, dict) and ref.get("path") == RUNTIME_TREE_REL.as_posix()] + if len(runtime_matches) != 1: + raise ProbeError("contract must bind exactly one opentaint-full runtime tree") + runtime_ref = runtime_matches[0] + runtime_path = root / RUNTIME_TREE_REL + runtime_raw = runtime_path.read_bytes() + runtime_sha = _require_digest(runtime_ref.get("sha256"), "opentaint-full runtime tree SHA-256") + if _sha_bytes(runtime_raw) != runtime_sha: + raise ProbeError("opentaint-full runtime tree differs from its contract SHA-256") + if identities.get(RUNTIME_TREE_REL.as_posix()) != runtime_sha: + raise ProbeError("opentaint-full runtime tree is not bound by contract input_identities") + runtime_tree = json.loads(runtime_raw) + if not isinstance(runtime_tree, dict) or runtime_tree.get("schema") != "release-runtime-tree/v1": + raise ProbeError("opentaint-full runtime tree has an unsupported schema") + if Path(runtime_tree.get("root", "")) != bundle: + raise ProbeError("opentaint-full runtime tree root differs from restored bundle root") + if runtime_tree.get("external_files") != {}: + raise ProbeError("opentaint-full runtime tree has unreviewed external dependencies") + expected_tree = runtime_tree.get("entries") + if not isinstance(expected_tree, dict): + raise ProbeError("opentaint-full runtime tree entries must be an object") + actual_tree = _tree_entries(bundle) + if actual_tree != expected_tree: + raise ProbeError("restored OpenTaint runtime tree membership, bytes or modes differ from contract") + actual_files = {name: entry["sha256"] for name, entry in actual_tree.items() if "sha256" in entry} + if actual_files != historical_by_path: + raise ProbeError("restored OpenTaint file hashes or exact membership differ from historical identity") + if wrapper_record.get("path") != str(bundle / "opentaint"): + raise ProbeError("contract wrapper path does not point into the restored bundle") + + groups = contract.get("groups") + matches = [g for g in groups if isinstance(g, dict) and g.get("id") == "opentaint-java-native"] if isinstance(groups, list) else [] + if len(matches) != 1 or matches[0].get("tool") != "opentaint": + raise ProbeError("contract must register exactly one opentaint-java-native environment") + environment = matches[0].get("environment") + if not isinstance(environment, dict) or not environment.get("PATH") or any( + not isinstance(k, str) or not isinstance(v, str) for k, v in environment.items()): + raise ProbeError("opentaint-java-native must have an explicit string environment with PATH") + nested_environment = dict(environment) + nested_environment["TMPDIR"] = control_env["TMPDIR"] + + return { + "contract": contract, + "contract_sha256": _sha_bytes(contract_raw), + "restoration_sha256": _sha_bytes(restoration_raw), + "historical_identity_sha256": historical_sha, + "runtime_tree_sha256": runtime_sha, + "bundle_root": bundle, + "wrapper": wrapper, + "wrapper_record": wrapper_record, + "javac": Path(javac_record["path"]), + "javac_record": javac_record, + "environment": dict(environment), + "nested_environment": nested_environment, + "control_environment": dict(control_env), + "scratch_root": expected_tmpdir, + "control_inventory_sha256": control_sha, + "control_deadline_seconds": outer_deadline, + "repo_root": root, + } + + +def _wrapper_environment(base: Mapping[str, str], bundle: Path) -> dict[str, str]: + env = dict(base) + java_home = bundle / "jre" + env["JAVA_HOME"] = str(java_home) + old_path = env.get("PATH", os.defpath) + jre_bin = str(java_home / "bin") + env["PATH"] = os.pathsep.join([jre_bin, *(p for p in old_path.split(os.pathsep) if p != jre_bin)]) + return env + + +def _load_average() -> list[float] | None: + try: + return list(os.getloadavg()) + except (AttributeError, OSError): + return None + + +def _run_bounded(argv: Sequence[str], *, cwd: Path, stdout: object, stderr: object, + env: Mapping[str, str], timeout: int = COMMAND_TIMEOUT_SECONDS) -> tuple[int, bool, str | None]: + if os.name != "posix": + raise OSError("bounded process-group cleanup requires a POSIX host") + previous_handlers: dict[int, object] = {} + + def interrupt(signum: int, _frame: object) -> None: + raise ProbeInterrupted(f"received {signal.Signals(signum).name}") + + for signum in (signal.SIGTERM, signal.SIGINT): + previous_handlers[signum] = signal.signal(signum, interrupt) + try: + process = subprocess.Popen(list(argv), cwd=cwd, stdout=stdout, stderr=stderr, + env=dict(env), start_new_session=True) + except BaseException: + for signum, handler in previous_handlers.items(): + signal.signal(signum, handler) + raise + def clean_group() -> None: + try: + os.killpg(process.pid, signal.SIGTERM) + except ProcessLookupError: + pass + try: + process.wait(timeout=1) + except subprocess.TimeoutExpired: + pass + # Signal descendants too, even when the group leader exited on SIGTERM. + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + process.wait() + + try: + return process.wait(timeout=timeout), False, None + except subprocess.TimeoutExpired: + clean_group() + return 124, True, "SIGTERM then SIGKILL sent to command process group" + except BaseException: + clean_group() + raise + finally: + for signum, handler in previous_handlers.items(): + signal.signal(signum, handler) + + +def _record_command(output: Path, *, command_id: str, argv: Sequence[str], env: Mapping[str, str], + identities: Mapping[str, object], runner: Callable[..., object] | None, + timeout: int, cwd: Path = ROOT) -> dict: + stdout_rel = f"{command_id}-stdout.txt" + stderr_rel = f"{command_id}-stderr.txt" + started = dt.datetime.now(dt.timezone.utc).isoformat() + monotonic_started = time.monotonic() + load_before = _load_average() + status = "launch-error" + exit_code = None + timed_out = False + error = None + interruption: BaseException | None = None + try: + with (output / stdout_rel).open("xb") as stdout, (output / stderr_rel).open("xb") as stderr: + execute = runner or _run_bounded + result = execute(argv, cwd=cwd, stdout=stdout, stderr=stderr, env=env, timeout=timeout) + if isinstance(result, tuple): + exit_code, timed_out, error = result + else: + exit_code = int(result) + exit_code = int(exit_code) + status = "timeout" if timed_out else ("succeeded" if exit_code == 0 else "failed") + except BaseException as exc: + error = f"{type(exc).__name__}: {exc}" + status = "interrupted" if not isinstance(exc, (OSError, subprocess.SubprocessError, ValueError)) else "launch-error" + interruption = exc + record = { + "schema": "opentaint-product-command/v090-v1", + "command_id": command_id, + "argv": [str(arg) for arg in argv], + "environment": dict(env), + "tool_identities": dict(identities), + "started_utc": started, + "ended_utc": dt.datetime.now(dt.timezone.utc).isoformat(), + "load_before": load_before, + "load_after": _load_average(), + "duration_seconds": round(time.monotonic() - monotonic_started, 6), + "timeout_seconds": timeout, + "timed_out": timed_out, + "exit_code": exit_code, + "status": status, + "error": error, + "stdout": stdout_rel, + "stderr": stderr_rel, + } + if interruption is not None: + _append_record(output, record) + raise interruption + return record + + +def _append_record(output: Path, record: dict) -> None: + with (output / "commands.jsonl").open("a", encoding="utf-8") as log: + log.write(json.dumps(record, sort_keys=True) + "\n") + log.flush() + os.fsync(log.fileno()) + + +def _skipped_record(command_id: str, reason: str, identities: Mapping[str, object]) -> dict: + now = dt.datetime.now(dt.timezone.utc).isoformat() + return { + "schema": "opentaint-product-command/v090-v1", "command_id": command_id, + "argv": None, "environment": None, "tool_identities": dict(identities), + "started_utc": now, "ended_utc": now, "load_before": _load_average(), "load_after": _load_average(), + "duration_seconds": 0, + "timeout_seconds": COMMAND_TIMEOUT_SECONDS, "timed_out": False, + "exit_code": None, "status": "skipped-after-prerequisite-failure", + "error": reason, "stdout": None, "stderr": None, + } + + +def _write_java_files(source: Path, files: Mapping[str, str]) -> list[Path]: + written = [] + for relative, content in files.items(): + target = source / relative + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(content, encoding="utf-8") + written.append(target) + return written + + +def _scan(output: Path, name: str, files: Mapping[str, str], packages: Sequence[str], *, + context: Mapping[str, object], runner: Callable[..., object] | None, + timeout: int, failed: list[str]) -> None: + work = output / name + source = work / "source" + classes = work / "classes" + source.mkdir(parents=True, exist_ok=False) + classes.mkdir() + try: + java_files = _write_java_files(source, files) + compile_argv = [str(context["javac"]), "-nowarn", "-d", str(classes), + *[str(path) for path in sorted(java_files)]] + compile_ids = { + "javac": {"path": str(context["javac"]), "sha256": context["javac_record"]["sha256"]}, + "contract_sha256": context["contract_sha256"], + } + compile_record = _record_command(output, command_id=f"{name}-compile", argv=compile_argv, + env=context["nested_environment"], identities=compile_ids, + runner=runner, timeout=timeout, cwd=context["repo_root"]) + _append_record(output, compile_record) + if compile_record["status"] != "succeeded": + failed.append(compile_record["command_id"]) + _append_record(output, _skipped_record( + f"{name}-product", "javac did not succeed", { + "opentaint-wrapper": context["wrapper_record"], + "runtime_tree_sha256": context["runtime_tree_sha256"], + "contract_sha256": context["contract_sha256"], + })) + return + + model = work / "project.yaml" + model.write_text( + "javaProjects:\n - sourceRoot: " + str(source) + + "\n modules:\n - moduleSourceRoot: " + str(source) + + "\n packages:\n" + "".join(f" - {package}\n" for package in packages) + + " moduleClasses:\n - " + str(classes) + "\n", + encoding="utf-8", + ) + wrapper_env = _wrapper_environment(context["nested_environment"], context["bundle_root"]) + argv = [str(context["wrapper"]), "scan", "--project-model", str(work), + "--entry-points", "*", "--ruleset", "builtin", "--output", + str(work / "product.sarif.json"), "--log-file", str(work / "product.log")] + product_ids = { + "opentaint-wrapper": dict(context["wrapper_record"]), + "runtime_tree_sha256": context["runtime_tree_sha256"], + "historical_identity_sha256": context["historical_identity_sha256"], + "contract_sha256": context["contract_sha256"], + } + product_record = _record_command(output, command_id=f"{name}-product", argv=argv, + env=wrapper_env, identities=product_ids, + runner=runner, timeout=timeout, cwd=context["repo_root"]) + _append_record(output, product_record) + if product_record["status"] != "succeeded": + failed.append(product_record["command_id"]) + except (OSError, UnicodeError, ValueError) as exc: + error_id = f"{name}-setup" + _append_record(output, { + "schema": "opentaint-product-command/v090-v1", "command_id": error_id, + "argv": None, "environment": None, "tool_identities": { + "contract_sha256": context["contract_sha256"], + "opentaint-wrapper": context["wrapper_record"], + }, "started_utc": dt.datetime.now(dt.timezone.utc).isoformat(), + "ended_utc": dt.datetime.now(dt.timezone.utc).isoformat(), "load_before": _load_average(), + "timeout_seconds": timeout, "timed_out": False, "exit_code": None, + "status": "setup-error", "error": f"{type(exc).__name__}: {exc}", + "stdout": None, "stderr": None, + }) + failed.append(error_id) + + +def capture(repo_root: Path = ROOT, contract_path: str | Path = CONTRACT_REL, + output_root: str | Path = OUTPUT_REL, *, + probe_script_path: str | Path | None = None, + runner: Callable[..., object] | None = None, + timeout: int = COMMAND_TIMEOUT_SECONDS) -> int: + root = Path(repo_root).resolve() + contract_file = _rooted(root, contract_path, "contract") + output = _rooted(root, output_root, "output") + script_path = Path(probe_script_path) if probe_script_path else root / SCRIPT_REL + context = _verify_v090_contract(root, contract_file, script_path) + native_cases = sorted((root / "cases/taint/java").glob("native-*")) + if len(native_cases) != NATIVE_FIXTURE_COUNT: + raise ProbeError(f"expected exactly 12 Java native fixtures, found {len(native_cases)}") + native_files: dict[str, dict[str, str]] = {} + for case in native_cases: + sources = sorted(case.glob("*.java")) + if not sources: + raise ProbeError(f"native fixture has no top-level Java source: {case.name}") + native_files[case.name] = { + f"dataflowbench/taint/{path.name}": path.read_text(encoding="utf-8") for path in sources + } + output.mkdir(parents=True, exist_ok=False) + with (output / "commands.jsonl").open("x", encoding="utf-8"): + pass + + failed: list[str] = [] + wrapper_env = _wrapper_environment(context["nested_environment"], context["bundle_root"]) + wrapper_ids = { + "opentaint-wrapper": dict(context["wrapper_record"]), + "runtime_tree_sha256": context["runtime_tree_sha256"], + "historical_identity_sha256": context["historical_identity_sha256"], + "contract_sha256": context["contract_sha256"], + } + for command_id, argv in ( + ("wrapper-version", [str(context["wrapper"]), "--version"]), + ("wrapper-help", [str(context["wrapper"]), "--help"]), + ("scan-help", [str(context["wrapper"]), "scan", "--help"]), + ): + record = _record_command(output, command_id=command_id, argv=argv, env=wrapper_env, + identities=wrapper_ids, runner=runner, timeout=timeout, cwd=root) + _append_record(output, record) + if record["status"] != "succeeded": + failed.append(command_id) + + for case_name, sources in native_files.items(): + _scan(output, case_name, sources, ["dataflowbench.taint"], context=context, + runner=runner, timeout=timeout, failed=failed) + + stubs = dict(SERVLET_STUBS) + for variant, value in (("positive", 'request.getParameter("cmd")'), ("negative", '"fixed-command"')): + files = dict(stubs) + files["dataflowbench/control/ControlServlet.java"] = ( + "package dataflowbench.control; import jakarta.servlet.http.*; " + "public class ControlServlet extends HttpServlet { @Override protected void doGet(" + "HttpServletRequest request,HttpServletResponse response) throws java.io.IOException { " + "String cmd = " + value + "; Runtime.getRuntime().exec(cmd); } }" + ) + _scan(output, f"servlet-{variant}", files, ["dataflowbench.control"], context=context, + runner=runner, timeout=timeout, failed=failed) + + records = [json.loads(line) for line in (output / "commands.jsonl").read_text(encoding="utf-8").splitlines()] + scope = { + "schema": "opentaint-product-probe/v090-v1", + "release": "v0.9.0", + "kind": "fresh-shipped-product-activation", + "status": "captured-with-failures" if failed else "captured", + "contract_sha256": context["contract_sha256"], + "opentaint_wrapper": context["wrapper_record"], + "runtime_tree_sha256": context["runtime_tree_sha256"], + "historical_identity_sha256": context["historical_identity_sha256"], + "native_fixtures": list(native_files), + "servlet_controls": { + "positive": "request.getParameter(cmd) flows to Runtime.exec", + "negative": "constant fixed-command reaches Runtime.exec", + }, + "entry_points": "* passed as an observed probe parameter; no equivalence is inferred", + "compiler": {"path": str(context["javac"]), "sha256": context["javac_record"]["sha256"], + "environment_source": "contract group opentaint-java-native, unchanged"}, + "wrapper_environment": "contract group environment with JAVA_HOME and PATH bound to the exact restored bundle JRE", + "normalization": "No normalized outcome is generated; inspect raw product SARIF/logs before equivalence claims.", + "command_count": len(records), + "failed_commands": failed, + "command_timeout_seconds": timeout, + "outer_deadline_seconds": context["control_deadline_seconds"], + "nested_timeouts": [row["command_id"] for row in records if row.get("timed_out")], + } + (output / "scope.json").write_text(json.dumps(scope, indent=2, sort_keys=True) + "\n", encoding="utf-8") + return 1 if failed else 0 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--contract", type=Path, default=CONTRACT_REL) + parser.add_argument("--output-root", type=Path, default=OUTPUT_REL) + parser.add_argument("--timeout-seconds", type=int, default=COMMAND_TIMEOUT_SECONDS) + args = parser.parse_args() + try: + if args.timeout_seconds < 1: + raise ProbeError("timeout-seconds must be positive") + return capture(contract_path=args.contract, output_root=args.output_root, + timeout=args.timeout_seconds) + except (OSError, ProbeError, json.JSONDecodeError) as exc: + print(f"probe-opentaint-product-v090: {exc}", file=sys.stderr) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/probe-python-modeling-load-bearing-v090.py b/scripts/probe-python-modeling-load-bearing-v090.py new file mode 100644 index 000000000..0dfa4722e --- /dev/null +++ b/scripts/probe-python-modeling-load-bearing-v090.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python3 +"""Capture the preregistered Python load-bearing controls for v0.9.0.""" + +from __future__ import annotations + +import argparse +import datetime as dt +import hashlib +import json +import os +from pathlib import Path +import signal +import shutil +import subprocess +import threading +from contextlib import contextmanager +from typing import Callable, Mapping, Sequence, TextIO + + +ROOT = Path(__file__).resolve().parents[1] +CONTRACT = ROOT / "reports/releases/v0.9.0/execution-v1/contract.json" +OUTPUT_ROOT = ROOT / "reports/raw/load-bearing-python-modeling-v090" +TIMEOUT_SECONDS = 600 +TOOL_KEYS = ("bifrost", "codeql", "codeql-packs", "joern", "semgrep", "semgrep-core") + + +def _sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as source: + for chunk in iter(lambda: source.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def _tool_record(tools: Mapping[str, object], key: str) -> Mapping[str, object]: + entry = tools.get(key) + if not isinstance(entry, dict): + raise ValueError(f"v0.9 contract has no pinned tool record for {key}") + return entry + + +def _bound_contract_input(root: Path, reference: Mapping[str, object], label: str) -> Path: + relative = reference.get("path") + digest = reference.get("sha256") + if not isinstance(relative, str) or not isinstance(digest, str): + raise ValueError(f"contract has no path and digest for {label}") + candidate_path = Path(relative) + if candidate_path.is_absolute() or ".." in candidate_path.parts: + raise ValueError(f"unsafe contract input path for {label}") + candidate = root / candidate_path + if not candidate.is_file() or _sha256(candidate) != digest: + raise ValueError(f"contract input digest differs for {label}: {relative}") + return candidate + + +def _check_control_environment(contract: Mapping[str, object], root: Path) -> dict[str, str]: + reference = contract.get("control_inventory") + if not isinstance(reference, dict): + raise ValueError("v0.9 contract has no bound control inventory") + relative = reference.get("path") + if not isinstance(relative, str): + raise ValueError("v0.9 contract control inventory path is missing") + identities = contract.get("input_identities") + if not isinstance(identities, dict) or identities.get(relative) != reference.get("sha256"): + raise ValueError("control inventory is not bound by the v0.9 contract identities") + inventory_path = _bound_contract_input(root, reference, "control inventory") + inventory = json.loads(inventory_path.read_text(encoding="utf-8")) + if inventory.get("schema") != "release-control-inventory/v1" or inventory.get("release") != "v0.9.0": + raise ValueError("control inventory is not the bound v0.9.0 inventory") + control_id = "probe-python-modeling-load-bearing" + if control_id not in inventory.get("supplemental_control_ids", []): + raise ValueError("Python modeling probe is not listed in supplemental_control_ids") + matches = [ + item for item in inventory.get("controls", []) + if isinstance(item, dict) and item.get("id") == control_id + ] + if len(matches) != 1: + raise ValueError("control inventory must contain exactly one Python modeling probe") + control = matches[0] + script_path = "scripts/probe-python-modeling-load-bearing-v090.py" + script_digest = _sha256(root / script_path) + identities = contract.get("input_identities", {}) + if identities.get(script_path) != script_digest: + raise ValueError("Python modeling probe differs from contract input identity") + script_identity = control.get("script_identity") + if not isinstance(script_identity, list) or script_identity != [ + {"path": script_path, "sha256": script_digest} + ]: + raise ValueError("Python modeling probe differs from control inventory script identity") + argv = control.get("argv") + if not isinstance(argv, list) or not argv or argv[-1] != "scripts/probe-python-modeling-load-bearing-v090.py": + raise ValueError("Python modeling control inventory points at a different script") + roots = control.get("output_roots") + scratch_relative = "reports/raw/control-scratch/probe-python-modeling-load-bearing" + if not isinstance(roots, list) or scratch_relative not in roots or "reports/raw/load-bearing-python-modeling-v090" not in roots: + raise ValueError("Python modeling control inventory output roots differ from the probe") + environment = control.get("environment") + if not isinstance(environment, dict) or not all( + isinstance(key, str) and isinstance(value, str) for key, value in environment.items() + ): + raise ValueError("Python modeling control inventory has no explicit environment") + scratch = environment.get("TMPDIR") + if not isinstance(scratch, str) or not Path(scratch).is_absolute(): + raise ValueError("Python modeling control inventory TMPDIR must be absolute") + expected_suffix = Path(scratch_relative).parts + if Path(scratch).parts[-len(expected_suffix):] != expected_suffix: + raise ValueError("Python modeling control inventory TMPDIR is outside its declared scratch root") + if not Path(scratch).is_dir(): + raise ValueError(f"recorder-created control TMPDIR is missing: {scratch}") + return dict(environment) + + +def _check_held_tool_digests( + contract: Mapping[str, object], contract_path: Path, root: Path, +) -> dict[str, Mapping[str, object]]: + """Bind the tools this probe invokes to both contract and held digest evidence.""" + tools = contract.get("tools") + if not isinstance(tools, dict): + raise ValueError("v0.9 contract tools must be an object") + + evidence_refs = [ + item for item in contract.get("identity_evidence", []) + if isinstance(item, dict) and item.get("path", "").endswith("/held-tool-digests.json") + ] + if len(evidence_refs) != 1: + raise ValueError("v0.9 contract must bind exactly one held-tool digest manifest") + identities = contract.get("input_identities") + if not isinstance(identities, dict) or identities.get(evidence_refs[0].get("path")) != evidence_refs[0].get("sha256"): + raise ValueError("held-tool digest manifest is not bound by contract input identities") + evidence_path = _bound_contract_input(root, evidence_refs[0], "held-tool-digests") + evidence_sha = _sha256(evidence_path) + if evidence_refs[0].get("sha256") != evidence_sha: + raise ValueError("held-tool-digests.json differs from the v0.9 contract identity evidence") + + held = json.loads(evidence_path.read_text(encoding="utf-8")) + artifacts = held.get("artifacts") + if not isinstance(artifacts, list): + raise ValueError("held-tool-digests.json artifacts must be a list") + by_id = {item.get("id"): item for item in artifacts if isinstance(item, dict)} + + pinned: dict[str, Mapping[str, object]] = {} + for key in TOOL_KEYS: + entry = _tool_record(tools, key) + path = entry.get("path") + digest = entry.get("sha256") + if key == "codeql-packs": + # The pack tree has its own contract identity; it is not a held file + # in held-tool-digests.json. + if not isinstance(path, str) or not path or not isinstance(entry.get("tree_sha256"), str): + raise ValueError("v0.9 contract has no pinned CodeQL pack tree identity") + if not Path(path).is_dir(): + raise ValueError(f"pinned CodeQL pack tree is missing: {path}") + pinned[key] = entry + continue + + if not isinstance(path, str) or not path or not isinstance(digest, str) or len(digest) != 64: + raise ValueError(f"v0.9 contract has no path and SHA-256 for {key}") + record = by_id.get(key) + if key == "codeql": + # The held-file manifest predates the restored 2.27.1 CLI and has + # no CodeQL row. Its current identity is bound by the Swift plan. + if isinstance(record, dict): + if record.get("path") != path or record.get("sha256") != digest: + raise ValueError("held CodeQL digest conflicts with the current v0.9 pin") + elif not isinstance(record, dict) or record.get("path") != path or record.get("sha256") != digest: + raise ValueError(f"held digest record differs from the v0.9 contract for {key}") + candidate = Path(path) + if not candidate.is_file(): + raise ValueError(f"pinned tool file is missing or not regular: {path}") + if _sha256(candidate) != digest: + raise ValueError(f"pinned tool digest differs from v0.9 contract: {path}") + pinned[key] = entry + + _check_codeql_cli_tree(contract, root, pinned["codeql"]) + + pack_refs = [ + item for item in contract.get("identity_evidence", []) + if isinstance(item, dict) and item.get("path", "").endswith("/non-swift-codeql-packs.json") + ] + if len(pack_refs) != 1: + raise ValueError("v0.9 contract must bind exactly one non-Swift CodeQL pack inventory") + if identities.get(pack_refs[0].get("path")) != pack_refs[0].get("sha256"): + raise ValueError("CodeQL pack inventory is not bound by contract input identities") + pack_manifest_path = _bound_contract_input(root, pack_refs[0], "CodeQL pack inventory") + pack_manifest = json.loads(pack_manifest_path.read_text(encoding="utf-8")) + pack_root = Path(str(pinned["codeql-packs"].get("path", ""))) + if pack_manifest.get("root") != str(pack_root): + raise ValueError("CodeQL pack inventory root differs from the v0.9 contract") + expected_files = pack_manifest.get("files") + if not isinstance(expected_files, list) or not expected_files: + raise ValueError("CodeQL pack inventory files must be a nonempty list") + expected: dict[str, Mapping[str, object]] = {} + for item in expected_files: + if not isinstance(item, dict): + raise ValueError("invalid CodeQL pack inventory entry") + relative = item.get("path") + if not isinstance(relative, str): + raise ValueError("CodeQL pack inventory entry has no relative path") + relative_path = Path(relative) + if relative_path.is_absolute() or ".." in relative_path.parts or relative in expected: + raise ValueError(f"unsafe or duplicate CodeQL pack path: {relative}") + expected[relative_path.as_posix()] = item + observed: set[str] = set() + for directory, dirs, files in os.walk(pack_root, followlinks=False): + base = Path(directory) + for name in list(dirs): + child = base / name + if child.is_symlink(): + raise ValueError(f"CodeQL pack inventory contains an unexpected symlink: {child}") + for name in files: + candidate = base / name + relative = candidate.relative_to(pack_root).as_posix() + if candidate.is_symlink() or not candidate.is_file(): + raise ValueError(f"CodeQL pack inventory contains a nonregular file: {candidate}") + item = expected.get(relative) + if item is None: + raise ValueError(f"unlisted CodeQL pack file: {relative}") + if candidate.stat().st_size != item.get("bytes") or _sha256(candidate) != item.get("sha256"): + raise ValueError(f"CodeQL pack file differs from v0.9 inventory: {relative}") + observed.add(relative) + if observed != set(expected): + missing = sorted(set(expected) - observed) + raise ValueError("CodeQL pack files missing from pinned tree: " + ", ".join(missing[:5])) + return pinned + + +def _check_codeql_cli_tree( + contract: Mapping[str, object], root: Path, codeql: Mapping[str, object], +) -> None: + plans = contract.get("swift_plans") + if not isinstance(plans, dict) or not isinstance(plans.get("codeql"), dict): + raise ValueError("v0.9 contract has no bound current CodeQL Swift plan") + plan_ref = plans["codeql"] + input_identities = contract.get("input_identities") + if not isinstance(input_identities, dict) or input_identities.get(plan_ref.get("path")) != plan_ref.get("sha256"): + raise ValueError("CodeQL Swift plan is not bound by contract input identities") + plan_path = _bound_contract_input(root, plan_ref, "CodeQL Swift plan") + plan = json.loads(plan_path.read_text(encoding="utf-8")) + runtime = plan.get("runtime") + manifests = runtime.get("manifests") if isinstance(runtime, dict) else None + if not isinstance(runtime, dict) or runtime.get("codeql") != codeql.get("path"): + raise ValueError("CodeQL CLI path differs from the current Swift runtime plan") + if not isinstance(manifests, dict) or not isinstance(manifests.get("cli_tree"), dict): + raise ValueError("CodeQL Swift plan has no bound CLI tree manifest") + tree_ref = manifests["cli_tree"] + tree_path = _bound_contract_input(root, tree_ref, "CodeQL CLI tree manifest") + expected_tree = json.loads(tree_path.read_text(encoding="utf-8")) + # Reuse the runner's established runtime membership, byte, symlink, and + # mode inventory so the executable hash is tied to its current CLI tree. + try: + from swift_normal_runner_v1 import file_inventory + except ImportError as exc: + raise ValueError("cannot load the CodeQL runtime tree verifier") from exc + cli_path = Path(str(codeql["path"])) + if file_inventory(cli_path.parent) != expected_tree: + raise ValueError("CodeQL CLI runtime tree differs from the current Swift plan") + + +def _python_environment( + contract: Mapping[str, object], tool: str, control_environment: Mapping[str, str], +) -> dict[str, str]: + groups = contract.get("groups") + if not isinstance(groups, list): + raise ValueError("v0.9 contract groups must be a list") + group_id = f"{tool}-python-modeling" + matches = [group for group in groups if isinstance(group, dict) and group.get("id") == group_id] + if len(matches) != 1: + raise ValueError(f"expected exactly one explicit environment group {group_id}") + environment = matches[0].get("environment") + if not isinstance(environment, dict) or not environment or not all( + isinstance(key, str) and isinstance(value, str) for key, value in environment.items() + ): + raise ValueError(f"missing explicit tool environment: {group_id}") + selected = dict(environment) + # Keep subprocess-created temporary files below the recorder-owned control + # scratch directory named by the hash-bound inventory. + selected["TMPDIR"] = control_environment["TMPDIR"] + return selected + + +def _signal_group(process: subprocess.Popen[bytes], sig: int) -> None: + try: + os.killpg(process.pid, sig) + except ProcessLookupError: + pass + + +def _stop_process_group(process: subprocess.Popen[bytes], grace_seconds: int = 1) -> None: + _signal_group(process, signal.SIGTERM) + try: + process.wait(timeout=grace_seconds) + except subprocess.TimeoutExpired: + pass + finally: + # The leader may exit while one of its children remains in the group. + _signal_group(process, signal.SIGKILL) + process.wait() + + +def _run_bounded( + argv: Sequence[str | Path], *, cwd: Path, stdout: object, stderr: object, + env: Mapping[str, str], timeout: int = TIMEOUT_SECONDS, +) -> int: + process = subprocess.Popen( + [str(value) for value in argv], cwd=cwd, stdout=stdout, stderr=stderr, + env=dict(env), start_new_session=True, + ) + try: + return process.wait(timeout=timeout) + except subprocess.TimeoutExpired: + _stop_process_group(process) + return 124 + except BaseException: + # Covers KeyboardInterrupt and caller cancellation while the child is + # running; preserve the original exception after the whole group exits. + _stop_process_group(process) + raise + + +class ProbeInterrupted(InterruptedError): + """Raised when the outer recorder interrupts this probe process.""" + + +@contextmanager +def _outer_interrupt_handlers(): + """Turn recorder signals into exceptions so nested process cleanup runs.""" + if threading.current_thread() is not threading.main_thread(): + yield + return + previous = {sig: signal.getsignal(sig) for sig in (signal.SIGTERM, signal.SIGINT)} + + def interrupt(signum: int, _frame: object) -> None: + raise ProbeInterrupted(f"received signal {signum}") + + try: + for sig in previous: + signal.signal(sig, interrupt) + yield + finally: + for sig, handler in previous.items(): + signal.signal(sig, handler) + + +def _fixture(root: Path, workspace: Path, name: str, arm: str) -> Path: + destination = workspace / arm + destination.mkdir(parents=True) + source = root / "cases/taint/python" / name + for path in source.glob("*.py"): + shutil.copy2(path, destination / path.name) + return destination + + +class ProbeRun: + def __init__( + self, *, root: Path, output: Path, contract: Mapping[str, object], + tools: Mapping[str, Mapping[str, object]], control_environment: Mapping[str, str], + runner: Callable[..., int] | None, + ) -> None: + self.root = root + self.output = output + self.workspace = output / "workspace" + self.contract = contract + self.tools = tools + self.control_environment = control_environment + self.runner = runner + self.failed = False + self.log: TextIO = (output / "commands.jsonl").open("x", encoding="utf-8") + + def run(self, name: str, tool: str, argv: Sequence[str | Path]) -> int: + start = dt.datetime.now(dt.timezone.utc).isoformat() + environment = _python_environment(self.contract, tool, self.control_environment) + stdout_path = self.output / f"{name}-stdout.txt" + stderr_path = self.output / f"{name}-stderr.txt" + command_cwd = self.output / "command-workspaces" / name + command_cwd.mkdir(parents=True, exist_ok=False) + record: dict[str, object] = { + "id": name, + "argv": [str(value) for value in argv], + "tool": tool, + "cwd": str(command_cwd), + "environment": environment, + "start_utc": start, + "timeout_seconds": TIMEOUT_SECONDS, + } + code: int | None = None + status = "launch-error" + error: str | None = None + try: + with stdout_path.open("xb") as stdout, stderr_path.open("xb") as stderr: + execute = self.runner or _run_bounded + code = int(execute( + [str(value) for value in argv], cwd=command_cwd, + stdout=stdout, stderr=stderr, env=environment, + timeout=TIMEOUT_SECONDS, + )) + status = "timeout" if code == 124 else ("completed" if code == 0 else "failed") + self.failed |= code != 0 + return code + except OSError as exc: + error = str(exc) + self.failed = True + return 127 + except BaseException as exc: + error = f"{type(exc).__name__}: {exc}" + self.failed = True + raise + finally: + record.update({ + "status": status, + "exit_code": code, + "end_utc": dt.datetime.now(dt.timezone.utc).isoformat(), + }) + if error is not None: + record["error"] = error + self.log.write(json.dumps(record, sort_keys=True) + "\n") + self.log.flush() + + +def _run_bifrost(probe: ProbeRun) -> None: + root, output, workspace = probe.root, probe.output, probe.workspace + policy = (root / "adapters/bifrost/policies/model-python.rqlp").read_text(encoding="utf-8") + for role in ("source", "sink"): + for polarity in ("positive", "negative"): + for variant in ("with", "without"): + name = f"bifrost-declared-{role}-{polarity}-{variant}" + work = _fixture(root, workspace, f"model-declared-{role}-{polarity}", name) + lines = policy.splitlines(keepends=True) + if variant == "without": + matched = False + for index, line in enumerate(lines): + if ":id declared-" + role in line: + lines[index] = line[:line.index("(" + role)] + line[line.rindex("])") :] + matched = True + break + if not matched: + raise ValueError(f"declared {role} model missing from policy") + artifact = output / f"{name}.rqlp" + artifact.write_text("".join(lines), encoding="utf-8") + shutil.copy2(artifact, work / "policy.rqlp") + probe.run(name, "bifrost", [ + probe.tools["bifrost"]["path"], "--root", work, + "--policy-file", "policy.rqlp", "--evaluation-date", "2026-08-11", + "--format", "json", "--fail-on", "never", "--output", output / f"{name}.json", + ]) + + +def _run_codeql(probe: ProbeRun) -> None: + root, output, workspace = probe.root, probe.output, probe.workspace + work = _fixture(root, workspace, "model-opaque-propagator-positive", "codeql-source") + database = workspace / "codeql-db" + codeql = probe.tools["codeql"]["path"] + if probe.run("codeql-extract", "codeql", [ + codeql, "database", "create", database, "--language=python", + "--source-root=" + str(work), "--overwrite", + ]) == 0: + for variant, query in (("with", "PythonModeling.ql"), ("without", "PythonModelingProbe.ql")): + name = "codeql-opaque-" + variant + probe.run(name, "codeql", [ + codeql, "database", "analyze", database, + root / "adapters/codeql/python/queries" / query, + "--format=sarif-latest", "--output=" + str(output / (name + ".sarif.json")), + "--rerun", "--additional-packs=" + str(probe.tools["codeql-packs"]["path"]), + ]) + + +def _run_joern(probe: ProbeRun) -> None: + root, output, workspace = probe.root, probe.output, probe.workspace + semantics = (root / "adapters/joern/semantics/model-python.semantics").read_text(encoding="utf-8") + needle = '"clean.py:.scrub"' + if not any(line.startswith(needle) for line in semantics.splitlines()): + raise ValueError("declared sanitizer semantics missing from Python model") + controls = ( + ("sanitizer-with", "model-sanitizer-kill-negative", False), + ("sanitizer-without", "model-sanitizer-kill-negative", True), + ("propagator-unmodeled", "model-opaque-propagator-positive", False), + ("summary-unmodeled", "model-summary-through-positive", False), + ) + for name, case, remove in controls: + work = _fixture(root, workspace, case, "joern-" + name) + semantics_path = output / ("joern-" + name + ".semantics") + semantics_path.write_text("".join( + line for line in semantics.splitlines(keepends=True) + if not (remove and line.startswith(needle)) + ), encoding="utf-8") + argv: list[str | Path] = [probe.tools["joern"]["path"], "--script", root / "adapters/joern/queries/modeling.sc"] + for key, value in ( + ("inputPath", work), ("language", "PYTHONSRC"), ("sourceName", "dfb_source"), + ("sinkName", "dfb_sink"), ("sourceKind", "call-return"), + ("semanticsPath", semantics_path), ("outputPath", output / ("joern-" + name + ".json")), + ): + argv.extend(("--param", f"{key}={value}")) + probe.run("joern-" + name, "joern", argv) + + +def _run_semgrep(probe: ProbeRun) -> None: + root, output, workspace = probe.root, probe.output, probe.workspace + rule = (root / "adapters/semgrep/rules/model-python.yaml").read_text(encoding="utf-8") + for role, needle in (("source", "- pattern: fetch_remote(...)"), ("sink", "- pattern: record(...)")): + for polarity in ("positive", "negative"): + for variant in ("with", "without"): + name = f"semgrep-declared-{role}-{polarity}-{variant}" + work = _fixture(root, workspace, f"model-declared-{role}-{polarity}", name) + artifact = output / f"{name}.yaml" + artifact.write_text("".join( + line for line in rule.splitlines(keepends=True) + if not (variant == "without" and needle in line) + ), encoding="utf-8") + probe.run(name, "semgrep", [ + probe.tools["semgrep"]["path"], "scan", "--metrics=off", "--oss-only", + "--disable-version-check", "--no-git-ignore", "--quiet", "--json", + "--config", artifact, work, + ]) + for variant in ("safe-functions", "default-functions"): + name = "semgrep-sanitizer-selectivity-" + variant + work = _fixture(root, workspace, "model-sanitizer-selectivity-positive", name) + artifact = output / (name + ".yaml") + content = rule if variant == "safe-functions" else rule.replace( + "taint_assume_safe_functions: true", "taint_assume_safe_functions: false" + ) + artifact.write_text(content, encoding="utf-8") + probe.run(name, "semgrep", [ + probe.tools["semgrep"]["path"], "scan", "--metrics=off", "--oss-only", + "--disable-version-check", "--no-git-ignore", "--quiet", "--json", + "--config", artifact, work, + ]) + + +def capture( + contract_path: Path = CONTRACT, + output_root: Path = OUTPUT_ROOT, + *, + root: Path = ROOT, + runner: Callable[..., int] | None = None, +) -> int: + raw_contract = contract_path.read_bytes() + contract = json.loads(raw_contract) + if contract.get("schema") != "release-execution-contract/v1" or contract.get("release") != "v0.9.0": + raise ValueError("contract is not the v0.9.0 execution-v1 contract") + if contract.get("execution_authorized") is not True: + raise ValueError("execution authorization required: reviewed executable contract required") + + # Complete every authorization and identity check before creating output. + control_environment = _check_control_environment(contract, root) + tools = _check_held_tool_digests(contract, contract_path, root) + output_root.mkdir(parents=True, exist_ok=False) + workspace = output_root / "workspace" + workspace.mkdir() + probe = ProbeRun( + root=root, output=output_root, contract=contract, tools=tools, + control_environment=control_environment, runner=runner, + ) + try: + with _outer_interrupt_handlers(): + _run_bifrost(probe) + _run_codeql(probe) + _run_joern(probe) + _run_semgrep(probe) + finally: + # The unique output root and workspace are evidence; leave them intact. + probe.log.close() + return 1 if probe.failed else 0 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--contract", type=Path, default=CONTRACT) + parser.add_argument("--output-root", type=Path, default=OUTPUT_ROOT) + args = parser.parse_args() + try: + return capture(args.contract, args.output_root) + except (OSError, ValueError, json.JSONDecodeError) as exc: + parser.error(str(exc)) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/probe-warm-observability-v090.py b/scripts/probe-warm-observability-v090.py index 92ca724f3..99a6b3cc3 100644 --- a/scripts/probe-warm-observability-v090.py +++ b/scripts/probe-warm-observability-v090.py @@ -3,8 +3,8 @@ This is observational evidence only. It does not run an analyzer, interpret a missing help option as a capability decline, or make any warm-performance -claim. The OpenTaint product wrapper is recorded as unavailable until its full -runtime identity is added to the reviewed v0.9.0 contract. +claim. The OpenTaint product wrapper is observed only when its exact identity +is registered in the reviewed v0.9.0 contract. """ from __future__ import annotations @@ -34,40 +34,67 @@ def _group_environment(contract: Mapping[str, object], command_id: str) -> dict[ "bifrost": "bifrost", "codeql-create": "codeql", "codeql-analyze": "codeql", "joern": "joern", "semgrep": "semgrep", "infer": "infer", "flowdroid": "flowdroid", "pysa": "pysa", "opentaint-jar": "opentaint", + "opentaint-product": "opentaint", }[command_id] for group in groups: if group.get('tool') == wanted_tool and isinstance(group.get('environment'), dict): - return dict(group['environment']) + environment = dict(group['environment']) + if command_id == 'opentaint-product': + runtime = str(Path(contract['tools']['opentaint-wrapper']['path']).parent / 'jre') + environment['JAVA_HOME'] = runtime + environment['PATH'] = runtime + '/bin:' + environment['PATH'] + reference = contract.get('control_inventory') + if reference: + raw = (ROOT / reference['path']).read_bytes() + if hashlib.sha256(raw).hexdigest() != reference['sha256']: + raise ValueError('control inventory digest mismatch') + matches = [item for item in json.loads(raw)['controls'] if item['id'] == 'probe-warm-observability'] + if len(matches) != 1: + raise ValueError('warm observability control must be registered exactly once') + environment['TMPDIR'] = matches[0]['environment']['TMPDIR'] + return environment raise ValueError('missing explicit tool environment: ' + wanted_tool) def _run_bounded(argv: Sequence[str], *, cwd: Path, stdout: object, stderr: object, env: Mapping[str, str], timeout: int = TIMEOUT_SECONDS) -> int: - process = subprocess.Popen( - list(argv), cwd=cwd, stdout=stdout, stderr=stderr, env=dict(env), - start_new_session=True, - ) - try: - return process.wait(timeout=timeout) - except subprocess.TimeoutExpired: + def interrupted(signum, frame): + raise KeyboardInterrupt('probe interrupted by signal ' + str(signum)) + old_handlers = {sig: signal.getsignal(sig) for sig in (signal.SIGTERM, signal.SIGINT)} + for sig in old_handlers: + signal.signal(sig, interrupted) + process = None + def stop(): + if process is None: + return try: os.killpg(process.pid, signal.SIGTERM) except ProcessLookupError: pass try: - process.wait(timeout=3) + process.wait(timeout=1) except subprocess.TimeoutExpired: - try: - os.killpg(process.pid, signal.SIGKILL) - except ProcessLookupError: - pass - process.wait() + pass try: os.killpg(process.pid, signal.SIGKILL) except ProcessLookupError: pass process.wait() + try: + process = subprocess.Popen( + list(argv), cwd=cwd, stdout=stdout, stderr=stderr, env=dict(env), + start_new_session=True, + ) + return process.wait(timeout=timeout) + except subprocess.TimeoutExpired: + stop() return 124 + except BaseException: + stop() + raise + finally: + for sig, handler in old_handlers.items(): + signal.signal(sig, handler) def _tool(tools: Mapping[str, object], key: str) -> tuple[str, str]: @@ -114,6 +141,11 @@ def command_inventory(contract: Mapping[str, object]) -> list[dict[str, object]] {"id": "opentaint-product", "argv": None, "identity": ["opentaint-wrapper"], "status": "not-run-no-pinned-v090-identity"}, ] + if 'opentaint-wrapper' in tools: + pinned['opentaint-wrapper'] = _tool(tools, 'opentaint-wrapper') + commands[-1] = {'id': 'opentaint-product', + 'argv': [pinned['opentaint-wrapper'][0], 'scan', '--help'], + 'identity': ['opentaint-wrapper']} for item in commands: item["tool_identities"] = { key: {"path": pinned[key][0], "sha256": pinned[key][1]} @@ -140,6 +172,21 @@ def capture( for key in ("bifrost", "codeql", "joern", "semgrep", "infer", "java", "flowdroid", "pyre", "opentaint-analyzer"): path, digest = _tool(tools, key) _check_pinned_file(path, digest) + if 'opentaint-wrapper' in tools: + _check_pinned_file(*_tool(tools, 'opentaint-wrapper')) + from release_runtime_inventory_v090 import verify + bundle = str(Path(tools['opentaint-wrapper']['path']).parent) + matched = False + for reference in contract.get('runtime_trees', []): + raw = (ROOT / reference['path']).read_bytes() + if hashlib.sha256(raw).hexdigest() != reference['sha256']: + raise ValueError('runtime inventory digest mismatch') + manifest = json.loads(raw) + if manifest.get('root') == bundle: + verify(manifest) + matched = True + if not matched: + raise ValueError('full product runtime tree is not bound') output_root.mkdir(parents=True, exist_ok=False) command_log = output_root / "commands.jsonl" failures = 0 @@ -189,7 +236,9 @@ def capture( "scope": "Advertised CLI help only. Help absence does not establish declined capability; pair with the registered same-work controls.", "flowdroid": "Batch support does not establish whole-population per-case-config equivalence; preserve as unsupported/unresolved unless independently qualified.", "warm_measurements": ["Joern Java", "Semgrep Java largest identical-rule group"], - "opentaint_product": "not run: v0.9 contract does not pin the full product wrapper identity/runtime tree", + "opentaint_product": ("Pinned shipped wrapper help captured; no equivalence inferred." + if 'opentaint-wrapper' in tools else + "not run: v0.9 contract does not pin the full product wrapper identity/runtime tree"), } (output_root / "scope.json").write_text(json.dumps(scope, indent=2, sort_keys=True) + "\n", encoding="utf-8") return 1 if failures else 0 diff --git a/scripts/release_control_attempt_v090.py b/scripts/release_control_attempt_v090.py new file mode 100644 index 000000000..47352ea0f --- /dev/null +++ b/scripts/release_control_attempt_v090.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +"""Capture one explicitly reviewed, non-report v0.9.0 control invocation. + +``validate_control(root, contract_path, control_id)`` is read-only and returns +the selected ``control`` plus its ``execution_root`` and declared +``output_roots``. ``run_control`` launches the command exactly once; measurement +repeats are harness metadata and never cause this recorder to rerun it. +""" + +from __future__ import annotations + +import datetime as _datetime +import fcntl +import hashlib +import importlib.util +import json +import os +from pathlib import Path, PurePosixPath +import re + + +RELEASE = "v0.9.0" +INVENTORY_SCHEMA = "release-control-inventory/v1" +ATTEMPTS_PATH = "reports/releases/v0.9.0/control-attempts" +LEDGER_PATH = "reports/releases/v0.9.0/control-ledger-v1.jsonl" +SCRATCH_ROOT = PurePosixPath("reports/raw/control-scratch") +_IMPL = Path(__file__).with_name("release_attempt_v090.py") +_SPEC = importlib.util.spec_from_file_location("_release_attempt_v090_helpers", _IMPL) +if _SPEC is None or _SPEC.loader is None: + raise RuntimeError(f"cannot load release-attempt helpers: {_IMPL}") +_HELPERS = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_HELPERS) + + +class ControlError(RuntimeError): + """A fail-closed contract, identity, or evidence error.""" + + +def _sha(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def _json_bytes(value: object) -> bytes: + return (json.dumps(value, sort_keys=True, indent=2, ensure_ascii=False) + "\n").encode() + + +def _timestamp() -> str: + return _datetime.datetime.now(_datetime.timezone.utc).isoformat() + + +def _load_bound_control(root, contract_path, control_id, *, require_authorized, require_fresh): + requested_root = Path(root) + if requested_root.is_symlink(): + raise ControlError("execution root cannot be a symlink") + try: + root = requested_root.resolve(strict=True) + except OSError as exc: + raise ControlError(f"execution root is unavailable: {exc}") from exc + if not root.is_dir() or os.name != "posix": + raise ControlError("execution root must be a directory on a POSIX host") + + contract_rel = _HELPERS._relative(Path(contract_path).as_posix(), "contract path") + contract_file = _HELPERS._safe_path(root, contract_rel, allow_missing=False) + contract, contract_raw = _HELPERS._read_json(contract_file, "execution contract") + if contract.get("schema_version") != 1 or contract.get("release") != RELEASE: + raise ControlError("execution contract schema or release identity mismatch") + authorized = contract.get("execution_authorized") is True + if type(contract.get("execution_authorized")) is not bool: + raise ControlError("execution contract must explicitly set execution_authorized") + if require_authorized and not authorized: + raise ControlError("execution contract is validation-only; execution_authorized is not true") + + inventory_ref = contract.get("control_inventory") + if not isinstance(inventory_ref, dict): + raise ControlError("contract must bind control_inventory path and SHA-256") + inventory_path = inventory_ref.get("path") + inventory_digest = inventory_ref.get("sha256") + if not isinstance(inventory_digest, str) or not re.fullmatch(r"[0-9a-f]{64}", inventory_digest): + raise ControlError("contract has invalid control_inventory SHA-256") + inventory_rel = _HELPERS._relative(inventory_path, "control inventory path") + inventory_raw = _HELPERS._ref(root, inventory_rel.as_posix(), inventory_digest, "control inventory") + inventory = json.loads(inventory_raw) + inventory_authorized = inventory.get("execution_authorized") + if (inventory.get("schema") != INVENTORY_SCHEMA or inventory.get("release") != RELEASE or + type(inventory_authorized) is not bool): + raise ControlError("control inventory schema, release, or authorization field mismatch") + if require_authorized and not inventory_authorized: + raise ControlError("control inventory is validation-only; execution_authorized is not true") + # If also listed among the general input identities, both pins must agree. + input_identities = contract.get("input_identities") + if not isinstance(input_identities, dict) or not input_identities: + raise ControlError("contract must bind non-empty input_identities") + if inventory_rel.as_posix() in input_identities and input_identities[inventory_rel.as_posix()] != inventory_digest: + raise ControlError("control inventory binding differs from contract input identity") + + for key, required, description in ( + ("parent_plan", _HELPERS.PLAN_PATH, "immutable parent plan"), + ("population", _HELPERS.POPULATION_PATH, "population"), + ): + ref = contract.get(key) + if not isinstance(ref, dict) or ref.get("path") != required: + raise ControlError(f"contract must bind {description} at {required}") + digest = ref.get("sha256") + if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest): + raise ControlError(f"contract has invalid {description} SHA-256") + _HELPERS._ref(root, required, digest, description) + plan, _ = _HELPERS._read_json(root / _HELPERS.PLAN_PATH, "immutable parent plan") + population, _ = _HELPERS._read_json(root / _HELPERS.POPULATION_PATH, "population") + if (plan.get("release") != RELEASE or population.get("population") != RELEASE or + contract.get("fixture_revision") != plan.get("fixture_revision") or + contract.get("fixture_revision") != population.get("fixture_revision")): + raise ControlError("contract, parent plan, and population release or fixture bindings differ") + if contract.get("input_commits") != plan.get("input_commits"): + raise ControlError("contract input commits differ from immutable parent plan") + if contract["population"].get("sha256") != plan.get("population", {}).get("sha256"): + raise ControlError("contract population digest differs from immutable parent plan") + for input_path, digest in input_identities.items(): + if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest): + raise ControlError(f"invalid input identity digest: {input_path}") + _HELPERS._ref(root, input_path, digest, "input identity") + + if not isinstance(control_id, str) or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", control_id): + raise ControlError("control_id must be a path-safe identifier") + controls = inventory.get("controls") + if not isinstance(controls, list): + raise ControlError("control inventory controls must be a list") + matches = [row for row in controls if isinstance(row, dict) and row.get("id") == control_id] + if len(matches) != 1: + raise ControlError(f"control must occur exactly once in hash-bound inventory: {control_id}") + control = matches[0] + control_roots = contract.get("control_execution_roots") + if not isinstance(control_roots, dict) or control_id not in control_roots: + raise ControlError(f"contract lacks an isolated execution root for control: {control_id}") + mapped_root = control_roots[control_id] + if not isinstance(mapped_root, str) or not Path(mapped_root).is_absolute(): + raise ControlError("control execution root must be an absolute path") + if Path(mapped_root).resolve(strict=True) != root: + raise ControlError("supplied execution root differs from contract.control_execution_roots") + + argv = control.get("argv") + if (not isinstance(argv, list) or not argv or + any(not isinstance(arg, str) or not arg or "\x00" in arg for arg in argv) or + any("{" in arg or "}" in arg for arg in argv)): + raise ControlError("control argv must be an explicit, resolved non-empty string array") + env = control.get("environment") + if not isinstance(env, dict) or any(not isinstance(k, str) or not isinstance(v, str) for k, v in env.items()): + raise ControlError("control must bind an explicit string-to-string environment") + deadline = control.get("deadline_seconds") + if type(deadline) is not int or deadline <= 0: + raise ControlError("control deadline_seconds must be a positive integer") + if control.get("maximum_attempts") != 2 or type(control.get("maximum_attempts")) is not int: + raise ControlError("control maximum_attempts must be exactly 2") + repeats = control.get("measurement_repeats") + if type(repeats) is not int or repeats < 1: + raise ControlError("control measurement_repeats must be an explicit positive integer") + mechanism = control.get("repeat_mechanism") + if mechanism not in ("inside-harness-command", "single-control-series"): + raise ControlError("control repeat_mechanism is missing or unsupported") + + roots_value = control.get("output_roots") + if not isinstance(roots_value, list) or not roots_value: + raise ControlError("control must declare complete output_roots") + roots = [_HELPERS._relative(item, "control output root") for item in roots_value] + scratch_rel = SCRATCH_ROOT / control_id + scratch_expected = str((root / Path(*scratch_rel.parts)).resolve(strict=False)) + if env.get("TMPDIR") != scratch_expected or scratch_rel not in roots: + raise ControlError("TMPDIR must be the control-specific declared control-scratch output root") + reserved = [PurePosixPath(ATTEMPTS_PATH), PurePosixPath(LEDGER_PATH), + PurePosixPath(_HELPERS.PLAN_PATH), PurePosixPath(_HELPERS.POPULATION_PATH), + contract_rel, inventory_rel] + for i, output in enumerate(roots): + if any(_HELPERS._inside(output, other) or _HELPERS._inside(other, output) for other in reserved): + raise ControlError(f"control output root overlaps recorder-owned or bound input: {output}") + if any(_HELPERS._inside(output, other) or _HELPERS._inside(other, output) for other in roots[i + 1:]): + raise ControlError("control output roots must be unique and disjoint") + target = _HELPERS._safe_path(root, output, allow_missing=True) + if require_fresh and (target.exists() or target.is_symlink()): + raise ControlError(f"declared control output root is not fresh: {output.as_posix()}") + + # Any declared script identity is checked as such. For command rows that + # omit script_identity, bind script arguments only through explicit pins in + # contract.input_identities; never infer a digest from the file itself. + scripts = control.get("script_identity") + if not isinstance(scripts, list): + scripts = [] + elif any(not isinstance(row, dict) for row in scripts): + raise ControlError("script_identity entries must be objects") + if not scripts and isinstance(control.get("script_sha256"), str): + # Supplemental controls bind their script digest separately. + script_path = control.get("script_path") + if script_path is None and len(argv) > 1 and not Path(argv[1]).is_absolute(): + script_path = argv[1] + scripts = [{"path": script_path, "sha256": control["script_sha256"]}] + if not scripts: + scripts = [] + for arg in argv: + if arg in input_identities: + scripts.append({"path": arg, "sha256": input_identities[arg]}) + if len(argv) > 1 and argv[1] in input_identities and Path(argv[0]).name in ("bash", "sh", "python", "python3"): + scripts.append({"path": argv[1], "sha256": input_identities[argv[1]]}) + scripts = list({row["path"]: row for row in scripts}.values()) + + runner_identity = None + runner = contract.get("tools", {}).get("runner") + runner_build = contract.get("runner_build") + if argv[0] == (runner.get("path") if isinstance(runner, dict) else None): + if not isinstance(runner, dict) or not isinstance(runner_build, dict): + raise ControlError("runner command lacks tools.runner and runner_build provenance") + binary_path = Path(runner["path"]) + expected_sha = runner.get("sha256") + if (not binary_path.is_absolute() or binary_path.is_symlink() or + not isinstance(expected_sha, str) or not re.fullmatch(r"[0-9a-f]{64}", expected_sha)): + raise ControlError("runner binary identity is invalid") + try: + binary_sha = _sha(binary_path.read_bytes()) + except OSError as exc: + raise ControlError(f"cannot read pinned runner binary: {exc}") from exc + if (binary_sha != expected_sha or runner_build.get("binary_path") != str(binary_path) or + runner_build.get("binary_sha256") != expected_sha): + raise ControlError("tools.runner and runner_build binary identity mismatch") + if not isinstance(runner_build.get("source_commit"), str) or not isinstance(runner_build.get("source_files"), dict): + raise ControlError("runner_build lacks source commit or source file provenance") + runner_identity = {"path": str(binary_path), "sha256": binary_sha, + "source_commit": runner_build["source_commit"], + "source_files": runner_build["source_files"], + "runner_build_schema": runner_build.get("schema"), + "runner_build_sha256": _sha(_json_bytes(runner_build))} + if not scripts: + if runner_identity is None: + raise ControlError("control must bind a script SHA-256 or exact contract.tools.runner identity") + identities = [] + seen_script_paths = set() + for row in scripts: + script_path, digest = row.get("path"), row.get("sha256") + if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest): + raise ControlError("control script identity has an invalid SHA-256") + script_rel = _HELPERS._relative(script_path, "control script identity path") + if script_rel.as_posix() in seen_script_paths: + raise ControlError("duplicate control script identity path") + seen_script_paths.add(script_rel.as_posix()) + _HELPERS._ref(root, script_rel.as_posix(), digest, "control script") + identities.append({"path": script_rel.as_posix(), "sha256": digest}) + + attempts_dir = _HELPERS._safe_path(root, PurePosixPath(ATTEMPTS_PATH) / control_id, allow_missing=True) + if attempts_dir.exists() and not attempts_dir.is_dir(): + raise ControlError("control attempt path is not a directory") + if require_fresh and attempts_dir.exists() and any(attempts_dir.iterdir()): + raise ControlError("an existing control attempt is immutable; another launch is disabled") + + attempt_base = _HELPERS._safe_path(root, PurePosixPath(ATTEMPTS_PATH), allow_missing=True) + if attempt_base.exists() and not attempt_base.is_dir(): + raise ControlError("control attempt path is not a directory") + return { + "control_id": control_id, + "control": dict(control), + "execution_root": str(root), + "output_roots": [item.as_posix() for item in roots], + "scratch_root": scratch_rel.as_posix(), + "script_identity": identities, + "runner_identity": runner_identity, + "control_inventory": {"path": inventory_rel.as_posix(), "sha256": _sha(inventory_raw)}, + "contract_sha256": _sha(contract_raw), + "parent_plan_sha256": _sha((root / _HELPERS.PLAN_PATH).read_bytes()), + "population_sha256": _sha((root / _HELPERS.POPULATION_PATH).read_bytes()), + "authorized": authorized and inventory_authorized, + } + + +def validate_control(root, contract_path, control_id, *, require_authorized=True, require_fresh=True): + """Read-only validation; result['control'] is the selected inventory row.""" + return _load_bound_control(root, contract_path, control_id, + require_authorized=require_authorized, require_fresh=require_fresh) + + +def _allocate_attempt(root: Path, control_id: str) -> tuple[Path, str, int]: + safe_id = re.sub(r"[^A-Za-z0-9_-]", "-", control_id) + base_rel = PurePosixPath(ATTEMPTS_PATH) / safe_id + base = _HELPERS._safe_path(root, base_rel, allow_missing=True) + base.mkdir(parents=True, exist_ok=True) + path = base / "attempt-01" + try: + path.mkdir(exist_ok=False) + except FileExistsError as exc: + raise ControlError("an existing control attempt is immutable; another launch is disabled") from exc + return path, f"{safe_id}-attempt-01", 1 + + +def _append_ledger(path: Path, row: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as ledger: + fcntl.flock(ledger.fileno(), fcntl.LOCK_EX) + ledger.write(json.dumps(row, sort_keys=True, separators=(",", ":")) + "\n") + ledger.flush() + os.fsync(ledger.fileno()) + fcntl.flock(ledger.fileno(), fcntl.LOCK_UN) + + +def _capture_roots(root: Path, attempt: Path, roots: list[str]) -> tuple[list[dict], list[str], list[str]]: + files, missing, errors = [], [], [] + for value in roots: + relative = _HELPERS._relative(value, "control output root") + source = _HELPERS._safe_path(root, relative, allow_missing=True) + if not source.exists() and not source.is_symlink(): + missing.append(value) + continue + target = attempt / "capture" / Path(*relative.parts) + try: + files.extend(_HELPERS._copy_tree(root, relative, target)) + except Exception as exc: + errors.append(f"{value}: {type(exc).__name__}: {exc}") + return sorted(files, key=lambda row: row["path"]), missing, errors + + +def run_control(root, contract_path, control_id) -> dict: + """Run one reviewed control once, retaining every started attempt and output. + + The caller owns host reservation/launch gating and serial ordering. Validation + errors do not allocate an attempt. A started invocation is never retried here. + """ + validated = validate_control(root, contract_path, control_id, require_authorized=True, require_fresh=True) + contract = json.loads((_HELPERS._safe_path(Path(root).resolve(strict=True), + _HELPERS._relative(Path(contract_path).as_posix(), "contract path"), allow_missing=False)).read_bytes()) + if contract.get("unresolved"): + raise ControlError("execution contract has unresolved items; control launch is blocked") + root = Path(validated["execution_root"]) + control = validated["control"] + attempt, attempt_id, number = _allocate_attempt(root, control_id) + start = _timestamp() + row = { + "schema": "release-control-attempt/v1", "release": RELEASE, + "control_id": control_id, "attempt_id": attempt_id, "attempt_number": number, + "status": "started", "start_utc": start, "execution_root": str(root), + "contract": {"path": Path(contract_path).as_posix(), "sha256": validated["contract_sha256"]}, + "control_inventory": validated["control_inventory"], + "parent_plan_sha256": validated["parent_plan_sha256"], + "population_sha256": validated["population_sha256"], + "script_identity": validated["script_identity"], + "runner_identity": validated["runner_identity"], + "argv": control["argv"], "environment": control["environment"], + "environment_sha256": _sha(_json_bytes(control["environment"])), + "output_roots": validated["output_roots"], + "measurement_repeats": control["measurement_repeats"], + "repeat_mechanism": control["repeat_mechanism"], + "maximum_attempts": 2, "deadline_seconds": control["deadline_seconds"], + "started_receipt": "started.json", + } + started_path = attempt / "started.json" + with started_path.open("xb") as stream: + stream.write(_json_bytes(row)) + stream.flush() + os.fsync(stream.fileno()) + + stdout_path, stderr_path = attempt / "stdout.txt", attempt / "stderr.txt" + stdout_path.touch(exist_ok=False) + stderr_path.touch(exist_ok=False) + captured_once = False + try: + scratch = _HELPERS._safe_path(root, _HELPERS._relative(validated["scratch_root"], "scratch root"), allow_missing=True) + scratch.mkdir(parents=True, exist_ok=False) + exit_code, timed_out, duration = _HELPERS._run( + control["argv"], root, control["environment"], stdout_path, + stderr_path, control["deadline_seconds"]) + row.update({"exit_code": exit_code, "timed_out": timed_out, "duration_seconds": duration}) + # Recheck the exact inventory, inputs and scripts against the original pins. + current = validate_control(root, contract_path, control_id, require_authorized=True, require_fresh=False) + for key in ("contract_sha256", "control_inventory", "parent_plan_sha256", "population_sha256", "script_identity", "runner_identity"): + if current[key] != validated[key]: + raise ControlError(f"{key} changed during control execution") + row["stdout"] = {"path": "stdout.txt", "sha256": _sha((attempt / "stdout.txt").read_bytes())} + row["stderr"] = {"path": "stderr.txt", "sha256": _sha((attempt / "stderr.txt").read_bytes())} + captured, missing, errors = _capture_roots(root, attempt, validated["output_roots"]) + captured_once = True + row["captured_files"], row["missing_output_roots"] = captured, missing + if errors: + row["capture_errors"] = errors + raise ControlError("one or more declared control output roots could not be captured") + if exit_code == 0 and not timed_out and missing: + raise ControlError("successful control omitted one or more declared output roots") + row["status"] = "timed-out" if timed_out else ("completed" if exit_code == 0 else "failed") + except BaseException as exc: + row["status"] = "recorder-error" + row["recorder_error"] = f"{type(exc).__name__}: {exc}" + if not captured_once: + captured, missing, errors = _capture_roots(root, attempt, validated["output_roots"]) + captured_once = True + row["captured_files"] = captured + row["missing_output_roots"] = missing + if errors: + row["capture_errors"] = errors + finally: + row["stdout"] = {"path": "stdout.txt", "sha256": _sha(stdout_path.read_bytes())} + row["stderr"] = {"path": "stderr.txt", "sha256": _sha(stderr_path.read_bytes())} + row["end_utc"] = _timestamp() + row["started_receipt_sha256"] = _sha(started_path.read_bytes()) + completed_path = attempt / "completed.json" + with completed_path.open("xb") as stream: + stream.write(_json_bytes(row)) + stream.flush() + os.fsync(stream.fileno()) + _append_ledger(root / LEDGER_PATH, row) + return row + + +def _captured_manifest(capture_root: Path) -> list[dict]: + if not capture_root.exists(): + return [] + rows = [] + for current, dirs, files in os.walk(capture_root, topdown=True, followlinks=False): + current_path = Path(current) + for name in dirs: + if (current_path / name).is_symlink(): + raise ControlError("symlink found in captured control outputs") + for name in files: + path = current_path / name + if path.is_symlink() or not path.is_file(): + raise ControlError("non-regular file found in captured control outputs") + raw = path.read_bytes() + rel = path.relative_to(capture_root).as_posix() + rows.append({"path": rel, "sha256": _sha(raw), "bytes": len(raw)}) + return sorted(rows, key=lambda row: row["path"]) + + +def verify_control(root, receipt_path, contract_path) -> dict: + """Verify receipt, locked ledger row, script/input pins and captured bytes.""" + root = Path(root).resolve(strict=True) + receipt_arg = Path(receipt_path) + if receipt_arg.is_absolute(): + try: + receipt_value = receipt_arg.relative_to(root).as_posix() + except ValueError as exc: + raise ControlError("control receipt path is outside execution root") from exc + else: + receipt_value = receipt_arg.as_posix() + receipt_rel = _HELPERS._relative(receipt_value, "control receipt path") + receipt = _HELPERS._safe_path(root, receipt_rel, allow_missing=False) + if receipt.name not in ("started.json", "completed.json"): + raise ControlError("receipt_path must name started.json or completed.json") + attempt = receipt.parent + expected_prefix = PurePosixPath(ATTEMPTS_PATH) + if (len(receipt_rel.parts) != len(expected_prefix.parts) + 3 or + receipt_rel.parts[:len(expected_prefix.parts)] != expected_prefix.parts or + receipt_rel.parts[-2] not in ("attempt-01", "attempt-02")): + raise ControlError("receipt path is outside the versioned control-attempt ledger") + started_path, completed_path = attempt / "started.json", attempt / "completed.json" + started, started_raw = _HELPERS._read_json(started_path, "started control receipt") + completed, completed_raw = _HELPERS._read_json(completed_path, "completed control receipt") + if _sha(started_raw) != completed.get("started_receipt_sha256"): + raise ControlError("started control receipt digest mismatch") + if completed.get("status") not in ("completed", "failed", "timed-out", "recorder-error"): + raise ControlError("control receipt is not terminal") + if started.get("status") != "started" or started.get("attempt_id") != completed.get("attempt_id"): + raise ControlError("started and completed receipt identities differ") + if any(started.get(key) != completed.get(key) for key in started if key != "status"): + raise ControlError("completed receipt changed immutable started fields") + if receipt not in (started_path, completed_path): + raise ControlError("receipt path does not resolve to this attempt") + + validated = _load_bound_control(root, contract_path, completed.get("control_id"), + require_authorized=False, require_fresh=False) + expected_contract = {"path": Path(contract_path).as_posix(), "sha256": validated["contract_sha256"]} + for field, expected in (("contract", expected_contract), + ("control_inventory", validated["control_inventory"]), + ("parent_plan_sha256", validated["parent_plan_sha256"]), + ("population_sha256", validated["population_sha256"]), + ("script_identity", validated["script_identity"]), + ("runner_identity", validated["runner_identity"])): + if started.get(field) != expected: + raise ControlError(f"receipt {field} differs from current hash-bound contract") + control = validated["control"] + for field in ("argv", "environment", "output_roots", "measurement_repeats", "repeat_mechanism", "deadline_seconds"): + expected = validated["output_roots"] if field == "output_roots" else control.get(field) + if started.get(field) != expected: + raise ControlError(f"receipt {field} differs from reviewed control") + expected_attempt_id = f"{re.sub(r'[^A-Za-z0-9_-]', '-', completed.get('control_id', ''))}-attempt-{receipt_rel.parts[-2][-2:]}" + if (started.get("execution_root") != str(root) or started.get("maximum_attempts") != 2 or + completed.get("attempt_id") != expected_attempt_id): + raise ControlError("receipt execution root or attempt limit mismatch") + if _sha((attempt / "stdout.txt").read_bytes()) != completed.get("stdout", {}).get("sha256"): + raise ControlError("captured stdout digest mismatch") + if _sha((attempt / "stderr.txt").read_bytes()) != completed.get("stderr", {}).get("sha256"): + raise ControlError("captured stderr digest mismatch") + if completed.get("status") == "completed" and completed.get("missing_output_roots"): + raise ControlError("completed control has missing declared output roots") + declared = completed.get("captured_files") + if not isinstance(declared, list) or len({row.get("path") for row in declared if isinstance(row, dict)}) != len(declared): + raise ControlError("captured file manifest is invalid") + for item in declared: + if not isinstance(item, dict) or not isinstance(item.get("path"), str): + raise ControlError("captured file manifest row is invalid") + rel = _HELPERS._relative(item["path"], "captured file path") + path = _HELPERS._safe_path(attempt / "capture", rel, allow_missing=False) + raw = path.read_bytes() + if _sha(raw) != item.get("sha256") or len(raw) != item.get("bytes"): + raise ControlError(f"captured output digest mismatch: {rel}") + if _captured_manifest(attempt / "capture") != sorted(declared, key=lambda row: row["path"]): + raise ControlError("captured output tree differs from immutable manifest") + ledger = _HELPERS._safe_path(root, PurePosixPath(LEDGER_PATH), allow_missing=False) + rows = [] + for line in ledger.read_text(encoding="utf-8").splitlines(): + try: + rows.append(json.loads(line)) + except json.JSONDecodeError as exc: + raise ControlError(f"malformed control ledger row: {exc}") from exc + matches = [row for row in rows if row.get("attempt_id") == completed.get("attempt_id")] + if len(matches) != 1 or matches[0] != completed: + raise ControlError("completed receipt does not match exactly one control-ledger row") + return {"control_id": completed["control_id"], "attempt_id": completed["attempt_id"], + "status": completed["status"], "captured_files": len(declared), "verified": True} diff --git a/scripts/run-release-control-v090.py b/scripts/run-release-control-v090.py new file mode 100644 index 000000000..8043e43db --- /dev/null +++ b/scripts/run-release-control-v090.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Validate or capture one reviewed non-report control under the shared host lock.""" +from __future__ import annotations +import argparse +import fcntl +import importlib.util +import json +import os +from pathlib import Path + +from release_attempt_v090 import AttemptError, SERIAL_LOCK_PATH + + +def launch_control(root, contract_path, control_id, *, execute=False): + from release_control_attempt_v090 import run_control, validate_control + root = Path(root).resolve() + validate_control(root, contract_path, control_id, require_authorized=execute) + if not execute: + return {'mode': 'validation-only', 'control_id': control_id} + contract = json.loads((root / contract_path).read_text()) + inventory = json.loads((root / contract['control_inventory']['path']).read_text()) + control = next(c for c in inventory['controls'] if c['id'] == control_id) + spec = importlib.util.spec_from_file_location('release_launch_gate', root / 'scripts/run-release-group-v090.py') + gate = importlib.util.module_from_spec(spec) + spec.loader.exec_module(gate) + flags = os.O_RDWR | os.O_CREAT | getattr(os, 'O_NOFOLLOW', 0) + descriptor = os.open(SERIAL_LOCK_PATH, flags, 0o600) + with os.fdopen(descriptor, 'r+') as lock: + try: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError as exc: + raise AttemptError('another release operation owns the analyzer slot') from exc + gate.launch_gate(root, contract, control) + return run_control(root, contract_path, control_id) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--root', type=Path, required=True) + parser.add_argument('--contract', default='reports/releases/v0.9.0/execution-v1/contract.json') + parser.add_argument('--control', required=True) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + result = launch_control(args.root, args.contract, args.control, execute=args.execute) + print(json.dumps(result, sort_keys=True)) + return 0 if not args.execute or result.get('status') == 'completed' else 1 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/scripts/run-release-group-v090.py b/scripts/run-release-group-v090.py index 0b540f609..8e783e175 100644 --- a/scripts/run-release-group-v090.py +++ b/scripts/run-release-group-v090.py @@ -20,9 +20,16 @@ def launch_gate(root, contract, group): head = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=root, text=True).strip() if contract.get('harness_commit') != head: raise AttemptError('execution checkout is not the exact reviewed harness') + changed = subprocess.check_output( + ['git', 'diff', '--name-only', 'HEAD', '--', 'src', 'cases', 'populations', + 'adapters', 'scripts', 'Cargo.toml', 'Cargo.lock'], cwd=root, text=True) + if changed.strip(): + raise AttemptError('execution source differs from the reviewed harness: ' + changed.strip()) # A single designated root per group prevents a fresh checkout resetting # the recorder-local two-attempt bound. The separate preparer reserves it. designated = contract.get('execution_roots', {}).get(group['id']) + if designated is None: + designated = contract.get('control_execution_roots', {}).get(group['id']) if not designated or Path(designated).resolve() != root: raise AttemptError('group execution root is not explicitly designated') runner = contract['tools']['runner'] @@ -47,10 +54,12 @@ def launch_gate(root, contract, group): if shutil.disk_usage(root).free < budget['minimum_launch_free_gib'] * 1024**3: raise AttemptError('launch capacity below reviewed floor') used = 0 - for owned_root in set(contract['execution_roots'].values()): - retained = Path(owned_root) / 'reports/releases/v0.9.0/attempts' - if retained.exists(): - used += sum(p.stat().st_size for p in retained.rglob('*') if p.is_file()) + owned_roots = set(contract['execution_roots'].values()) | set(contract.get('control_execution_roots', {}).values()) + for owned_root in owned_roots: + for category in ('attempts', 'control-attempts'): + retained = Path(owned_root) / 'reports/releases/v0.9.0' / category + if retained.exists(): + used += sum(p.stat().st_size for p in retained.rglob('*') if p.is_file()) window_start = reservation.get('window_started_at_unix_seconds') if not isinstance(window_start, (int, float)) or window_start > time.time(): raise AttemptError('recorded release window start required') diff --git a/scripts/test-execute-release-controls-v090.py b/scripts/test-execute-release-controls-v090.py new file mode 100644 index 000000000..826e9e889 --- /dev/null +++ b/scripts/test-execute-release-controls-v090.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +"""The control orchestrator cannot silently reset attempts or drift membership.""" +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest +from unittest.mock import patch + +spec = importlib.util.spec_from_file_location('controls', Path(__file__).with_name('execute-release-controls-v090.py')) +controls = importlib.util.module_from_spec(spec) +spec.loader.exec_module(controls) + + +class ControlTests(unittest.TestCase): + def test_requires_immutable_commit(self): + with self.assertRaisesRegex(ValueError, 'exact reviewed'): + controls.reviewed_inventory(Path('.'), 'main', 'contract.json') + + def test_tampered_inventory_rejected(self): + contract = {'control_inventory': {'path': 'inventory.json', 'sha256': '0'*64}} + with patch.object(controls.subprocess, 'check_output', side_effect=[json.dumps(contract).encode(), b'{}']): + with self.assertRaisesRegex(ValueError, 'differs'): + controls.reviewed_inventory(Path('.'), 'a'*40, 'contract.json') + + def test_unauthorized_execution_never_prepares_root(self): + with tempfile.TemporaryDirectory() as tmp, patch.object(controls, 'reviewed_inventory', return_value=({'execution_authorized': False}, [])), patch.object(controls.subprocess, 'run') as run, patch('sys.argv', ['controls', '--source', tmp, '--plan-commit', 'a'*40, '--execute']): + with self.assertRaisesRegex(ValueError, 'final executable-plan'): + controls.main() + run.assert_not_called() + + def test_existing_root_never_restarted(self): + with tempfile.TemporaryDirectory() as tmp: + contract = {'execution_authorized': True, 'unresolved': [], 'control_execution_roots': {'one': tmp}} + with patch.object(controls, 'reviewed_inventory', return_value=(contract, [{'id': 'one'}])), patch.object(controls.subprocess, 'run') as run, patch('sys.argv', ['controls', '--source', tmp, '--plan-commit', 'a'*40, '--execute']): + with self.assertRaisesRegex(ValueError, 'resume review'): + controls.main() + run.assert_not_called() + + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/test-prepare-release-root-v090.py b/scripts/test-prepare-release-root-v090.py index 590cfdf11..6434f118b 100644 --- a/scripts/test-prepare-release-root-v090.py +++ b/scripts/test-prepare-release-root-v090.py @@ -59,6 +59,118 @@ def test_unsafe_path_rejected(self): with self.subTest(path=path), self.assertRaises(ValueError): prepare.safe_relative(path) + def make_control_fixture(self, base, *, duplicate_roots=False, tamper_inventory=False, + reviewed_script_overlay=False): + source = base/'source' + destination = base/'controls'/'java-modeling' + source.mkdir() + + def git(*args): + return subprocess.check_output(['git', '-C', str(source), *args], stderr=subprocess.DEVNULL).decode().strip() + + def write(relative, data): + path = source/relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data if isinstance(data, bytes) else data.encode()) + + git('init') + git('config', 'user.email', 'fixture@example.test') + git('config', 'user.name', 'Fixture') + git('config', 'commit.gpgsign', 'false') + write('config.txt', 'harness config\n') + write('scripts/java-control.sh', '#!/bin/sh\necho java\n') + write('scripts/other-control.sh', '#!/bin/sh\necho other\n') + output = 'reports/raw/java-modeling' + write(output + '/source-evidence.txt', 'preserve source evidence\n') + write(output + '/scratch/stale.txt', 'stale scratch\n') + write('reports/raw/other/keep.txt', 'other control output\n') + git('add', '.') + git('commit', '-m', 'exact control harness') + harness = git('rev-parse', 'HEAD') + + inventory_path = 'reports/releases/v0.9.0/execution-v1/control-inventory.json' + contract_path = 'reports/releases/v0.9.0/execution-v1/contract.json' + inventory = { + 'controls': [ + {'id': 'java-modeling', 'script_identity': [ + {'path': 'scripts/java-control.sh', 'sha256': hashlib.sha256(b'#!/bin/sh\necho java\n').hexdigest()}], + 'output_roots': [output, output + '/scratch']}, + {'id': 'other', 'script_identity': [ + {'path': 'scripts/other-control.sh', 'sha256': hashlib.sha256(b'#!/bin/sh\necho other\n').hexdigest()}], + 'output_roots': ['reports/raw/other']}, + ] + } + inventory_bytes = json.dumps(inventory, sort_keys=True).encode() + if reviewed_script_overlay: + write('scripts/java-control.sh', '#!/bin/sh\necho changed\n') + inventory['controls'][0]['script_identity'][0]['sha256'] = hashlib.sha256( + b'#!/bin/sh\necho changed\n').hexdigest() + inventory_bytes = json.dumps(inventory, sort_keys=True).encode() + inventory_sha = hashlib.sha256(inventory_bytes).hexdigest() + identities = { + 'config.txt': hashlib.sha256(b'harness config\n').hexdigest(), + inventory_path: inventory_sha, + } + roots = {'java-modeling': str(destination), + 'other': str(destination if duplicate_roots else base/'controls'/'other')} + contract = { + 'harness_commit': harness, + 'input_identities': identities, + 'control_inventory': {'path': inventory_path, 'sha256': inventory_sha}, + 'control_execution_roots': roots, + 'groups': [], + } + write(inventory_path, inventory_bytes) + if tamper_inventory: + write(inventory_path, inventory_bytes + b' ') + write(contract_path, json.dumps(contract, sort_keys=True).encode()) + git('add', '.') + git('commit', '-m', 'reviewed control plan descendant') + plan = git('rev-parse', 'HEAD') + return source, destination, plan, contract_path, output + + def test_control_root_clears_only_selected_roots_in_isolated_checkout(self): + with tempfile.TemporaryDirectory() as temporary: + base = Path(temporary).resolve() + source, dest, plan, contract_path, output = self.make_control_fixture(base) + receipt = prepare.prepare_control_root(source, dest, plan, contract_path, 'java-modeling') + self.assertEqual(receipt['control_id'], 'java-modeling') + self.assertEqual(receipt['harness_commit'], + subprocess.check_output(['git', '-C', str(source), 'rev-parse', plan + '^'], text=True).strip()) + self.assertFalse((dest/output).exists()) + self.assertEqual((source/output/'source-evidence.txt').read_text(), 'preserve source evidence\n') + self.assertEqual((source/output/'scratch/stale.txt').read_text(), 'stale scratch\n') + self.assertEqual((dest/'config.txt').read_text(), 'harness config\n') + self.assertTrue((dest/'scripts/java-control.sh').exists()) + self.assertEqual((dest/'reports/raw/other/keep.txt').read_text(), 'other control output\n') + with self.assertRaisesRegex(ValueError, 'already exists'): + prepare.prepare_control_root(source, dest, plan, contract_path, 'java-modeling') + + def test_control_roots_must_be_unique(self): + with tempfile.TemporaryDirectory() as temporary: + base = Path(temporary).resolve() + source, dest, plan, contract_path, _ = self.make_control_fixture(base, duplicate_roots=True) + with self.assertRaisesRegex(ValueError, 'must be unique'): + prepare.prepare_control_root(source, dest, plan, contract_path, 'java-modeling') + self.assertFalse(dest.exists()) + + def test_control_inventory_overlay_tamper_rejected(self): + with tempfile.TemporaryDirectory() as temporary: + base = Path(temporary).resolve() + source, dest, plan, contract_path, _ = self.make_control_fixture(base, tamper_inventory=True) + with self.assertRaisesRegex(ValueError, 'inventory digest mismatch'): + prepare.prepare_control_root(source, dest, plan, contract_path, 'java-modeling') + self.assertFalse(dest.exists()) + + def test_control_script_overlay_must_match_harness_even_when_rehashed(self): + with tempfile.TemporaryDirectory() as temporary: + base = Path(temporary).resolve() + source, dest, plan, contract_path, _ = self.make_control_fixture( + base, reviewed_script_overlay=True) + with self.assertRaisesRegex(ValueError, 'differs from exact harness checkout'): + prepare.prepare_control_root(source, dest, plan, contract_path, 'java-modeling') + self.assertFalse(dest.exists()) + if __name__ == '__main__': unittest.main() diff --git a/scripts/test-probe-opentaint-product-v090.py b/scripts/test-probe-opentaint-product-v090.py new file mode 100644 index 000000000..b990891a3 --- /dev/null +++ b/scripts/test-probe-opentaint-product-v090.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +"""Mock-only contract, identity, receipt and interruption tests for v0.9 probe.""" + +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import shutil +import signal +import tempfile +import unittest +from unittest.mock import Mock, call, patch + + +SCRIPT = Path(__file__).with_name("probe-opentaint-product-v090.py") +SPEC = importlib.util.spec_from_file_location("opentaint_product_v090", SCRIPT) +assert SPEC and SPEC.loader +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) + + +def sha(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +class Fixture: + def __init__(self, base: Path): + self.root = base + self.bundle = base / "bundle" + self.bundle.mkdir() + self._write(self.bundle / "opentaint", b"mock wrapper bytes", 0o755) + self._write(self.bundle / "jre/bin/java", b"mock bundled java", 0o755) + self._write(self.bundle / "lib/rules/builtin.yaml", b"mock builtin rules") + filler = self.bundle / "payload" + filler.mkdir() + # The restoration points at the historical 329-file identity. + for index in range(326): + self._write(filler / f"member-{index:03}.bin", f"member {index}\n".encode()) + files = sorted(p for p in self.bundle.rglob("*") if p.is_file()) + assert len(files) == 329 + self.historical_rel = Path("reports/releases/v0.7.1/opentaint-full-bundle-identity.json") + self.historical_path = self.root / self.historical_rel + self.historical_path.parent.mkdir(parents=True) + archive_sha = "a" * 64 + historical = { + "archive_sha256": archive_sha, + "files": [{"relative_path": p.relative_to(self.bundle).as_posix(), "sha256": sha(p.read_bytes())} + for p in files], + } + self._json(self.historical_path, historical) + self.restoration_rel = MODULE.RESTORATION_REL + self.restoration_path = self.root / self.restoration_rel + self.restoration_path.parent.mkdir(parents=True) + restoration = { + "schema": "runtime-restoration/v1", "bundle_root": str(self.bundle), + "archive_sha256": archive_sha, "exact_file_membership": True, + "runtime_executed": False, "verified_files": 329, + "reference": {"path": self.historical_rel.as_posix(), "sha256": sha(self.historical_path.read_bytes())}, + } + self._json(self.restoration_path, restoration) + self.runtime_rel = MODULE.RUNTIME_TREE_REL + self.runtime_path = self.root / self.runtime_rel + self.runtime_path.parent.mkdir(parents=True, exist_ok=True) + runtime_tree = { + "schema": "release-runtime-tree/v1", "root": str(self.bundle), + "entries": MODULE._tree_entries(self.bundle), "external_files": {}, + } + self._json(self.runtime_path, runtime_tree) + + javac = base / "jdk/bin/javac" + self._write(javac, b"mock contract javac", 0o755) + self.javac = javac + self.control_root = base / "isolated-control-root" + self.tmpdir = self.control_root / "reports/raw/control-scratch/probe-opentaint-product-v090" + self.tmpdir.mkdir(parents=True) + (self.root / "scripts").mkdir() + self.probe_script = self.root / MODULE.SCRIPT_REL + shutil.copyfile(SCRIPT, self.probe_script) + + self.control_rel = Path("reports/releases/v0.9.0/execution-v1/control-inventory.json") + self.control_path = self.root / self.control_rel + self.control_path.parent.mkdir(parents=True, exist_ok=True) + control = { + "controls": [{ + "id": "probe-opentaint-product-v090", + "argv": ["/usr/bin/python3", MODULE.SCRIPT_REL.as_posix()], + "deadline_seconds": 10200, + "environment": {"HOME": str(base), "PATH": "/system/bin", "TMPDIR": str(self.tmpdir)}, + "output_roots": [MODULE.OUTPUT_REL.as_posix(), "reports/raw/control-scratch/probe-opentaint-product-v090"], + "script_identity": [{"path": MODULE.SCRIPT_REL.as_posix(), "sha256": sha(self.probe_script.read_bytes())}], + }] + } + self._json(self.control_path, control) + contract = { + "schema": "release-execution-contract/v1", "release": "v0.9.0", + "execution_authorized": True, "unresolved": [], + "tools": { + "opentaint-wrapper": {"path": str(self.bundle / "opentaint"), + "sha256": sha((self.bundle / "opentaint").read_bytes()), "version": "0.4.6"}, + "javac": {"path": str(javac), "sha256": sha(javac.read_bytes())}, + }, + "groups": [{"id": "opentaint-java-native", "tool": "opentaint", + "environment": {"HOME": str(base), "JAVA_HOME": "/reviewed/jdk", + "LANG": "C.UTF-8", "PATH": "/reviewed/jdk/bin:/usr/bin", + "TMPDIR": "/private/tmp"}}], + "runtime_trees": [{"path": self.runtime_rel.as_posix(), "sha256": sha(self.runtime_path.read_bytes())}], + "control_inventory": {"path": self.control_rel.as_posix(), "sha256": sha(self.control_path.read_bytes())}, + "control_execution_roots": {"probe-opentaint-product-v090": str(self.control_root)}, + "input_identities": { + self.restoration_rel.as_posix(): sha(self.restoration_path.read_bytes()), + self.runtime_rel.as_posix(): sha(self.runtime_path.read_bytes()), + self.control_rel.as_posix(): sha(self.control_path.read_bytes()), + }, + } + self.contract_rel = Path("reports/releases/v0.9.0/execution-v1/contract.json") + self.contract_path = self.root / self.contract_rel + self._json(self.contract_path, contract) + + cases = self.root / "cases/taint/java" + for index in range(12): + case = cases / f"native-case-{index:02}" + case.mkdir(parents=True) + (case / f"Case{index}.java").write_text( + f"package dataflowbench.taint; class Case{index} {{}}\n", encoding="utf-8") + + @staticmethod + def _write(path: Path, data: bytes, mode: int = 0o644) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data) + path.chmod(mode) + + @staticmethod + def _json(path: Path, value: object) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, sort_keys=True) + "\n", encoding="utf-8") + + +class ProductProbeV090Tests(unittest.TestCase): + def setUp(self) -> None: + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.fx = Fixture(Path(self.temp.name)) + self.output = self.fx.root / MODULE.OUTPUT_REL + + def capture(self, runner): + return MODULE.capture( + self.fx.root, self.fx.contract_rel, MODULE.OUTPUT_REL, + probe_script_path=self.fx.probe_script, runner=runner, + ) + + @staticmethod + def successful_runner(calls): + def run(argv, *, cwd, stdout, stderr, env, timeout): + calls.append((list(map(str, argv)), dict(env), timeout)) + stdout.write(b"mock stdout\n") + stderr.write(b"mock stderr\n") + return 0 + return run + + def test_authorization_gate_has_no_output_or_runner_side_effect(self): + contract = json.loads(self.fx.contract_path.read_text()) + contract["execution_authorized"] = False + Fixture._json(self.fx.contract_path, contract) + calls = [] + with self.assertRaisesRegex(MODULE.ProbeError, "execution_authorized"): + self.capture(self.successful_runner(calls)) + self.assertFalse(self.output.exists()) + self.assertEqual(calls, []) + + def test_bundle_membership_is_verified_before_output_creation(self): + (self.fx.bundle / "unregistered.bin").write_bytes(b"extra") + calls = [] + with self.assertRaisesRegex(MODULE.ProbeError, "runtime tree membership"): + self.capture(self.successful_runner(calls)) + self.assertFalse(self.output.exists()) + self.assertEqual(calls, []) + + def test_runs_mocked_help_and_all_fourteen_fixture_pairs_with_scoped_envs(self): + calls = [] + self.assertEqual(self.capture(self.successful_runner(calls)), 0) + self.assertEqual(len(calls), 31) + records = [json.loads(line) for line in (self.output / "commands.jsonl").read_text().splitlines()] + self.assertEqual(len(records), 31) + self.assertEqual(sum(row["command_id"].endswith("-compile") for row in records), 14) + self.assertEqual(sum(row["command_id"].endswith("-product") for row in records), 14) + self.assertEqual(len([row for row in records if row["command_id"] in {"wrapper-version", "wrapper-help", "scan-help"}]), 3) + compile_call = next(row for row in calls if "-nowarn" in row[0]) + product_call = next(row for row in calls if "--project-model" in row[0]) + self.assertEqual(compile_call[1]["JAVA_HOME"], "/reviewed/jdk") + self.assertEqual(compile_call[1]["TMPDIR"], str(self.fx.tmpdir)) + self.assertEqual(product_call[1]["JAVA_HOME"], str(self.fx.bundle / "jre")) + self.assertTrue(product_call[1]["PATH"].startswith(str(self.fx.bundle / "jre/bin") + os.pathsep)) + self.assertEqual(product_call[1]["TMPDIR"], str(self.fx.tmpdir)) + scope = json.loads((self.output / "scope.json").read_text()) + self.assertEqual(len(scope["native_fixtures"]), 12) + self.assertEqual(scope["outer_deadline_seconds"], 10200) + self.assertEqual(scope["nested_timeouts"], []) + self.assertFalse((self.output / "reports/releases/v0.9.0").exists()) + with self.assertRaises(FileExistsError): + self.capture(self.successful_runner([])) + + def test_nonzero_compile_is_retained_and_later_controls_continue(self): + calls = [] + + def run(argv, *, cwd, stdout, stderr, env, timeout): + calls.append(list(map(str, argv))) + stdout.write(b"captured\n") + stderr.write(b"captured\n") + return 9 if "-nowarn" in argv and not any("-nowarn" in x for x in calls[:-1]) else 0 + + self.assertEqual(self.capture(run), 1) + records = [json.loads(line) for line in (self.output / "commands.jsonl").read_text().splitlines()] + self.assertEqual(records[3]["status"], "failed") + self.assertEqual(records[4]["status"], "skipped-after-prerequisite-failure") + self.assertTrue(any(row["command_id"] == "servlet-positive-product" and row["status"] == "succeeded" for row in records)) + self.assertTrue((self.output / "native-case-00-compile-stderr.txt").exists()) + self.assertEqual(len(calls), 30) + + def test_interrupt_cleans_child_group_and_command_recorder_persists_failure(self): + process = Mock() + process.pid = 41234 + first_wait = True + + def wait(timeout=None): + nonlocal first_wait + if first_wait: + first_wait = False + os.kill(os.getpid(), signal.SIGTERM) + return 0 + + process.wait.side_effect = wait + with tempfile.TemporaryDirectory() as directory: + out = Path(directory) + with patch.object(MODULE.subprocess, "Popen", return_value=process), \ + patch.object(MODULE.os, "killpg") as killpg: + with (out / "o").open("wb") as stdout, (out / "e").open("wb") as stderr: + with self.assertRaises(MODULE.ProbeInterrupted): + MODULE._run_bounded(["mock"], cwd=out, stdout=stdout, stderr=stderr, + env={}, timeout=1) + self.assertEqual(killpg.call_args_list, [call(41234, signal.SIGTERM), call(41234, signal.SIGKILL)]) + + command_out = out / "command-out" + command_out.mkdir() + + def interrupted(*args, **kwargs): + raise MODULE.ProbeInterrupted("mock outer interruption") + + with self.assertRaises(MODULE.ProbeInterrupted): + MODULE._record_command(command_out, command_id="interrupted", argv=["mock"], env={}, + identities={}, runner=interrupted, timeout=1, cwd=out) + receipt = json.loads((command_out / "commands.jsonl").read_text()) + self.assertEqual(receipt["status"], "interrupted") + self.assertIn("ProbeInterrupted", receipt["error"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test-probe-python-modeling-load-bearing-v090.py b/scripts/test-probe-python-modeling-load-bearing-v090.py new file mode 100644 index 000000000..901490ccc --- /dev/null +++ b/scripts/test-probe-python-modeling-load-bearing-v090.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +"""Mocked authorization, capture, retention, and isolation tests for the v0.9 probe.""" + +from __future__ import annotations + +import hashlib +import importlib.util +import json +from pathlib import Path +import shutil +import signal +import tempfile +import unittest +from unittest.mock import patch + + +SCRIPT = Path(__file__).with_name("probe-python-modeling-load-bearing-v090.py") +SPEC = importlib.util.spec_from_file_location("python_modeling_v090", SCRIPT) +assert SPEC and SPEC.loader +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def write_json(path: Path, value: object) -> str: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, sort_keys=True) + "\n", encoding="utf-8") + return sha256(path) + + +def prepared_root(root: Path) -> tuple[Path, Path, Path]: + """Create a tiny hash-bound contract and fixture tree; no analyzer is run.""" + execution = root / "reports/releases/v0.9.0/execution-v1" + scratch = root / "isolated/reports/raw/control-scratch/probe-python-modeling-load-bearing" + scratch.mkdir(parents=True) + script_copy = root / "scripts/probe-python-modeling-load-bearing-v090.py" + script_copy.parent.mkdir(parents=True) + shutil.copy2(SCRIPT, script_copy) + + tool_root = root / "tools" + tools: dict[str, dict[str, str]] = {} + artifacts = [] + for key in ("bifrost", "codeql", "joern", "semgrep", "semgrep-core"): + path = tool_root / key / "bin" / key + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes((key + " pinned bytes\n").encode()) + path.chmod(0o755) + digest = sha256(path) + tools[key] = {"path": str(path), "sha256": digest} + if key != "codeql": + artifacts.append({"id": key, "path": str(path), "sha256": digest}) + + packs = tool_root / "codeql-packs" + pack_file = packs / "codeql/python/1.0.0/qlpack.yml" + pack_file.parent.mkdir(parents=True) + pack_file.write_text("name: codeql/python\n", encoding="utf-8") + pack_manifest_rel = "reports/releases/v0.9.0/execution-v1/non-swift-codeql-packs.json" + pack_manifest_sha = write_json(execution / "non-swift-codeql-packs.json", { + "claim": "exact local pack file inventory", + "root": str(packs), + "files": [{"path": pack_file.relative_to(packs).as_posix(), "sha256": sha256(pack_file), "bytes": pack_file.stat().st_size}], + }) + tools["codeql-packs"] = {"path": str(packs), "tree_sha256": "a" * 64} + + cli_tree_rel = "adapters/codeql/swift-normal-v1/plan-test/cli-tree.json" + cli_tree_sha = write_json(root / cli_tree_rel, { + "codeql": {"sha256": sha256(Path(tools["codeql"]["path"])), "mode": 0o755}, + }) + swift_plan_rel = "adapters/codeql/swift-normal-v1/plan-test/plan.json" + swift_plan_sha = write_json(root / swift_plan_rel, { + "runtime": { + "codeql": tools["codeql"]["path"], + "manifests": {"cli_tree": {"path": cli_tree_rel, "sha256": cli_tree_sha}}, + }, + }) + + held_sha = write_json(execution / "held-tool-digests.json", {"artifacts": artifacts}) + inventory_rel = "reports/releases/v0.9.0/execution-v1/control-inventory.json" + inventory = { + "schema": "release-control-inventory/v1", + "release": "v0.9.0", + "supplemental_control_ids": ["probe-python-modeling-load-bearing"], + "controls": [{ + "id": "probe-python-modeling-load-bearing", + "argv": ["/usr/bin/python3", "scripts/probe-python-modeling-load-bearing-v090.py"], + "output_roots": [ + "reports/raw/load-bearing-python-modeling-v090", + "reports/raw/control-scratch/probe-python-modeling-load-bearing", + ], + "environment": {"HOME": str(root), "PATH": "/pinned/bin", "TMPDIR": str(scratch)}, + "script_identity": [{"path": "scripts/probe-python-modeling-load-bearing-v090.py", "sha256": sha256(script_copy)}], + }], + } + inventory_sha = write_json(root / inventory_rel, inventory) + contract = { + "schema": "release-execution-contract/v1", + "release": "v0.9.0", + "execution_authorized": True, + "tools": tools, + "groups": [ + {"id": f"{tool}-python-modeling", "environment": {"HOME": str(root), "PATH": "/pinned/{tool}", "TMPDIR": "/private/tmp"}} + for tool in ("bifrost", "codeql", "joern", "semgrep") + ], + "control_inventory": {"path": inventory_rel, "sha256": inventory_sha}, + "identity_evidence": [ + {"path": "reports/releases/v0.9.0/execution-v1/held-tool-digests.json", "sha256": held_sha}, + {"path": pack_manifest_rel, "sha256": pack_manifest_sha}, + ], + "input_identities": { + inventory_rel: inventory_sha, + "reports/releases/v0.9.0/execution-v1/held-tool-digests.json": held_sha, + pack_manifest_rel: pack_manifest_sha, + swift_plan_rel: swift_plan_sha, + "scripts/probe-python-modeling-load-bearing-v090.py": sha256(script_copy), + }, + "swift_plans": {"codeql": {"path": swift_plan_rel, "sha256": swift_plan_sha}}, + } + contract_path = execution / "contract.json" + write_json(contract_path, contract) + + case_names = ( + "model-declared-source-positive", "model-declared-source-negative", + "model-declared-sink-positive", "model-declared-sink-negative", + "model-opaque-propagator-positive", "model-sanitizer-kill-negative", + "model-summary-through-positive", "model-sanitizer-selectivity-positive", + ) + for name in case_names: + fixture = root / "cases/taint/python" / name + fixture.mkdir(parents=True) + (fixture / "fixture.py").write_text("value = 1\n", encoding="utf-8") + bifrost = root / "adapters/bifrost/policies/model-python.rqlp" + bifrost.parent.mkdir(parents=True) + bifrost.write_text( + '(source :id declared-source :selector (rql :schema-version 1))])\n' + '(sink :id declared-sink :selector (rql :schema-version 1))])\n', encoding="utf-8" + ) + codeql_queries = root / "adapters/codeql/python/queries" + codeql_queries.mkdir(parents=True) + for name in ("PythonModeling.ql", "PythonModelingProbe.ql"): + (codeql_queries / name).write_text("// mock query\n", encoding="utf-8") + joern_semantics = root / "adapters/joern/semantics/model-python.semantics" + joern_semantics.parent.mkdir(parents=True) + joern_semantics.write_text('"clean.py:.scrub"\n', encoding="utf-8") + joern_query = root / "adapters/joern/queries/modeling.sc" + joern_query.parent.mkdir(parents=True) + joern_query.write_text("// mock query\n", encoding="utf-8") + semgrep_rule = root / "adapters/semgrep/rules/model-python.yaml" + semgrep_rule.parent.mkdir(parents=True) + semgrep_rule.write_text( + "- pattern: fetch_remote(...)\n" + "- pattern: record(...)\n" + "taint_assume_safe_functions: true\n", encoding="utf-8" + ) + return contract_path, root, scratch + + +class PythonModelingV090Tests(unittest.TestCase): + def test_denied_execution_creates_no_output_and_never_calls_runner(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + contract_path = root / "contract.json" + write_json(contract_path, { + "schema": "release-execution-contract/v1", + "release": "v0.9.0", + "execution_authorized": False, + }) + output = root / "must-not-exist" + calls: list[object] = [] + + with self.assertRaisesRegex(ValueError, "execution authorization"): + MODULE.capture(contract_path, output, root=root, runner=lambda *a, **k: calls.append(a) or 0) + + self.assertFalse(output.exists()) + self.assertEqual(calls, []) + + def test_failed_mocked_commands_retain_outputs_and_use_isolated_joern_workspaces(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + contract_path, root, scratch = prepared_root(root) + output = root / "reports/raw/load-bearing-python-modeling-v090" + calls: list[dict[str, object]] = [] + + def fail_mock(argv, *, cwd, stdout, stderr, env, timeout): + calls.append({"argv": argv, "cwd": cwd, "env": dict(env), "timeout": timeout}) + stdout.write(b"mock failure stdout\n") + stderr.write(b"mock failure stderr\n") + return 17 + + self.assertEqual(MODULE.capture(contract_path, output, root=root, runner=fail_mock), 1) + self.assertEqual(len(calls), 23) # Extraction fails; the exact historical controls continue. + self.assertTrue((output / "workspace/bifrost-declared-source-positive-with/fixture.py").is_file()) + first_name = "bifrost-declared-source-positive-with" + self.assertTrue((output / f"{first_name}-stdout.txt").is_file()) + self.assertTrue((output / f"{first_name}-stderr.txt").is_file()) + self.assertIn("mock failure stdout", (output / f"{first_name}-stdout.txt").read_text()) + records = [json.loads(line) for line in (output / "commands.jsonl").read_text().splitlines()] + self.assertEqual(records[0]["exit_code"], 17) + self.assertEqual(records[0]["status"], "failed") + self.assertEqual(records[0]["environment"]["TMPDIR"], str(scratch)) + self.assertTrue(all(call["env"]["TMPDIR"] == str(scratch) for call in calls)) + joern_cwds = [record["cwd"] for record in records if record["tool"] == "joern"] + self.assertEqual(len(joern_cwds), 4) + self.assertEqual(len(set(joern_cwds)), 4) + self.assertTrue(all(Path(cwd).is_dir() for cwd in joern_cwds)) + + def test_codeql_pack_manifest_rejects_extra_tree_membership(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + contract_path, root, _scratch = prepared_root(root) + contract = json.loads(contract_path.read_text(encoding="utf-8")) + extra = Path(contract["tools"]["codeql-packs"]["path"]) / "unlisted.txt" + extra.write_text("unlisted\n", encoding="utf-8") + + with self.assertRaisesRegex(ValueError, "unlisted CodeQL pack file"): + MODULE._check_held_tool_digests(contract, contract_path, root) + + def test_current_codeql_pin_uses_swift_tree_when_absent_from_held_file_list(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + contract_path, root, _scratch = prepared_root(root) + contract = json.loads(contract_path.read_text(encoding="utf-8")) + held_path = root / "reports/releases/v0.9.0/execution-v1/held-tool-digests.json" + held = json.loads(held_path.read_text(encoding="utf-8")) + self.assertNotIn("codeql", {item["id"] for item in held["artifacts"]}) + pinned = MODULE._check_held_tool_digests(contract, contract_path, root) + self.assertEqual(pinned["codeql"]["path"], contract["tools"]["codeql"]["path"]) + + def test_outer_term_runs_bounded_nested_process_group_cleanup(self) -> None: + calls: list[tuple[int, int]] = [] + + class MockProcess: + pid = 12345 + + def __init__(self): + self.wait_calls = 0 + + def wait(self, timeout=None): + self.wait_calls += 1 + if self.wait_calls == 1: + raise MODULE.ProbeInterrupted("recorder TERM") + return 0 + + process = MockProcess() + with patch.object(MODULE.subprocess, "Popen", return_value=process) as popen, \ + patch.object(MODULE.os, "killpg", side_effect=lambda pid, sig: calls.append((pid, sig))): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + with self.assertRaisesRegex(MODULE.ProbeInterrupted, "recorder TERM"): + MODULE._run_bounded( + ["mock-tool"], cwd=root, stdout=None, stderr=None, + env={"PATH": "/mock"}, timeout=5, + ) + self.assertEqual(popen.call_args.kwargs["start_new_session"], True) + self.assertEqual(calls, [(process.pid, signal.SIGTERM), (process.pid, signal.SIGKILL)]) + self.assertEqual(process.wait_calls, 3) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test-probe-warm-observability-v090.py b/scripts/test-probe-warm-observability-v090.py index acaa4a231..926bb9dc0 100644 --- a/scripts/test-probe-warm-observability-v090.py +++ b/scripts/test-probe-warm-observability-v090.py @@ -9,6 +9,7 @@ from pathlib import Path import tempfile import unittest +from unittest.mock import Mock, patch SCRIPT = Path(__file__).with_name("probe-warm-observability-v090.py") @@ -33,6 +34,26 @@ def fixture_contract() -> dict: class WarmObservabilityV090Tests(unittest.TestCase): + def test_interruption_kills_nested_group_and_restores_handlers(self): + process = Mock(pid=12345) + process.wait.side_effect = [KeyboardInterrupt(), 0, 0] + original = {sig: MODULE.signal.getsignal(sig) for sig in (MODULE.signal.SIGTERM, MODULE.signal.SIGINT)} + with patch.object(MODULE.subprocess, 'Popen', return_value=process), patch.object(MODULE.os, 'killpg') as kill: + with self.assertRaises(KeyboardInterrupt): + MODULE._run_bounded(['fake'], cwd=Path('.'), stdout=None, stderr=None, env={}) + self.assertEqual([call.args[1] for call in kill.call_args_list], [MODULE.signal.SIGTERM, MODULE.signal.SIGKILL]) + self.assertEqual({sig: MODULE.signal.getsignal(sig) for sig in original}, original) + + def test_pinned_product_uses_bundled_jre(self) -> None: + contract = fixture_contract() + contract['tools']['opentaint-wrapper'] = {'path': '/held/product/opentaint', 'sha256': 'a' * 64} + command = MODULE.command_inventory(contract)[-1] + self.assertEqual(command['argv'], ['/held/product/opentaint', 'scan', '--help']) + env = MODULE._group_environment(contract, 'opentaint-product') + self.assertEqual(env['JAVA_HOME'], '/held/product/jre') + self.assertEqual(env['PATH'], '/held/product/jre/bin:/pinned/bin') + self.assertEqual(contract['groups'][-1]['environment']['JAVA_HOME'], '/pinned/jdk') + def test_uses_only_v090_pins_and_skips_unpinned_wrapper(self) -> None: inventory = MODULE.command_inventory(fixture_contract()) self.assertEqual(len(inventory), 10) diff --git a/scripts/test-release-control-attempt-v090.py b/scripts/test-release-control-attempt-v090.py new file mode 100644 index 000000000..02c90caaa --- /dev/null +++ b/scripts/test-release-control-attempt-v090.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +"""Synthetic-only tests for the immutable release control recorder.""" + +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import sys +import tempfile +import unittest + +SCRIPT = Path(__file__).with_name("release_control_attempt_v090.py") +SPEC = importlib.util.spec_from_file_location("release_control_attempt_v090", SCRIPT) +RECORDER = importlib.util.module_from_spec(SPEC) +assert SPEC and SPEC.loader +sys.modules[SPEC.name] = RECORDER +SPEC.loader.exec_module(RECORDER) + +CONTRACT = "reports/releases/v0.9.0/execution-v1/contract.json" +FIXTURE = "sha256:" + "a" * 64 + + +class ControlAttemptTests(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name).resolve() + self.control_id = "fake-control" + self.output = "reports/raw/fake-control" + self.scratch = f"reports/raw/control-scratch/{self.control_id}" + (self.root / self.scratch).parent.mkdir(parents=True) + self.command = [sys.executable, "scripts/fake-control.py"] + self._write("scripts/fake-control.py", b"synthetic script identity\n") + self._write("reports/releases/v0.9.0/input-pin.json", b"pinned input\n") + population = {"population": "v0.9.0", "fixture_revision": FIXTURE} + population_raw = self._json(population) + self._write("populations/v0.9.0.json", population_raw) + plan = { + "release": "v0.9.0", "fixture_revision": FIXTURE, + "input_commits": {"corpus": "a" * 40}, + "population": {"path": "populations/v0.9.0.json", "sha256": self.sha(population_raw)}, + } + plan_raw = self._json(plan) + self._write("reports/releases/v0.9.0/plan.json", plan_raw) + self.inventory = {"schema": "release-control-inventory/v1", "release": "v0.9.0", + "execution_authorized": True, + "controls": [self._control()]} + inventory_raw = self._json(self.inventory) + self._write("reports/releases/v0.9.0/execution-v1/control-inventory.json", inventory_raw) + self.contract = { + "schema_version": 1, "release": "v0.9.0", "execution_authorized": True, + "fixture_revision": FIXTURE, + "parent_plan": {"path": "reports/releases/v0.9.0/plan.json", "sha256": self.sha(plan_raw)}, + "population": {"path": "populations/v0.9.0.json", "sha256": self.sha(population_raw)}, + "input_commits": plan["input_commits"], + "input_identities": { + "reports/releases/v0.9.0/input-pin.json": self.sha(b"pinned input\n"), + "scripts/fake-control.py": self.sha(b"synthetic script identity\n"), + "reports/releases/v0.9.0/execution-v1/control-inventory.json": self.sha(inventory_raw), + }, + "control_inventory": {"path": "reports/releases/v0.9.0/execution-v1/control-inventory.json", + "sha256": self.sha(inventory_raw)}, + "control_execution_roots": {self.control_id: str(self.root)}, + "tools": {}, "unresolved": [], + } + self._write(CONTRACT, self._json(self.contract)) + + def tearDown(self): + self.tmp.cleanup() + + @staticmethod + def sha(value): + return hashlib.sha256(value).hexdigest() + + @staticmethod + def _json(value): + return (json.dumps(value, sort_keys=True, indent=2) + "\n").encode() + + def _write(self, rel, data): + path = self.root / rel + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data) + return path + + def _control(self): + return { + "id": self.control_id, "argv": self.command, + "environment": {"PATH": os.defpath, "TMPDIR": str(self.root / self.scratch)}, + "deadline_seconds": 5, "maximum_attempts": 2, + "measurement_repeats": 2, "repeat_mechanism": "inside-harness-command", + "output_roots": [self.output, self.scratch], + "script_identity": [{"path": "scripts/fake-control.py", "sha256": self.sha(b"synthetic script identity\n")}], + } + + def _install_command(self, code): + self.command = [sys.executable, "-c", code] + self.inventory["controls"][0]["argv"] = self.command + raw = self._json(self.inventory) + self._write("reports/releases/v0.9.0/execution-v1/control-inventory.json", raw) + self.contract["input_identities"]["reports/releases/v0.9.0/execution-v1/control-inventory.json"] = self.sha(raw) + self.contract["control_inventory"]["sha256"] = self.sha(raw) + self._write(CONTRACT, self._json(self.contract)) + + def _run_and_verify(self, expected): + row = RECORDER.run_control(self.root, CONTRACT, self.control_id) + self.assertEqual(row["status"], expected) + attempt = Path(RECORDER.ATTEMPTS_PATH) / self.control_id / "attempt-01" / "completed.json" + verified = RECORDER.verify_control(self.root, attempt, CONTRACT) + self.assertTrue(verified["verified"]) + self.assertEqual(verified["status"], expected) + self.assertEqual(len((self.root / RECORDER.LEDGER_PATH).read_text().splitlines()), 1) + return row, attempt + + def test_successful_control_capture_and_verify_roundtrip(self): + code = ("from pathlib import Path; " + f"p=Path({self.output!r}); p.mkdir(parents=True); (p/'raw.bin').write_bytes(b'raw\\x00bytes'); " + f"Path({self.scratch!r},'tmp.txt').write_text('scratch'); " + "print('synthetic stdout')") + self._install_command(code) + result = RECORDER.validate_control(self.root, CONTRACT, self.control_id) + self.assertEqual(result["control"]["measurement_repeats"], 2) + self.assertEqual(result["execution_root"], str(self.root)) + row, receipt = self._run_and_verify("completed") + self.assertEqual(len(row["captured_files"]), 2) + + def test_failed_control_is_captured_and_verifiable(self): + code = ("from pathlib import Path; " + f"p=Path({self.output!r}); p.mkdir(parents=True); (p/'partial').write_text('kept'); " + "print('failure output'); raise SystemExit(7)") + self._install_command(code) + row, _ = self._run_and_verify("failed") + self.assertEqual(row["exit_code"], 7) + self.assertTrue(row["captured_files"]) + + def test_runner_binary_only_control_uses_runner_build_provenance(self): + binary = self.root / "fake-runner" + binary.write_text("#!/usr/bin/env python3\nfrom pathlib import Path\n" + f"p=Path({self.output!r}); p.mkdir(parents=True); (p/'runner.bin').write_bytes(b'ok')\n") + binary.chmod(0o755) + digest = self.sha(binary.read_bytes()) + self.command = [str(binary), "probe"] + self.inventory["controls"][0]["argv"] = self.command + self.inventory["controls"][0]["script_identity"] = [] + self.inventory["controls"][0]["output_roots"] = [self.output, self.scratch] + inventory_raw = self._json(self.inventory) + self._write("reports/releases/v0.9.0/execution-v1/control-inventory.json", inventory_raw) + self.contract["input_identities"].pop("scripts/fake-control.py") + self.contract["input_identities"]["reports/releases/v0.9.0/execution-v1/control-inventory.json"] = self.sha(inventory_raw) + self.contract["control_inventory"]["sha256"] = self.sha(inventory_raw) + self.contract["tools"]["runner"] = {"path": str(binary), "sha256": digest} + self.contract["runner_build"] = {"binary_path": str(binary), "binary_sha256": digest, + "source_commit": "b" * 40, "source_files": {"src/main.rs": "c" * 64}, + "schema": "release-runner-build/v1"} + self._write(CONTRACT, self._json(self.contract)) + validated = RECORDER.validate_control(self.root, CONTRACT, self.control_id) + self.assertEqual(validated["script_identity"], []) + self.assertEqual(validated["runner_identity"]["sha256"], digest) + row, _ = self._run_and_verify("completed") + self.assertEqual(row["script_identity"], []) + self.assertEqual(row["runner_identity"]["sha256"], digest) + + def test_existing_attempt_does_not_allocate_attempt_two(self): + prior = self.root / RECORDER.ATTEMPTS_PATH / self.control_id / "attempt-01" + prior.mkdir(parents=True) + with self.assertRaisesRegex(RECORDER.ControlError, "existing control attempt"): + RECORDER.validate_control(self.root, CONTRACT, self.control_id) + self.assertFalse((prior.parent / "attempt-02").exists()) + + def test_tampered_capture_is_rejected(self): + self._install_command(f"from pathlib import Path; p=Path({self.output!r}); p.mkdir(parents=True); (p/'raw').write_text('original')") + row = RECORDER.run_control(self.root, CONTRACT, self.control_id) + captured = self.root / RECORDER.ATTEMPTS_PATH / self.control_id / "attempt-01" / "capture" / self.output / "raw" + captured.write_text("tampered") + with self.assertRaisesRegex(RECORDER.ControlError, "digest mismatch|tree differs"): + receipt = captured.parents[4] / "completed.json" + RECORDER.verify_control(self.root, receipt.relative_to(self.root), CONTRACT) + + def test_success_with_missing_declared_root_is_recorder_error(self): + self._install_command("print('did not create declared outputs')") + row, _ = self._run_and_verify("recorder-error") + self.assertIn(self.output, row["missing_output_roots"]) + + def test_timeout_is_terminal_and_verifiable(self): + self.inventory["controls"][0]["deadline_seconds"] = 1 + self._install_command("import time; time.sleep(3)") + row, _ = self._run_and_verify("timed-out") + self.assertTrue(row["timed_out"]) + + def test_unauthorized_validation_and_existing_attempt_have_no_launch_side_effects(self): + self.contract["execution_authorized"] = False + self._write(CONTRACT, self._json(self.contract)) + validated = RECORDER.validate_control(self.root, CONTRACT, self.control_id, require_authorized=False) + self.assertFalse(validated["authorized"]) + with self.assertRaisesRegex(RECORDER.ControlError, "validation-only"): + RECORDER.run_control(self.root, CONTRACT, self.control_id) + self.assertFalse((self.root / RECORDER.ATTEMPTS_PATH).exists()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test-run-release-control-v090.py b/scripts/test-run-release-control-v090.py new file mode 100644 index 000000000..0ddf36d30 --- /dev/null +++ b/scripts/test-run-release-control-v090.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Control launch must pass both identity validation and the shared host gate.""" +import importlib.util +from pathlib import Path +import tempfile +import types +import unittest +from unittest.mock import Mock, patch + +spec = importlib.util.spec_from_file_location('control_launch', Path(__file__).with_name('run-release-control-v090.py')) +launch = importlib.util.module_from_spec(spec) +spec.loader.exec_module(launch) + + +class LaunchTests(unittest.TestCase): + def test_validation_only_never_runs_control(self): + fake = types.ModuleType('release_control_attempt_v090') + fake.validate_control = Mock(return_value={}) + fake.run_control = Mock() + with tempfile.TemporaryDirectory() as tmp, patch.dict('sys.modules', {fake.__name__: fake}): + self.assertEqual(launch.launch_control(tmp, 'contract.json', 'one'), + {'mode': 'validation-only', 'control_id': 'one'}) + fake.run_control.assert_not_called() + self.assertFalse(list(Path(tmp).iterdir())) + + def test_failed_validation_never_opens_host_lock(self): + fake = types.ModuleType('release_control_attempt_v090') + fake.validate_control = Mock(side_effect=ValueError('unreviewed identity')) + fake.run_control = Mock() + with tempfile.TemporaryDirectory() as tmp, patch.dict('sys.modules', {fake.__name__: fake}), patch.object(launch.os, 'open') as opened: + with self.assertRaisesRegex(ValueError, 'unreviewed'): + launch.launch_control(tmp, 'contract.json', 'one', execute=True) + opened.assert_not_called() + fake.run_control.assert_not_called() + + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/test-run-release-group-v090.py b/scripts/test-run-release-group-v090.py index 1a280b2f0..a424c9657 100644 --- a/scripts/test-run-release-group-v090.py +++ b/scripts/test-run-release-group-v090.py @@ -33,15 +33,35 @@ def setUp(self): 'total_wall_budget_seconds': 102960, 'resource_budget': {'minimum_launch_free_gib': 100, 'proposed_total_retention_ceiling_gib': 24}} - def gate(self, *, processes='', free=120*1024**3): + def gate(self, *, processes='', free=120*1024**3, changed=''): def output(argv, **kwargs): - return 'a'*40 if argv[0] == 'git' else processes + if argv[0] == 'git': + return changed if argv[1] == 'diff' else 'a'*40 + return processes with patch('release_runtime_inventory_v090.verify', return_value=True), patch.object(launch.subprocess, 'check_output', side_effect=output), patch.object(launch.shutil, 'disk_usage', return_value=types.SimpleNamespace(free=free)): launch.launch_gate(self.root, self.contract, {'id': 'one', 'deadline_seconds': 600}) def test_valid_gate(self): self.gate() + def test_dirty_fixture_blocks_exact_harness_claim(self): + with self.assertRaisesRegex(launch.AttemptError, 'source differs'): + self.gate(changed='cases/taint/java/direct-positive/Main.java\n') + + def test_control_uses_its_designated_root(self): + self.contract['execution_roots'] = {} + self.contract['control_execution_roots'] = {'one': str(self.root)} + self.gate() + + def test_retention_counts_other_control_roots(self): + retained = self.root/'other/reports/releases/v0.9.0/control-attempts' + retained.mkdir(parents=True) + (retained/'raw').write_bytes(b'ab') + self.contract['control_execution_roots'] = {'control': str(self.root/'other')} + self.contract['resource_budget']['proposed_total_retention_ceiling_gib'] = 1 / 1024**3 + with self.assertRaisesRegex(launch.AttemptError, 'retention budget'): + self.gate() + def test_another_build_blocks(self): with self.assertRaisesRegex(launch.AttemptError, 'contending'): self.gate(processes='12 /toolchain/rustc\n') diff --git a/scripts/test-v090-execution-contract.py b/scripts/test-v090-execution-contract.py index 187556afb..a1b1aab87 100644 --- a/scripts/test-v090-execution-contract.py +++ b/scripts/test-v090-execution-contract.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """Mutations that must never turn a planning contract into executable evidence.""" import importlib.util +import hashlib import json from pathlib import Path import tempfile @@ -19,7 +20,7 @@ def setUp(self): self.root = Path(self.tmp.name) self.plan = json.loads((ROOT/checker.CONTRACT).read_text()) refs = [self.plan['preparation_plan'], *self.plan['identity_evidence'], - *self.plan['environment_implementation'], self.plan['codeql_compatibility']] + *self.plan['environment_implementation'], self.plan['codeql_compatibility'], self.plan['control_inventory']] for ref in refs: target = self.root/ref['path'] target.parent.mkdir(parents=True, exist_ok=True) @@ -32,7 +33,7 @@ def check(self): return checker.validate(self.root) def test_complete_planning_inventory(self): - self.assertEqual(self.check(), {'groups': 84, 'controls': 30, 'executable': False}) + self.assertEqual(self.check(), {'groups': 84, 'controls': 33, 'executable': False}) def test_cannot_self_authorize(self): self.plan['execution_authorized'] = True @@ -80,6 +81,17 @@ def test_deadline_cannot_shrink(self): with self.assertRaisesRegex(ValueError, 'deadline changed'): self.check() + def test_measurement_repeats_cannot_become_retries(self): + reference = self.plan['control_inventory'] + path = self.root/reference['path'] + inventory = json.loads(path.read_text()) + next(c for c in inventory['controls'] if c['stage'] == 'warm')['measurement_repeats'] = 1 + raw = json.dumps(inventory).encode() + path.write_bytes(raw) + reference['sha256'] = hashlib.sha256(raw).hexdigest() + with self.assertRaisesRegex(ValueError, 'measurement repeats'): + self.check() + if __name__ == '__main__': unittest.main() From c540382bf8a322963d631e5ea71c58d79a5446b2 Mon Sep 17 00:00:00 2001 From: David Baker Effendi Date: Tue, 29 Sep 2026 17:39:17 +0200 Subject: [PATCH 2/2] Bind binary control provenance and retain capture errors --- .../v0.9.0/execution-v1/contract.json | 4 ++-- .../control-integration-validation.json | 2 +- scripts/release_control_attempt_v090.py | 22 ++++++++++++++----- scripts/test-release-control-attempt-v090.py | 4 ++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/reports/releases/v0.9.0/execution-v1/contract.json b/reports/releases/v0.9.0/execution-v1/contract.json index af98d26a9..ae9c85c3b 100644 --- a/reports/releases/v0.9.0/execution-v1/contract.json +++ b/reports/releases/v0.9.0/execution-v1/contract.json @@ -14471,7 +14471,7 @@ "populations/v0.9.0.json": "d05be4f2d9effe7b14a2ce5544238364f171e1fb763db2f79ff486526ca7c890", "reports/releases/v0.9.0/execution-v1/codeql-compatibility/summary.json": "7bc68a9bc11994525c8dd536fc9c419c795003228ee285e288ea3bc7e9dd6adc", "reports/releases/v0.9.0/execution-v1/command-parsing-check.json": "73b9849ac390763dd65e26d829148323ec79f24fbae34f0572ae63a64bb306ab", - "reports/releases/v0.9.0/execution-v1/control-integration-validation.json": "2767dd4588bd447dcd686179bc5cfd85ae73a98a7d2274826cff3e6db4d359a4", + "reports/releases/v0.9.0/execution-v1/control-integration-validation.json": "fe09cac1435971ff4535f0091f53568f5e8bac32675353eb8e755d9f86d38028", "reports/releases/v0.9.0/execution-v1/control-inventory.json": "4179a3617cd95bb950417171e1a5c9e8490577f98d559d340000a821789d8d6f", "reports/releases/v0.9.0/execution-v1/dependency-version-witnesses.json": "50d7d8566cb24d1fdee789bfc7932463f3d014c8702f69822101db46ba916480", "reports/releases/v0.9.0/execution-v1/held-tool-digests.json": "9d748428932fdf7922ae62a066304226166a8f7105ebd5a8be9a42c15a4ffab2", @@ -14514,7 +14514,7 @@ "scripts/probe-python-modeling-load-bearing-v090.py": "0331eba3b003d455f1cee163c4f47d3bd648551f07473c5b7eeb3ad0e2900073", "scripts/probe-semgrep-jsjava-native.sh": "d09e3bab859030d4679af455a341e7a155119fd48cba248abdc11b0fdf8d44f2", "scripts/probe-warm-observability-v090.py": "c2c31b70154f25287488f262a3bd23e5d029e784d1c7c60173212a113b7346a8", - "scripts/release_control_attempt_v090.py": "96d95db99791276a68c467ab2e406360b731c30ebdae1942927dc2ab9c8ceac0", + "scripts/release_control_attempt_v090.py": "7e876cfd9c0be39c2c0a85bec99c1282366f3c69fffe3eb524b5aa2ffacf41b5", "scripts/release_environment_v090.py": "344ed79eb774134c4e04e4e243bf05c20279a52d0e050e674a9c79ddb0f16ebd", "scripts/run-release-control-v090.py": "8cf9785659f8319a08115422c6229162883a858dd766e128c906431ed96c7d0d", "scripts/run-release-group-v090.py": "a19ae44adefe386f7f71b006ece268e1d3e3ea0ee54d94247737a0b1eb6b1ac4" diff --git a/reports/releases/v0.9.0/execution-v1/control-integration-validation.json b/reports/releases/v0.9.0/execution-v1/control-integration-validation.json index 4bf46a61b..74544f941 100644 --- a/reports/releases/v0.9.0/execution-v1/control-integration-validation.json +++ b/reports/releases/v0.9.0/execution-v1/control-integration-validation.json @@ -81,7 +81,7 @@ "scripts/test-release-control-attempt-v090.py" ], "exit_code": 0, - "output": "........\n----------------------------------------------------------------------\nRan 8 tests in 2.631s\n\nOK\n", + "output": "........\n----------------------------------------------------------------------\nRan 8 tests in 1.828s\n\nOK\n", "seconds": 2.678 }, { diff --git a/scripts/release_control_attempt_v090.py b/scripts/release_control_attempt_v090.py index 47352ea0f..c267a4e4b 100644 --- a/scripts/release_control_attempt_v090.py +++ b/scripts/release_control_attempt_v090.py @@ -136,6 +136,10 @@ def _load_bound_control(root, contract_path, control_id, *, require_authorized, raise ControlError("control execution root must be an absolute path") if Path(mapped_root).resolve(strict=True) != root: raise ControlError("supplied execution root differs from contract.control_execution_roots") + canonical_roots = [Path(value).resolve(strict=False) for value in control_roots.values() + if isinstance(value, str) and Path(value).is_absolute()] + if len(canonical_roots) != len(control_roots) or len(set(canonical_roots)) != len(canonical_roots): + raise ControlError("contract.control_execution_roots must map every control to a unique absolute root") argv = control.get("argv") if (not isinstance(argv, list) or not argv or @@ -220,6 +224,12 @@ def _load_bound_control(root, contract_path, control_id, *, require_authorized, raise ControlError("tools.runner and runner_build binary identity mismatch") if not isinstance(runner_build.get("source_commit"), str) or not isinstance(runner_build.get("source_files"), dict): raise ControlError("runner_build lacks source commit or source file provenance") + build_refs = [path for path in input_identities if Path(path).name == "runner-build.json"] + if len(build_refs) != 1: + raise ControlError("runner_build must be bound by exactly one contract input identity") + build_value, _ = _HELPERS._read_json(root / build_refs[0], "runner build provenance") + if build_value != runner_build: + raise ControlError("contract runner_build differs from its hash-bound provenance file") runner_identity = {"path": str(binary_path), "sha256": binary_sha, "source_commit": runner_build["source_commit"], "source_files": runner_build["source_files"], @@ -298,13 +308,13 @@ def _append_ledger(path: Path, row: dict) -> None: def _capture_roots(root: Path, attempt: Path, roots: list[str]) -> tuple[list[dict], list[str], list[str]]: files, missing, errors = [], [], [] for value in roots: - relative = _HELPERS._relative(value, "control output root") - source = _HELPERS._safe_path(root, relative, allow_missing=True) - if not source.exists() and not source.is_symlink(): - missing.append(value) - continue - target = attempt / "capture" / Path(*relative.parts) try: + relative = _HELPERS._relative(value, "control output root") + source = _HELPERS._safe_path(root, relative, allow_missing=True) + if not source.exists() and not source.is_symlink(): + missing.append(value) + continue + target = attempt / "capture" / Path(*relative.parts) files.extend(_HELPERS._copy_tree(root, relative, target)) except Exception as exc: errors.append(f"{value}: {type(exc).__name__}: {exc}") diff --git a/scripts/test-release-control-attempt-v090.py b/scripts/test-release-control-attempt-v090.py index 02c90caaa..b5775b71f 100644 --- a/scripts/test-release-control-attempt-v090.py +++ b/scripts/test-release-control-attempt-v090.py @@ -151,6 +151,10 @@ def test_runner_binary_only_control_uses_runner_build_provenance(self): self.contract["runner_build"] = {"binary_path": str(binary), "binary_sha256": digest, "source_commit": "b" * 40, "source_files": {"src/main.rs": "c" * 64}, "schema": "release-runner-build/v1"} + build_path = 'reports/releases/v0.9.0/execution-v1/runner-build.json' + build_raw = self._json(self.contract['runner_build']) + self._write(build_path, build_raw) + self.contract['input_identities'][build_path] = self.sha(build_raw) self._write(CONTRACT, self._json(self.contract)) validated = RECORDER.validate_control(self.root, CONTRACT, self.control_id) self.assertEqual(validated["script_identity"], [])