personal memory agent
0

Configure Feed

Select the types of activity you want to include in your feed.

solstone / tests / test_check_rust_release_manifest.py
58 kB 1716 lines
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4from __future__ import annotations 5 6import fcntl 7import hashlib 8import json 9import os 10import re 11import subprocess 12import sys 13import tomllib 14from dataclasses import replace 15from pathlib import Path 16from typing import Any 17 18import pytest 19 20import scripts.check_rust_release_manifest as checker 21import scripts.release_tool_pins as pins 22 23SCRIPT = ( 24 Path(__file__).resolve().parents[1] / "scripts" / "check_rust_release_manifest.py" 25) 26VALID_COMMIT = checker.fixture_source_commit() 27PYTHON_WHITESPACE_CODE_POINTS: tuple[int, ...] = ( 28 0x09, 29 0x0A, 30 0x0B, 31 0x0C, 32 0x0D, 33 0x1C, 34 0x1D, 35 0x1E, 36 0x1F, 37 0x20, 38 0x85, 39 0xA0, 40 0x1680, 41 0x2000, 42 0x2001, 43 0x2002, 44 0x2003, 45 0x2004, 46 0x2005, 47 0x2006, 48 0x2007, 49 0x2008, 50 0x2009, 51 0x200A, 52 0x2028, 53 0x2029, 54 0x202F, 55 0x205F, 56 0x3000, 57) 58RUSTC_REJECTED_SEPARATOR_CODE_POINTS: tuple[int, ...] = tuple( 59 code_point for code_point in PYTHON_WHITESPACE_CODE_POINTS if code_point != 0x20 60) 61 62 63def _errors(failures: list[checker.Failure]) -> set[str]: 64 return {failure.error for failure in failures} 65 66 67def _assert_error(failures: list[checker.Failure], error: str) -> None: 68 assert error in _errors(failures) 69 70 71def _assert_redacted( 72 failures: list[checker.Failure], forbidden: tuple[str, ...] 73) -> None: 74 for failure in failures: 75 fields = (failure.error, failure.expected, failure.actual, failure.repair) 76 for needle in forbidden: 77 assert all(needle not in field for field in fields) 78 79 80def _assert_formatted_redacted( 81 failures: list[checker.Failure], 82 forbidden: tuple[str, ...], 83 capsys: pytest.CaptureFixture[str], 84) -> None: 85 checker._format_failures(failures) 86 stderr = capsys.readouterr().err 87 for needle in forbidden: 88 assert needle not in stderr 89 90 91def _repo_root() -> Path: 92 return Path(__file__).resolve().parents[1] 93 94 95def _rustc_lines(host: str = "x86_64-unknown-linux-gnu") -> list[str]: 96 return checker.fixture_rustc_verbose(host).split("\n") 97 98 99def _rustc_line_mutant(canonical: str, line_index: int, replacement: str) -> str: 100 lines = canonical.split("\n") 101 lines[line_index] = replacement 102 return "\n".join(lines) 103 104 105def _candidate( 106 tmp_path: Path, 107 *, 108 include_models: bool = False, 109 source_commit: str = VALID_COMMIT, 110) -> Path: 111 release_dir = tmp_path / ("candidate-models" if include_models else "candidate") 112 failures = checker.write_inert_candidate( 113 release_dir, 114 include_models=include_models, 115 source_commit=source_commit, 116 ) 117 assert failures == [] 118 return release_dir 119 120 121def test_release_manifest_imports_tool_pins_from_authoritative_module() -> None: 122 assert checker.RUSTC_VERSION_BANNER == pins.RUSTC_VERSION_BANNER 123 assert checker.RUSTC_BINARY_PIN == pins.RUSTC_BINARY_PIN 124 assert checker.RUSTC_COMMIT_HASH_PIN == pins.RUSTC_COMMIT_HASH_PIN 125 assert checker.RUSTC_COMMIT_DATE_PIN == pins.RUSTC_COMMIT_DATE_PIN 126 assert checker.RUSTC_RELEASE_PIN == pins.RUSTC_RELEASE_PIN 127 assert checker.RUSTC_LLVM_PIN == pins.RUSTC_LLVM_PIN 128 assert checker.CARGO_VERSION_PIN == pins.CARGO_VERSION_PIN 129 assert checker.CARGO_RELEASE_PIN == pins.CARGO_RELEASE_PIN 130 assert checker.CARGO_DENY_PIN == pins.CARGO_DENY_PIN 131 132 source = Path(checker.__file__).read_text(encoding="utf-8") 133 assert "RUSTC_VERSION_BANNER =" not in source 134 assert "CARGO_DENY_PIN =" not in source 135 136 137def test_macos_swift_pin_requires_exact_canonical_grounded_output() -> None: 138 exact = "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)" 139 140 assert pins.MACOS_SWIFT_PIN == exact 141 assert checker.validate_public_evidence_text("swift", pins.MACOS_SWIFT_PIN) == [] 142 assert ( 143 checker.validate_public_evidence_text("swift", pins.MACOS_SWIFT_RAW_BANNER) 144 == [] 145 ) 146 assert ( 147 checker.validate_public_evidence_text( 148 "swift", pins.MACOS_SWIFT_FLATTENED_BANNER 149 ) 150 == [] 151 ) 152 assert not any( 153 line.startswith("MACOS_SWIFT_VERSION") 154 for line in Path(pins.__file__).read_text(encoding="utf-8").splitlines() 155 ) 156 157 skewed_observations = ( 158 "6.3.3", 159 "swift 6.3.3", 160 "Apple Swift 6.3.3", 161 "Apple Swift 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.102)", 162 "Apple Swift 6.3.3 (swiftlang-6.3.3.1.4 clang-2100.1.1.101)", 163 "Apple Swift version 6.3.4 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)", 164 "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.4 clang-2100.1.1.101)", 165 "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.102)", 166 f" {exact}", 167 f"{exact} ", 168 ) 169 assert all(observed != pins.MACOS_SWIFT_PIN for observed in skewed_observations) 170 171 172def test_build_system_pins_match_release_tool_pins() -> None: 173 root = _repo_root() 174 setuptools_projects = ( 175 root / "pyproject.toml", 176 root / "packages" / "solstone-journal" / "pyproject.toml", 177 root / "packages" / "solstone-journal-cuda" / "pyproject.toml", 178 root / "packages" / "solstone-journal-models" / "pyproject.toml", 179 ) 180 for path in setuptools_projects: 181 data = tomllib.loads(path.read_text(encoding="utf-8")) 182 assert data["build-system"]["requires"] == list(pins.SETUPTOOLS_BUILD_REQUIRES) 183 184 core_data = tomllib.loads( 185 (root / "packages" / "solstone-core" / "pyproject.toml").read_text( 186 encoding="utf-8" 187 ) 188 ) 189 assert core_data["build-system"]["requires"] == [pins.MATURIN_REQUIREMENT] 190 191 192def test_all_macos_maturin_invocations_are_locked() -> None: 193 makefile = (_repo_root() / "Makefile").read_text(encoding="utf-8") 194 assignments = re.findall( 195 r'MATURIN_PEP517_ARGS="([^"]*aarch64-apple-darwin[^"]*)"', makefile 196 ) 197 198 assert assignments 199 assert all("--locked" in args.split() for args in assignments) 200 201 202def _manifest_for_lane(release_dir: Path, lane: checker.LaneName) -> Path: 203 for artifact_name, ( 204 artifact_lane, 205 _target, 206 ) in checker.rust_artifact_targets().items(): 207 if artifact_lane == lane: 208 return release_dir / f"{artifact_name}.rust-release-manifest.json" 209 raise AssertionError(f"no artifact for lane {lane}") 210 211 212def _artifact_for_lane(release_dir: Path, lane: checker.LaneName) -> Path: 213 manifest = _manifest_for_lane(release_dir, lane) 214 return release_dir / manifest.name.removesuffix(".rust-release-manifest.json") 215 216 217def _source_artifact(tmp_path: Path) -> Path: 218 release_dir = tmp_path / "source-artifact" 219 checker.write_inert_packages(release_dir, include_models=False) 220 return _artifact_for_lane(release_dir, "source") 221 222 223def _assert_malformed_redacted( 224 failures: list[checker.Failure], 225 forbidden: tuple[str, ...], 226 capsys: pytest.CaptureFixture[str], 227) -> None: 228 assert failures 229 _assert_error(failures, "rustc_verbose is malformed") 230 for failure in failures: 231 assert failure.actual == "redacted" 232 _assert_redacted(failures, forbidden) 233 _assert_formatted_redacted(failures, forbidden, capsys) 234 235 236def _assert_rustc_mutant_rejected( 237 artifact_path: Path, 238 capsys: pytest.CaptureFixture[str], 239 *, 240 mutant: str, 241 forbidden: tuple[str, ...], 242) -> None: 243 parsed, parse_failures = checker.parse_rustc_verbose(mutant) 244 assert parsed is None 245 _assert_malformed_redacted(parse_failures, forbidden, capsys) 246 247 evidence = checker.fixture_evidence_by_lane()["source"] 248 generated, generate_failures = checker.generate_manifest( 249 artifact_path, 250 lane="source", 251 evidence=replace(evidence, rustc_verbose=mutant), 252 cohort=checker._default_cohort(VALID_COMMIT), 253 ) 254 assert generated is None 255 _assert_malformed_redacted(generate_failures, forbidden, capsys) 256 257 258def _assert_rustc_line_mutant_rejected( 259 artifact_path: Path, 260 capsys: pytest.CaptureFixture[str], 261 *, 262 canonical: str, 263 mutant: str, 264 line_index: int, 265) -> None: 266 canonical_lines = canonical.split("\n") 267 mutant_lines = mutant.split("\n") 268 assert len(mutant_lines) == len(canonical_lines) 269 assert mutant_lines[line_index] != canonical_lines[line_index] 270 for index, line in enumerate(canonical_lines): 271 if index != line_index: 272 assert mutant_lines[index] == line 273 _assert_rustc_mutant_rejected( 274 artifact_path, 275 capsys, 276 mutant=mutant, 277 forbidden=(mutant, mutant_lines[line_index]), 278 ) 279 280 281def _assert_rustc_lf_separator_mutant_rejected( 282 artifact_path: Path, 283 capsys: pytest.CaptureFixture[str], 284 *, 285 canonical: str, 286 mutant: str, 287 line_index: int, 288) -> None: 289 canonical_lines = canonical.split("\n") 290 label, value = canonical_lines[line_index].split(": ", 1) 291 mutant_lines = mutant.split("\n") 292 assert len(mutant_lines) == len(canonical_lines) + 1 293 assert mutant_lines[:line_index] == canonical_lines[:line_index] 294 assert mutant_lines[line_index : line_index + 2] == [f"{label}:", value] 295 assert mutant_lines[line_index + 2 :] == canonical_lines[line_index + 1 :] 296 _assert_rustc_mutant_rejected( 297 artifact_path, 298 capsys, 299 mutant=mutant, 300 forbidden=(mutant, f"{label}:\n{value}"), 301 ) 302 303 304def _load_manifest(path: Path) -> dict: 305 return json.loads(path.read_text(encoding="utf-8")) 306 307 308def _write_manifest(path: Path, payload: dict) -> None: 309 path.write_bytes(checker.canonical_json_bytes(payload)) 310 311 312def _replace_manifest_artifact(manifest_path: Path, artifact_name: str) -> None: 313 artifact = manifest_path.parent / artifact_name 314 digest = hashlib.sha256(artifact.read_bytes()).hexdigest() 315 payload = _load_manifest(manifest_path) 316 payload["artifacts"] = [ 317 { 318 "path": artifact_name, 319 "sha256": digest, 320 "bytes": artifact.stat().st_size, 321 } 322 ] 323 _write_manifest(manifest_path, payload) 324 325 326def _source_dist(tmp_path: Path, *, include_models: bool = False) -> Path: 327 dist = tmp_path / "dist" 328 checker.write_inert_packages(dist, include_models=include_models) 329 return dist 330 331 332def _build_ready( 333 tmp_path: Path, 334 *, 335 include_models: bool = False, 336 hook=None, 337) -> tuple[Path, list[checker.Failure]]: 338 ready = tmp_path / "ready" 339 failures = checker.build_and_promote_candidate( 340 _source_dist(tmp_path, include_models=include_models), 341 ready, 342 source_commit=VALID_COMMIT, 343 evidence_by_lane=checker.fixture_evidence_by_lane(), 344 include_models=include_models, 345 _post_promote_hook=hook, 346 ) 347 return ready, failures 348 349 350def test_python_whitespace_code_point_table_is_explicit() -> None: 351 assert len(PYTHON_WHITESPACE_CODE_POINTS) == 29 352 assert set(PYTHON_WHITESPACE_CODE_POINTS) == { 353 0x09, 354 0x0A, 355 0x0B, 356 0x0C, 357 0x0D, 358 0x1C, 359 0x1D, 360 0x1E, 361 0x1F, 362 0x20, 363 0x85, 364 0xA0, 365 0x1680, 366 0x2000, 367 0x2001, 368 0x2002, 369 0x2003, 370 0x2004, 371 0x2005, 372 0x2006, 373 0x2007, 374 0x2008, 375 0x2009, 376 0x200A, 377 0x2028, 378 0x2029, 379 0x202F, 380 0x205F, 381 0x3000, 382 } 383 384 385def test_script_runs_without_site_packages_from_outside_repo(tmp_path: Path) -> None: 386 env = os.environ.copy() 387 env.pop("PYTHONPATH", None) 388 env.pop("VIRTUAL_ENV", None) 389 390 result = subprocess.run( 391 [sys.executable, "-S", "-E", str(SCRIPT), "--help"], 392 cwd=tmp_path, 393 env=env, 394 capture_output=True, 395 text=True, 396 check=False, 397 timeout=15, 398 ) 399 400 assert result.returncode == 0 401 assert "Rust release manifests" in result.stdout 402 403 404def test_vendored_schema_digest_and_trailing_newline() -> None: 405 data = checker.SCHEMA_PATH.read_bytes() 406 407 assert len(data) == 4416 408 assert data.endswith(b"\n") 409 assert hashlib.sha256(data).hexdigest() == checker.SCHEMA_SHA256 410 411 412def test_load_schema_checks_draft_2020_12_and_format_checker() -> None: 413 schema = checker.load_schema() 414 415 assert schema["$id"] == checker.SCHEMA_ID 416 assert schema["$schema"] == checker.SCHEMA_DRAFT 417 418 419def test_validate_manifest_rejects_schema_level_invalid_payload( 420 tmp_path: Path, 421) -> None: 422 release_dir = _candidate(tmp_path) 423 manifest = _manifest_for_lane(release_dir, "source") 424 payload = _load_manifest(manifest) 425 payload["unexpected"] = "not in schema" 426 _write_manifest(manifest, payload) 427 428 failures = checker.validate_manifest_file(manifest) 429 430 _assert_error(failures, "manifest does not match Rust release manifest schema") 431 432 433def test_main_mode_selection_fixtures_manifest_release_dir(tmp_path: Path) -> None: 434 release_dir = _candidate(tmp_path) 435 manifest = _manifest_for_lane(release_dir, "source") 436 437 assert checker.main([], env={}) == 0 438 assert checker.main([], env={"MANIFEST": str(manifest)}) == 0 439 assert ( 440 checker.main( 441 [], 442 env={"RELEASE_DIR": str(release_dir), "SOURCE_COMMIT": VALID_COMMIT}, 443 ) 444 == 0 445 ) 446 447 448def test_main_rejects_conflicting_env_modes(tmp_path: Path) -> None: 449 assert ( 450 checker.main( 451 [], 452 env={ 453 "MANIFEST": str(tmp_path / "manifest.json"), 454 "RELEASE_DIR": str(tmp_path), 455 }, 456 ) 457 == 1 458 ) 459 assert ( 460 checker.main( 461 [], 462 env={ 463 "RELEASE_DIR": str(tmp_path), 464 "SOURCE_COMMIT": "abc", 465 }, 466 ) 467 == 1 468 ) 469 assert ( 470 checker.main( 471 [], 472 env={ 473 "MANIFEST": str(tmp_path / "manifest.json"), 474 "SOURCE_COMMIT": VALID_COMMIT, 475 }, 476 ) 477 == 1 478 ) 479 480 481@pytest.mark.parametrize( 482 "lane", 483 ["source", "linux-x86_64-musl", "linux-aarch64-musl", "macos-arm64"], 484) 485def test_generate_manifest_valid_lane_shapes( 486 tmp_path: Path, lane: checker.LaneName 487) -> None: 488 artifact_name = next( 489 name 490 for name, (artifact_lane, _target) in checker.rust_artifact_targets().items() 491 if artifact_lane == lane 492 ) 493 artifact_path = tmp_path / artifact_name 494 artifact_path.write_bytes(b"artifact bytes\n") 495 496 generated, failures = checker.generate_manifest( 497 artifact_path, 498 lane=lane, 499 evidence=checker.fixture_evidence_by_lane()[lane], 500 cohort=checker.CohortInputs( 501 product=checker.PRODUCT, 502 version=checker._current_version(), 503 source_commit=VALID_COMMIT, 504 source_dirty=False, 505 active_exceptions=(), 506 ), 507 ) 508 509 assert failures == [] 510 assert generated is not None 511 assert generated.bytes.endswith(b"\n") 512 assert ( 513 generated.payload["target"] == checker.rust_artifact_targets()[artifact_name][1] 514 ) 515 516 517def test_release_dir_accepts_exact_15_without_models(tmp_path: Path) -> None: 518 release_dir = _candidate(tmp_path) 519 520 assert len(list(release_dir.iterdir())) == 15 521 assert ( 522 checker.validate_release_dir(release_dir, expected_source_commit=VALID_COMMIT) 523 == [] 524 ) 525 526 527def test_release_dir_accepts_exact_17_with_models(tmp_path: Path) -> None: 528 release_dir = _candidate(tmp_path, include_models=True) 529 530 assert len(list(release_dir.iterdir())) == 17 531 assert ( 532 checker.validate_release_dir(release_dir, expected_source_commit=VALID_COMMIT) 533 == [] 534 ) 535 536 537def test_release_dir_rejects_one_file_models_set(tmp_path: Path) -> None: 538 release_dir = _candidate(tmp_path) 539 model_name = sorted(checker._models_expected_names())[0] 540 (release_dir / model_name).write_bytes(b"leftover model\n") 541 542 failures = checker.validate_release_dir( 543 release_dir, expected_source_commit=VALID_COMMIT 544 ) 545 546 _assert_error(failures, "release directory contains exactly one models archive") 547 548 549def test_release_dir_rejects_wrong_model_version_pair(tmp_path: Path) -> None: 550 release_dir = _candidate(tmp_path) 551 (release_dir / "solstone_journal_models-0.0.0.tar.gz").write_bytes(b"wrong\n") 552 (release_dir / "solstone_journal_models-0.0.0-py3-none-any.whl").write_bytes( 553 b"wrong\n" 554 ) 555 556 failures = checker.validate_release_dir( 557 release_dir, expected_source_commit=VALID_COMMIT 558 ) 559 560 _assert_error( 561 failures, "models archive names do not match current models version pair" 562 ) 563 564 565def test_release_dir_rejects_skipped_model_leftover(tmp_path: Path) -> None: 566 release_dir = _candidate(tmp_path) 567 for name in list(checker.expected_package_names(include_models=False))[:2]: 568 (release_dir / name).unlink() 569 for name in checker._models_expected_names(): 570 (release_dir / name).write_bytes(b"skipped model\n") 571 572 failures = checker.validate_release_dir( 573 release_dir, expected_source_commit=VALID_COMMIT 574 ) 575 576 _assert_error(failures, "15-file candidate contains models archive leftover") 577 578 579def test_release_dir_rejects_unknown_missing_extra_assets_and_case_collision( 580 tmp_path: Path, 581) -> None: 582 release_dir = _candidate(tmp_path) 583 (release_dir / "unknown.whl").write_bytes(b"unknown\n") 584 (release_dir / checker.expected_package_names(include_models=False)[0]).unlink() 585 for name in checker._models_expected_names(): 586 (release_dir / name).write_bytes(b"extra\n") 587 (release_dir / "CASE.txt").write_bytes(b"a\n") 588 (release_dir / "case.TXT").write_bytes(b"b\n") 589 590 failures = checker.validate_release_dir( 591 release_dir, expected_source_commit=VALID_COMMIT 592 ) 593 594 _assert_error(failures, "release directory contains unknown asset") 595 _assert_error(failures, "release directory is missing required assets") 596 _assert_error(failures, "release directory contains extra assets") 597 _assert_error(failures, "release directory contains case-colliding filenames") 598 599 600def test_release_dir_rejects_special_file_entry(tmp_path: Path) -> None: 601 release_dir = _candidate(tmp_path) 602 package = release_dir / checker.expected_package_names(include_models=False)[0] 603 package.unlink() 604 package.mkdir() 605 606 failures = checker.validate_release_dir( 607 release_dir, expected_source_commit=VALID_COMMIT 608 ) 609 610 _assert_error(failures, "release file is not a regular non-symlink file") 611 612 613def test_release_dir_rejects_extra_or_pure_package_manifest(tmp_path: Path) -> None: 614 release_dir = _candidate(tmp_path) 615 pure_artifact = next( 616 name 617 for name in checker.expected_package_names(include_models=False) 618 if name.startswith("solstone-") 619 ) 620 manifest = release_dir / f"{pure_artifact}.rust-release-manifest.json" 621 source_payload = _load_manifest(_manifest_for_lane(release_dir, "source")) 622 source_payload["target"] = {"kind": "source"} 623 digest = hashlib.sha256((release_dir / pure_artifact).read_bytes()).hexdigest() 624 source_payload["artifacts"] = [ 625 { 626 "path": pure_artifact, 627 "sha256": digest, 628 "bytes": (release_dir / pure_artifact).stat().st_size, 629 } 630 ] 631 _write_manifest(manifest, source_payload) 632 633 failures = checker.validate_release_dir( 634 release_dir, expected_source_commit=VALID_COMMIT 635 ) 636 637 _assert_error(failures, "manifest covers a non-Rust release artifact") 638 _assert_error( 639 failures, "release directory must contain exactly four Rust manifests" 640 ) 641 642 643def test_release_dir_rejects_duplicate_coverage(tmp_path: Path) -> None: 644 release_dir = _candidate(tmp_path) 645 original = _manifest_for_lane(release_dir, "source") 646 duplicate = release_dir / "duplicate.rust-release-manifest.json" 647 duplicate.write_bytes(original.read_bytes()) 648 649 failures = checker.validate_release_dir( 650 release_dir, expected_source_commit=VALID_COMMIT 651 ) 652 653 _assert_error(failures, "Rust artifact is covered by multiple manifests") 654 655 656def test_release_dir_rejects_unmanifested_rust_artifact(tmp_path: Path) -> None: 657 release_dir = _candidate(tmp_path) 658 _manifest_for_lane(release_dir, "macos-arm64").unlink() 659 660 failures = checker.validate_release_dir( 661 release_dir, expected_source_commit=VALID_COMMIT 662 ) 663 664 _assert_error(failures, "Rust artifact is not covered by any manifest") 665 666 667def test_release_dir_rejects_artifact_target_swap(tmp_path: Path) -> None: 668 release_dir = _candidate(tmp_path) 669 manifest = _manifest_for_lane(release_dir, "linux-x86_64-musl") 670 payload = _load_manifest(manifest) 671 payload["target"] = checker.rust_artifact_targets()[ 672 _artifact_for_lane(release_dir, "macos-arm64").name 673 ][1] 674 _write_manifest(manifest, payload) 675 676 failures = checker.validate_release_dir( 677 release_dir, expected_source_commit=VALID_COMMIT 678 ) 679 680 _assert_error(failures, "artifact target does not match release lane") 681 682 683def test_release_dir_rejects_mixed_cohort_including_advisory_time( 684 tmp_path: Path, 685) -> None: 686 release_dir = _candidate(tmp_path) 687 manifest = _manifest_for_lane(release_dir, "source") 688 payload = _load_manifest(manifest) 689 payload["dependency_policy"]["advisory_checked_at"] = "2026-07-21T00:00:00Z" 690 _write_manifest(manifest, payload) 691 692 failures = checker.validate_release_dir( 693 release_dir, expected_source_commit=VALID_COMMIT 694 ) 695 696 _assert_error(failures, "Rust release manifests do not agree on cohort fields") 697 698 699def test_release_dir_rejects_false_source_commit(tmp_path: Path) -> None: 700 release_dir = _candidate(tmp_path) 701 702 failures = checker.validate_release_dir( 703 release_dir, expected_source_commit="b" * 40 704 ) 705 706 _assert_error(failures, "source_commit does not match SOURCE_COMMIT") 707 708 709def test_rustc_verbose_allows_only_lane_bound_host_difference(tmp_path: Path) -> None: 710 release_dir = _candidate(tmp_path) 711 712 assert ( 713 checker.validate_release_dir(release_dir, expected_source_commit=VALID_COMMIT) 714 == [] 715 ) 716 717 source_manifest = _manifest_for_lane(release_dir, "source") 718 payload = _load_manifest(source_manifest) 719 payload["rust"]["rustc_verbose"] = checker.fixture_rustc_verbose( 720 "aarch64-apple-darwin" 721 ) 722 _write_manifest(source_manifest, payload) 723 failures = checker.validate_release_dir( 724 release_dir, expected_source_commit=VALID_COMMIT 725 ) 726 _assert_error(failures, "rustc host is not an allowed build host") 727 728 729def test_rustc_host_mismatch_redacts_host_value( 730 tmp_path: Path, capsys: pytest.CaptureFixture[str] 731) -> None: 732 host = "buildhost01" 733 release_dir = _candidate(tmp_path) 734 manifest = _manifest_for_lane(release_dir, "source") 735 payload = _load_manifest(manifest) 736 payload["rust"]["rustc_verbose"] = checker.fixture_rustc_verbose(host) 737 _write_manifest(manifest, payload) 738 739 failures = checker.validate_manifest_file(manifest) 740 741 _assert_error(failures, "rustc host is not an allowed build host") 742 _assert_redacted(failures, (host,)) 743 _assert_formatted_redacted(failures, (host,), capsys) 744 745 746@pytest.mark.parametrize("code_point", RUSTC_REJECTED_SEPARATOR_CODE_POINTS) 747def test_rustc_verbose_rejects_non_space_separator_code_points( 748 tmp_path: Path, 749 capsys: pytest.CaptureFixture[str], 750 code_point: int, 751) -> None: 752 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 753 canonical_lines = canonical.split("\n") 754 artifact_path = _source_artifact(tmp_path) 755 756 for line_index in range(1, len(canonical_lines)): 757 label, value = canonical_lines[line_index].split(": ", 1) 758 mutant_line = f"{label}:{chr(code_point)}{value}" 759 mutant = _rustc_line_mutant(canonical, line_index, mutant_line) 760 if code_point == 0x0A: 761 _assert_rustc_lf_separator_mutant_rejected( 762 artifact_path, 763 capsys, 764 canonical=canonical, 765 mutant=mutant, 766 line_index=line_index, 767 ) 768 else: 769 _assert_rustc_line_mutant_rejected( 770 artifact_path, 771 capsys, 772 canonical=canonical, 773 mutant=mutant, 774 line_index=line_index, 775 ) 776 777 778@pytest.mark.parametrize( 779 "spacing_case", ("zero", "two", "three", "pre_colon", "trailing") 780) 781def test_rustc_verbose_rejects_noncanonical_space_counts( 782 tmp_path: Path, 783 capsys: pytest.CaptureFixture[str], 784 spacing_case: str, 785) -> None: 786 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 787 canonical_lines = canonical.split("\n") 788 artifact_path = _source_artifact(tmp_path) 789 790 for line_index in range(1, len(canonical_lines)): 791 label, value = canonical_lines[line_index].split(": ", 1) 792 if spacing_case == "zero": 793 mutant_line = f"{label}:{value}" 794 elif spacing_case == "two": 795 mutant_line = f"{label}: {value}" 796 elif spacing_case == "three": 797 mutant_line = f"{label}: {value}" 798 elif spacing_case == "pre_colon": 799 mutant_line = f"{label} : {value}" 800 else: 801 mutant_line = f"{label}: {value} " 802 mutant = _rustc_line_mutant(canonical, line_index, mutant_line) 803 _assert_rustc_line_mutant_rejected( 804 artifact_path, 805 capsys, 806 canonical=canonical, 807 mutant=mutant, 808 line_index=line_index, 809 ) 810 811 812def test_rustc_verbose_rejects_crlf_line_boundary( 813 tmp_path: Path, capsys: pytest.CaptureFixture[str] 814) -> None: 815 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 816 lines = canonical.split("\n") 817 mutant = canonical.replace("\n", "\r\n", 1) 818 assert "\r\n" in mutant 819 assert mutant.replace("\r\n", "\n", 1) == canonical 820 _assert_rustc_mutant_rejected( 821 _source_artifact(tmp_path), 822 capsys, 823 mutant=mutant, 824 forbidden=(mutant, f"{lines[0]}\r\n{lines[1]}"), 825 ) 826 827 828def test_rustc_verbose_rejects_lone_cr_line_boundary( 829 tmp_path: Path, capsys: pytest.CaptureFixture[str] 830) -> None: 831 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 832 lines = canonical.split("\n") 833 mutant = canonical.replace("\n", "\r", 1) 834 assert "\r" in mutant 835 assert mutant.replace("\r", "\n", 1) == canonical 836 _assert_rustc_mutant_rejected( 837 _source_artifact(tmp_path), 838 capsys, 839 mutant=mutant, 840 forbidden=(mutant, f"{lines[0]}\r{lines[1]}"), 841 ) 842 843 844@pytest.mark.parametrize("separator", ("\u0085", "\u2028", "\u2029")) 845def test_rustc_verbose_rejects_unicode_line_boundary_replacements( 846 tmp_path: Path, 847 capsys: pytest.CaptureFixture[str], 848 separator: str, 849) -> None: 850 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 851 lines = canonical.split("\n") 852 mutant = canonical.replace("\n", separator, 1) 853 assert separator in mutant 854 assert mutant.replace(separator, "\n", 1) == canonical 855 _assert_rustc_mutant_rejected( 856 _source_artifact(tmp_path), 857 capsys, 858 mutant=mutant, 859 forbidden=(mutant, f"{lines[0]}{separator}{lines[1]}"), 860 ) 861 862 863def test_rustc_verbose_rejects_doubled_lf_line_boundary( 864 tmp_path: Path, capsys: pytest.CaptureFixture[str] 865) -> None: 866 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 867 lines = canonical.split("\n") 868 mutant = canonical.replace("\n", "\n\n", 1) 869 assert mutant.count("\n") == canonical.count("\n") + 1 870 assert mutant.replace("\n\n", "\n", 1) == canonical 871 _assert_rustc_mutant_rejected( 872 _source_artifact(tmp_path), 873 capsys, 874 mutant=mutant, 875 forbidden=(mutant, f"{lines[0]}\n\n{lines[1]}"), 876 ) 877 878 879def test_rustc_verbose_rejects_leading_lf( 880 tmp_path: Path, capsys: pytest.CaptureFixture[str] 881) -> None: 882 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 883 lines = canonical.split("\n") 884 mutant = "\n" + canonical 885 assert mutant.startswith("\n") 886 assert mutant.count("\n") == canonical.count("\n") + 1 887 assert mutant.removeprefix("\n") == canonical 888 _assert_rustc_mutant_rejected( 889 _source_artifact(tmp_path), 890 capsys, 891 mutant=mutant, 892 forbidden=(mutant, f"\n{lines[0]}"), 893 ) 894 895 896def test_rustc_verbose_rejects_trailing_lf( 897 tmp_path: Path, capsys: pytest.CaptureFixture[str] 898) -> None: 899 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 900 lines = canonical.split("\n") 901 mutant = canonical + "\n" 902 assert mutant.endswith("\n") 903 assert mutant.count("\n") == canonical.count("\n") + 1 904 assert mutant.removesuffix("\n") == canonical 905 _assert_rustc_mutant_rejected( 906 _source_artifact(tmp_path), 907 capsys, 908 mutant=mutant, 909 forbidden=(mutant, f"{lines[-1]}\n"), 910 ) 911 912 913@pytest.mark.parametrize( 914 ("name", "separator"), (("nbsp", "\u00a0"), ("zero", ""), ("two", " ")) 915) 916def test_rustc_verbose_separator_rejections_cover_file_and_candidate_channels( 917 tmp_path: Path, 918 capsys: pytest.CaptureFixture[str], 919 name: str, 920 separator: str, 921) -> None: 922 canonical = checker.fixture_rustc_verbose("x86_64-unknown-linux-gnu") 923 lines = canonical.split("\n") 924 label, value = lines[1].split(": ", 1) 925 mutant_line = f"{label}:{separator}{value}" 926 mutant = _rustc_line_mutant(canonical, 1, mutant_line) 927 928 release_dir = _candidate(tmp_path / f"file-{name}") 929 manifest = _manifest_for_lane(release_dir, "source") 930 payload = _load_manifest(manifest) 931 payload["rust"]["rustc_verbose"] = mutant 932 _write_manifest(manifest, payload) 933 failures = checker.validate_manifest_file(manifest) 934 _assert_malformed_redacted(failures, (mutant, mutant_line), capsys) 935 936 evidence = checker.fixture_evidence_by_lane() 937 mutated_evidence = dict(evidence) 938 mutated_evidence["source"] = replace(evidence["source"], rustc_verbose=mutant) 939 failures = checker.write_inert_candidate( 940 tmp_path / f"generated-{name}", 941 include_models=False, 942 evidence_by_lane=mutated_evidence, 943 ) 944 _assert_malformed_redacted(failures, (mutant, mutant_line), capsys) 945 946 947@pytest.mark.parametrize( 948 ("lane", "host"), 949 ( 950 ("source", "x86_64-unknown-linux-gnu"), 951 ("macos-arm64", "aarch64-apple-darwin"), 952 ), 953) 954def test_rustc_verbose_canonical_evidence_is_byte_exact( 955 tmp_path: Path, 956 lane: str, 957 host: str, 958) -> None: 959 canonical = checker.fixture_rustc_verbose(host) 960 parsed, parse_failures = checker.parse_rustc_verbose(canonical) 961 assert parse_failures == [] 962 assert parsed is not None 963 964 release_dir = tmp_path / lane 965 checker.write_inert_packages(release_dir, include_models=False) 966 artifact_path = _artifact_for_lane(release_dir, lane) 967 evidence = checker.fixture_evidence_by_lane()[lane] 968 assert evidence.rustc_verbose == canonical 969 970 generated, generate_failures = checker.generate_manifest( 971 artifact_path, 972 lane=lane, 973 evidence=evidence, 974 cohort=checker._default_cohort(VALID_COMMIT), 975 ) 976 assert generate_failures == [] 977 assert generated is not None 978 manifest_bytes = checker.canonical_json_bytes(generated.payload) 979 assert manifest_bytes == generated.bytes 980 981 decoded = json.loads(manifest_bytes) 982 rustc_verbose = decoded["rust"]["rustc_verbose"] 983 assert rustc_verbose == canonical 984 assert rustc_verbose.encode("utf-8") == canonical.encode("utf-8") 985 986 manifest_path = release_dir / generated.manifest_name 987 manifest_path.write_bytes(manifest_bytes) 988 assert checker.validate_manifest_file(manifest_path) == [] 989 990 991def test_rustc_verbose_rejects_malformed_spoof_and_mixed_labeled_lines( 992 tmp_path: Path, 993) -> None: 994 release_dir = _candidate(tmp_path) 995 manifest = _manifest_for_lane(release_dir, "macos-arm64") 996 payload = _load_manifest(manifest) 997 payload["rust"]["rustc_verbose"] = checker.fixture_rustc_verbose( 998 "x86_64-unknown-linux-gnu" 999 ) 1000 _write_manifest(manifest, payload) 1001 failures = checker.validate_release_dir( 1002 release_dir, expected_source_commit=VALID_COMMIT 1003 ) 1004 _assert_error(failures, "rustc host is not an allowed build host") 1005 1006 release_dir = _candidate(tmp_path / "malformed") 1007 manifest = _manifest_for_lane(release_dir, "source") 1008 payload = _load_manifest(manifest) 1009 payload["rust"]["rustc_verbose"] = "rustc nope" 1010 _write_manifest(manifest, payload) 1011 failures = checker.validate_release_dir( 1012 release_dir, expected_source_commit=VALID_COMMIT 1013 ) 1014 _assert_error(failures, "rustc_verbose is malformed") 1015 1016 release_dir = _candidate(tmp_path / "mixed") 1017 manifest = _manifest_for_lane(release_dir, "source") 1018 payload = _load_manifest(manifest) 1019 payload["rust"]["rustc_verbose"] = payload["rust"]["rustc_verbose"].replace( 1020 f"LLVM version: {checker.RUSTC_LLVM_PIN}", "LLVM version: 22.0.0" 1021 ) 1022 _write_manifest(manifest, payload) 1023 failures = checker.validate_release_dir( 1024 release_dir, expected_source_commit=VALID_COMMIT 1025 ) 1026 _assert_error(failures, "rustc_verbose is malformed") 1027 1028 1029def test_rust_evidence_rejects_uniformly_wrong_toolchain_pins(tmp_path: Path) -> None: 1030 release_dir = _candidate(tmp_path) 1031 wrong_rustc = "\n".join( 1032 [ 1033 "rustc 1.96.0 (111111111 2026-01-01)", 1034 "binary: rustc", 1035 "commit-hash: 1111111111111111111111111111111111111111", 1036 "commit-date: 2026-01-01", 1037 "host: x86_64-unknown-linux-gnu", 1038 "release: 1.96.0", 1039 "LLVM version: 21.0.0", 1040 ] 1041 ) 1042 for lane in checker.LANES: 1043 manifest = _manifest_for_lane(release_dir, lane) 1044 payload = _load_manifest(manifest) 1045 payload["rust"]["rustc_verbose"] = wrong_rustc.replace( 1046 "x86_64-unknown-linux-gnu", checker.LANE_HOSTS[lane] 1047 ) 1048 payload["rust"]["cargo_version"] = "cargo 1.96.0 (222222222 2026-01-01)" 1049 payload["dependency_policy"]["cargo_deny_version"] = "cargo-deny 0.1.0" 1050 _write_manifest(manifest, payload) 1051 1052 failures = checker.validate_release_dir( 1053 release_dir, expected_source_commit=VALID_COMMIT 1054 ) 1055 1056 _assert_error(failures, "rustc_verbose is malformed") 1057 _assert_error(failures, "cargo_version is malformed") 1058 _assert_error(failures, "cargo_deny_version is not pinned") 1059 1060 1061def test_rustc_verbose_rejects_wrong_binary(tmp_path: Path) -> None: 1062 release_dir = _candidate(tmp_path) 1063 manifest = _manifest_for_lane(release_dir, "source") 1064 payload = _load_manifest(manifest) 1065 payload["rust"]["rustc_verbose"] = payload["rust"]["rustc_verbose"].replace( 1066 f"binary: {checker.RUSTC_BINARY_PIN}", "binary: rustdoc" 1067 ) 1068 _write_manifest(manifest, payload) 1069 1070 failures = checker.validate_manifest_file(manifest) 1071 1072 _assert_error(failures, "rustc_verbose is malformed") 1073 1074 1075def test_rustc_verbose_rejects_missing_duplicate_unknown_labels( 1076 tmp_path: Path, 1077) -> None: 1078 cases: tuple[tuple[str, list[str]], ...] = ( 1079 ("missing", _rustc_lines()[:3] + _rustc_lines()[4:]), 1080 ( 1081 "duplicate", 1082 [ 1083 *_rustc_lines()[:4], 1084 f"commit-date: {checker.RUSTC_COMMIT_DATE_PIN}", 1085 *_rustc_lines()[5:], 1086 ], 1087 ), 1088 ( 1089 "unknown", 1090 [ 1091 *_rustc_lines()[:5], 1092 "channel: stable", 1093 _rustc_lines()[6], 1094 ], 1095 ), 1096 ) 1097 for name, lines in cases: 1098 release_dir = _candidate(tmp_path / name) 1099 manifest = _manifest_for_lane(release_dir, "source") 1100 payload = _load_manifest(manifest) 1101 payload["rust"]["rustc_verbose"] = "\n".join(lines) 1102 _write_manifest(manifest, payload) 1103 1104 failures = checker.validate_manifest_file(manifest) 1105 1106 _assert_error(failures, "rustc_verbose is malformed") 1107 1108 1109def test_rustc_verbose_rejects_wrong_commit_date_release_llvm_and_bad_host( 1110 tmp_path: Path, 1111) -> None: 1112 replacements = ( 1113 ( 1114 "commit", 1115 f"commit-hash: {checker.RUSTC_COMMIT_HASH_PIN}", 1116 "commit-hash: " + "b" * 40, 1117 ), 1118 ( 1119 "date", 1120 f"commit-date: {checker.RUSTC_COMMIT_DATE_PIN}", 1121 "commit-date: 2026-01-01", 1122 ), 1123 ("release", f"release: {checker.RUSTC_RELEASE_PIN}", "release: 1.96.0"), 1124 ("llvm", f"LLVM version: {checker.RUSTC_LLVM_PIN}", "LLVM version: 21.0.0"), 1125 ) 1126 for name, old, new in replacements: 1127 release_dir = _candidate(tmp_path / name) 1128 manifest = _manifest_for_lane(release_dir, "source") 1129 payload = _load_manifest(manifest) 1130 payload["rust"]["rustc_verbose"] = payload["rust"]["rustc_verbose"].replace( 1131 old, new 1132 ) 1133 _write_manifest(manifest, payload) 1134 1135 failures = checker.validate_manifest_file(manifest) 1136 1137 _assert_error(failures, "rustc_verbose is malformed") 1138 1139 release_dir = _candidate(tmp_path / "host") 1140 manifest = _manifest_for_lane(release_dir, "source") 1141 payload = _load_manifest(manifest) 1142 payload["rust"]["rustc_verbose"] = checker.fixture_rustc_verbose("localhost") 1143 _write_manifest(manifest, payload) 1144 failures = checker.validate_manifest_file(manifest) 1145 _assert_error(failures, "rustc_verbose contains disallowed content") 1146 _assert_error(failures, "rustc host is not an allowed build host") 1147 1148 1149def test_rustc_verbose_rejects_blank_interstitial_reordered_and_extra_lines( 1150 tmp_path: Path, 1151) -> None: 1152 lines = _rustc_lines() 1153 cases: tuple[tuple[str, list[str]], ...] = ( 1154 ("blank", [*lines[:3], "", *lines[3:]]), 1155 ("reordered", [lines[0], lines[2], lines[1], *lines[3:]]), 1156 ("extra", [*lines, "extra: public"]), 1157 ) 1158 for name, rustc_lines in cases: 1159 release_dir = _candidate(tmp_path / name) 1160 manifest = _manifest_for_lane(release_dir, "source") 1161 payload = _load_manifest(manifest) 1162 payload["rust"]["rustc_verbose"] = "\n".join(rustc_lines) 1163 _write_manifest(manifest, payload) 1164 1165 failures = checker.validate_manifest_file(manifest) 1166 1167 _assert_error(failures, "rustc_verbose is malformed") 1168 1169 1170def test_rust_evidence_redacts_canaries_from_failures_and_formatted_output( 1171 tmp_path: Path, capsys: pytest.CaptureFixture[str] 1172) -> None: 1173 token = "sk-abcdefghijklmnopqrstuvwx" 1174 private_path = "/Users/jer/.cargo/bin" 1175 release_dir = _candidate(tmp_path) 1176 manifest = _manifest_for_lane(release_dir, "source") 1177 payload = _load_manifest(manifest) 1178 payload["rust"]["rustc_verbose"] = "\n".join( 1179 [*_rustc_lines(), f"leak: {token} {private_path}"] 1180 ) 1181 _write_manifest(manifest, payload) 1182 1183 failures = checker.validate_release_dir( 1184 release_dir, expected_source_commit=VALID_COMMIT 1185 ) 1186 1187 assert failures 1188 _assert_error(failures, "rustc_verbose contains disallowed content") 1189 _assert_redacted(failures, (token, private_path)) 1190 _assert_formatted_redacted(failures, (token, private_path), capsys) 1191 1192 1193def test_cargo_and_cargo_deny_pins_are_enforced_without_echoing_input( 1194 tmp_path: Path, capsys: pytest.CaptureFixture[str] 1195) -> None: 1196 token = "sk-abcdefghijklmnopqrstuvwx" 1197 private_path = "/Users/jer/.cargo/bin" 1198 release_dir = _candidate(tmp_path) 1199 manifest = _manifest_for_lane(release_dir, "source") 1200 payload = _load_manifest(manifest) 1201 payload["rust"]["cargo_version"] = f"cargo 1.96.0 ({token} 2026-01-01)" 1202 payload["dependency_policy"]["cargo_deny_version"] = ( 1203 f"cargo-deny 0.1.0 {private_path} {token}" 1204 ) 1205 _write_manifest(manifest, payload) 1206 1207 failures = checker.validate_manifest_file(manifest) 1208 1209 _assert_error(failures, "cargo_version is malformed") 1210 _assert_error(failures, "cargo_deny_version is not pinned") 1211 _assert_error(failures, "cargo_version contains disallowed content") 1212 _assert_error(failures, "cargo_deny_version contains disallowed content") 1213 _assert_redacted(failures, (token, private_path)) 1214 _assert_formatted_redacted(failures, (token, private_path), capsys) 1215 1216 1217def test_cargo_and_cargo_deny_reject_surrounding_whitespace_and_control( 1218 tmp_path: Path, 1219) -> None: 1220 cargo_variants = ( 1221 " " + checker.CARGO_VERSION_PIN, 1222 checker.CARGO_VERSION_PIN + " ", 1223 checker.CARGO_VERSION_PIN + "\n", 1224 checker.CARGO_VERSION_PIN + "\t", 1225 checker.CARGO_VERSION_PIN + "\x00", 1226 ) 1227 for index, variant in enumerate(cargo_variants): 1228 release_dir = _candidate(tmp_path / f"cargo-{index}") 1229 manifest = _manifest_for_lane(release_dir, "source") 1230 payload = _load_manifest(manifest) 1231 payload["rust"]["cargo_version"] = variant 1232 _write_manifest(manifest, payload) 1233 1234 failures = checker.validate_manifest_file(manifest) 1235 1236 _assert_error(failures, "cargo_version is malformed") 1237 _assert_redacted(failures, (variant,)) 1238 1239 cargo_deny_variants = ( 1240 " " + checker.CARGO_DENY_PIN, 1241 checker.CARGO_DENY_PIN + " ", 1242 checker.CARGO_DENY_PIN + "\n", 1243 checker.CARGO_DENY_PIN + "\t", 1244 checker.CARGO_DENY_PIN + "\x00", 1245 ) 1246 for index, variant in enumerate(cargo_deny_variants): 1247 release_dir = _candidate(tmp_path / f"deny-{index}") 1248 manifest = _manifest_for_lane(release_dir, "source") 1249 payload = _load_manifest(manifest) 1250 payload["dependency_policy"]["cargo_deny_version"] = variant 1251 _write_manifest(manifest, payload) 1252 1253 failures = checker.validate_manifest_file(manifest) 1254 1255 _assert_error(failures, "cargo_deny_version is not pinned") 1256 _assert_redacted(failures, (variant,)) 1257 1258 1259def test_generate_rejects_cargo_and_cargo_deny_surrounding_whitespace_and_redacts( 1260 tmp_path: Path, capsys: pytest.CaptureFixture[str] 1261) -> None: 1262 evidence = checker.fixture_evidence_by_lane() 1263 cargo_evidence = dict(evidence) 1264 cargo_evidence["source"] = replace( 1265 evidence["source"], cargo_version=checker.CARGO_VERSION_PIN + " " 1266 ) 1267 1268 failures = checker.write_inert_candidate( 1269 tmp_path / "gen-cargo", 1270 include_models=False, 1271 evidence_by_lane=cargo_evidence, 1272 ) 1273 1274 assert failures 1275 _assert_error(failures, "cargo_version is malformed") 1276 1277 deny_evidence = dict(evidence) 1278 deny_evidence["source"] = replace( 1279 evidence["source"], cargo_deny_version=" " + checker.CARGO_DENY_PIN 1280 ) 1281 1282 failures = checker.write_inert_candidate( 1283 tmp_path / "gen-deny", 1284 include_models=False, 1285 evidence_by_lane=deny_evidence, 1286 ) 1287 1288 assert failures 1289 _assert_error(failures, "cargo_deny_version is not pinned") 1290 1291 private_path = "/Users/jer/.cargo" 1292 token = "sk-abcdefghijklmnopqrstuvwx" 1293 deny_canary_evidence = dict(evidence) 1294 deny_canary_evidence["source"] = replace( 1295 evidence["source"], 1296 cargo_deny_version=f"{checker.CARGO_DENY_PIN} {private_path} {token}", 1297 ) 1298 1299 failures = checker.write_inert_candidate( 1300 tmp_path / "gen-deny-canary", 1301 include_models=False, 1302 evidence_by_lane=deny_canary_evidence, 1303 ) 1304 1305 assert failures 1306 _assert_error(failures, "cargo_deny_version is not pinned") 1307 _assert_error(failures, "cargo_deny_version contains disallowed content") 1308 _assert_redacted(failures, (private_path, token)) 1309 _assert_formatted_redacted(failures, (private_path, token), capsys) 1310 1311 1312def test_rust_evidence_accepts_pinned_linux_and_macos_hosts() -> None: 1313 cases: tuple[tuple[checker.LaneName, str], ...] = ( 1314 ("source", "x86_64-unknown-linux-gnu"), 1315 ("macos-arm64", "aarch64-apple-darwin"), 1316 ) 1317 for lane, host in cases: 1318 rustc, cargo, failures = checker._validate_rust_for_lane( 1319 lane, 1320 { 1321 "rustc_verbose": checker.fixture_rustc_verbose(host), 1322 "cargo_version": checker.CARGO_VERSION_PIN, 1323 }, 1324 ) 1325 1326 assert failures == [] 1327 assert rustc is not None 1328 assert rustc.host == host 1329 assert cargo == checker.CARGO_RELEASE_PIN 1330 1331 1332def test_native_tools_allowlists_by_lane() -> None: 1333 evidence = checker.fixture_evidence_by_lane() 1334 1335 for lane, lane_evidence in evidence.items(): 1336 assert checker.validate_native_tools(lane, lane_evidence.native_tools) == [] 1337 1338 failures = checker.validate_native_tools( 1339 "source", 1340 {"uv": pins.UV_PIN, "maturin": pins.MATURIN_PIN, "zig": pins.ZIG_PIN}, 1341 ) 1342 _assert_error(failures, "native_tools keys do not match lane allowlist") 1343 1344 failures = checker.validate_native_tools("source", {"uv": pins.UV_PIN}) 1345 _assert_error(failures, "native_tools keys do not match lane allowlist") 1346 1347 failures = checker.validate_native_tools( 1348 "macos-arm64", 1349 { 1350 "uv": pins.UV_PIN, 1351 "maturin": pins.MATURIN_PIN, 1352 "xcode": pins.MACOS_XCODE_PIN, 1353 "codesign": pins.MACOS_CODESIGN_PUBLIC_PIN, 1354 "notarytool": pins.MACOS_NOTARYTOOL_PIN, 1355 "signing_mode": "unsigned", 1356 }, 1357 ) 1358 _assert_error(failures, "macOS signing_mode is not signed-verified") 1359 1360 1361@pytest.mark.parametrize( 1362 ("value", "error"), 1363 [ 1364 ( 1365 f"{pins.UV_PIN}\nPATH=/tmp/bin", 1366 "native_tools value is not a normalized public single-line string", 1367 ), 1368 ( 1369 "TOKEN=abc123", 1370 "native_tools value is not a normalized public single-line string", 1371 ), 1372 ("secret token abc123", "native_tools value contains secret/token canary"), 1373 ( 1374 "built on build-host.local", 1375 "native_tools value contains private host, IP, or path", 1376 ), 1377 ( 1378 "reachable at 192.168.1.10", 1379 "native_tools value contains private host, IP, or path", 1380 ), 1381 ( 1382 "installed in /Users/jer/bin", 1383 "native_tools value contains private host, IP, or path", 1384 ), 1385 ("owner@example.com", "native_tools value contains email address"), 1386 ( 1387 "Developer ID Application: sol pbc", 1388 "native_tools value contains signing identity", 1389 ), 1390 ( 1391 "submission 123e4567-e89b-12d3-a456-426614174000", 1392 "native_tools value contains notarization submission ID", 1393 ), 1394 ], 1395) 1396def test_native_tools_rejects_canaries(value: str, error: str) -> None: 1397 tools = {"uv": value, "maturin": pins.MATURIN_PIN} 1398 1399 failures = checker.validate_native_tools("source", tools) 1400 1401 _assert_error(failures, error) 1402 1403 1404@pytest.mark.parametrize( 1405 "bad_path", ["../artifact.whl", "/tmp/artifact.whl", "a\\b.whl"] 1406) 1407def test_validate_manifest_rejects_path_traversal_absolute_backslash( 1408 tmp_path: Path, bad_path: str 1409) -> None: 1410 release_dir = _candidate(tmp_path) 1411 manifest = _manifest_for_lane(release_dir, "source") 1412 payload = _load_manifest(manifest) 1413 payload["artifacts"][0]["path"] = bad_path 1414 _write_manifest(manifest, payload) 1415 1416 failures = checker.validate_manifest_file(manifest) 1417 1418 _assert_error(failures, "artifact path is not a safe relative basename") 1419 1420 1421def test_validate_manifest_rejects_control_character_artifact_path( 1422 tmp_path: Path, 1423) -> None: 1424 release_dir = _candidate(tmp_path) 1425 manifest = _manifest_for_lane(release_dir, "source") 1426 payload = _load_manifest(manifest) 1427 payload["artifacts"][0]["path"] = "bad" + chr(10) + ".whl" 1428 _write_manifest(manifest, payload) 1429 1430 failures = checker.validate_manifest_file(manifest) 1431 1432 _assert_error(failures, "artifact path is not a safe relative basename") 1433 1434 1435def test_validate_manifest_rejects_symlink_missing_empty_and_hash_mismatch( 1436 tmp_path: Path, 1437) -> None: 1438 release_dir = _candidate(tmp_path / "missing") 1439 manifest = _manifest_for_lane(release_dir, "source") 1440 _artifact_for_lane(release_dir, "source").unlink() 1441 failures = checker.validate_manifest_file(manifest) 1442 _assert_error(failures, "manifest artifact is missing") 1443 1444 release_dir = _candidate(tmp_path / "empty") 1445 manifest = _manifest_for_lane(release_dir, "source") 1446 _artifact_for_lane(release_dir, "source").write_bytes(b"") 1447 failures = checker.validate_manifest_file(manifest) 1448 _assert_error(failures, "manifest artifact is empty") 1449 1450 release_dir = _candidate(tmp_path / "hash") 1451 manifest = _manifest_for_lane(release_dir, "source") 1452 _artifact_for_lane(release_dir, "source").write_bytes(b"mutated\n") 1453 failures = checker.validate_manifest_file(manifest) 1454 _assert_error(failures, "artifact sha256 does not match manifest") 1455 1456 release_dir = _candidate(tmp_path / "symlink") 1457 manifest = _manifest_for_lane(release_dir, "source") 1458 artifact = _artifact_for_lane(release_dir, "source") 1459 artifact.unlink() 1460 artifact.symlink_to( 1461 release_dir / checker.expected_package_names(include_models=False)[0] 1462 ) 1463 failures = checker.validate_manifest_file(manifest) 1464 _assert_error(failures, "release file is not a regular non-symlink file") 1465 1466 1467def test_validate_manifest_rejects_invalid_time_hash_commit_target_features( 1468 tmp_path: Path, 1469) -> None: 1470 release_dir = _candidate(tmp_path / "time") 1471 manifest = _manifest_for_lane(release_dir, "source") 1472 payload = _load_manifest(manifest) 1473 payload["dependency_policy"]["advisory_checked_at"] = "2026-07-20T00:00:00-06:00" 1474 _write_manifest(manifest, payload) 1475 failures = checker.validate_manifest_file(manifest) 1476 _assert_error(failures, "advisory timestamp is not RFC3339 UTC") 1477 1478 release_dir = _candidate(tmp_path / "hash") 1479 manifest = _manifest_for_lane(release_dir, "source") 1480 payload = _load_manifest(manifest) 1481 payload["artifacts"][0]["sha256"] = "ABC" 1482 _write_manifest(manifest, payload) 1483 failures = checker.validate_manifest_file(manifest) 1484 _assert_error(failures, "sha256 is not lowercase hex") 1485 1486 release_dir = _candidate(tmp_path / "commit") 1487 manifest = _manifest_for_lane(release_dir, "source") 1488 payload = _load_manifest(manifest) 1489 payload["source_commit"] = "abc" 1490 _write_manifest(manifest, payload) 1491 failures = checker.validate_manifest_file(manifest) 1492 _assert_error(failures, "source_commit is not a full lowercase commit") 1493 1494 release_dir = _candidate(tmp_path / "target") 1495 manifest = _manifest_for_lane(release_dir, "linux-x86_64-musl") 1496 payload = _load_manifest(manifest) 1497 payload["target"]["profile"] = "debug" 1498 _write_manifest(manifest, payload) 1499 failures = checker.validate_manifest_file(manifest) 1500 _assert_error(failures, "artifact target does not match release lane") 1501 1502 release_dir = _candidate(tmp_path / "features") 1503 manifest = _manifest_for_lane(release_dir, "linux-x86_64-musl") 1504 payload = _load_manifest(manifest) 1505 payload["target"]["features"] = ["extra"] 1506 _write_manifest(manifest, payload) 1507 failures = checker.validate_manifest_file(manifest) 1508 _assert_error(failures, "artifact target does not match release lane") 1509 1510 1511def test_validate_manifest_rejects_non_finite_json(tmp_path: Path) -> None: 1512 release_dir = _candidate(tmp_path) 1513 manifest = _manifest_for_lane(release_dir, "source") 1514 text = manifest.read_text(encoding="utf-8").replace( 1515 '"schema_version":1', '"schema_version":NaN' 1516 ) 1517 manifest.write_text(text, encoding="utf-8") 1518 1519 failures = checker.validate_manifest_file(manifest) 1520 1521 _assert_error(failures, "manifest JSON is invalid or non-finite") 1522 1523 1524def test_validate_manifest_accepts_historical_utc_timestamp(tmp_path: Path) -> None: 1525 release_dir = _candidate(tmp_path) 1526 manifest = _manifest_for_lane(release_dir, "source") 1527 payload = _load_manifest(manifest) 1528 payload["dependency_policy"]["advisory_checked_at"] = "2020-01-01T00:00:00+00:00" 1529 _write_manifest(manifest, payload) 1530 1531 failures = checker.validate_manifest_file(manifest) 1532 1533 assert failures == [] 1534 1535 1536def test_canonical_json_fixed_input_bytes_are_deterministic() -> None: 1537 payload = { 1538 "b": {"features": ["z", "a"]}, 1539 "active_exceptions": ["z", "a"], 1540 "a": 1, 1541 } 1542 1543 first = checker.canonical_json_bytes(payload) 1544 second = checker.canonical_json_bytes(payload) 1545 1546 assert first == second 1547 assert first.endswith(b"\n") 1548 assert b'"active_exceptions":["a","z"]' in first 1549 with pytest.raises(ValueError, match="non-finite"): 1550 checker.canonical_json_bytes({"x": float("nan")}) 1551 1552 1553def test_build_and_promote_candidate_success_is_whole_directory_rename( 1554 tmp_path: Path, 1555) -> None: 1556 ready, failures = _build_ready(tmp_path) 1557 1558 assert failures == [] 1559 assert ready.is_dir() 1560 assert not (tmp_path / "ready.staging").exists() 1561 assert len(list(ready.iterdir())) == 15 1562 assert ( 1563 checker.validate_release_dir(ready, expected_source_commit=VALID_COMMIT) == [] 1564 ) 1565 1566 1567def test_build_and_promote_candidate_rejects_lock_contention(tmp_path: Path) -> None: 1568 dist = _source_dist(tmp_path) 1569 ready = tmp_path / "ready" 1570 lock = (tmp_path / ".rust-release-candidate.lock").open("a+") 1571 try: 1572 fcntl.flock(lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) 1573 failures = checker.build_and_promote_candidate( 1574 dist, 1575 ready, 1576 source_commit=VALID_COMMIT, 1577 evidence_by_lane=checker.fixture_evidence_by_lane(), 1578 include_models=False, 1579 ) 1580 finally: 1581 fcntl.flock(lock.fileno(), fcntl.LOCK_UN) 1582 lock.close() 1583 1584 _assert_error(failures, "release candidate lock is already held") 1585 assert not ready.exists() 1586 assert not (tmp_path / "ready.staging").exists() 1587 1588 1589def test_build_and_promote_candidate_rolls_back_staging_on_pre_promotion_failure( 1590 tmp_path: Path, 1591) -> None: 1592 dist = _source_dist(tmp_path) 1593 (dist / checker.expected_package_names(include_models=False)[0]).unlink() 1594 ready = tmp_path / "ready" 1595 1596 failures = checker.build_and_promote_candidate( 1597 dist, 1598 ready, 1599 source_commit=VALID_COMMIT, 1600 evidence_by_lane=checker.fixture_evidence_by_lane(), 1601 include_models=False, 1602 ) 1603 1604 _assert_error(failures, "manifest artifact is missing") 1605 assert not ready.exists() 1606 assert not (tmp_path / "ready.staging").exists() 1607 1608 1609def test_build_and_promote_candidate_removes_ready_on_post_promotion_failure( 1610 tmp_path: Path, 1611) -> None: 1612 def mutate(path: Path) -> None: 1613 artifact = next(iter(sorted(checker._rust_artifact_names()))) 1614 (path / artifact).write_bytes(b"mutated\n") 1615 1616 ready, failures = _build_ready(tmp_path, hook=mutate) 1617 1618 _assert_error(failures, "artifact sha256 does not match manifest") 1619 assert not ready.exists() 1620 assert not (tmp_path / "ready.quarantine").exists() 1621 1622 1623def test_build_and_promote_candidate_quarantines_ready_when_post_promote_hook_raises( 1624 tmp_path: Path, 1625) -> None: 1626 ready = tmp_path / "ready" 1627 1628 def fail_after_promote(_path: Path) -> None: 1629 raise RuntimeError("hook boom") 1630 1631 with pytest.raises(RuntimeError, match="hook boom"): 1632 _build_ready(tmp_path, hook=fail_after_promote) 1633 1634 assert not ready.exists() 1635 assert not (tmp_path / "ready.staging").exists() 1636 assert not (tmp_path / "ready.quarantine").exists() 1637 1638 ready, failures = _build_ready(tmp_path) 1639 assert failures == [] 1640 assert ready.is_dir() 1641 1642 1643def test_build_and_promote_candidate_quarantines_ready_when_final_validator_raises( 1644 tmp_path: Path, monkeypatch: pytest.MonkeyPatch 1645) -> None: 1646 ready = tmp_path / "ready" 1647 1648 def fail_final_validator( 1649 _release_dir: Path, 1650 *, 1651 expected_source_commit: str | None, 1652 schema_path: Path = checker.SCHEMA_PATH, 1653 ) -> list[checker.Failure]: 1654 raise RuntimeError("validator boom") 1655 1656 monkeypatch.setattr(checker, "_final_validate_release_dir", fail_final_validator) 1657 with pytest.raises(RuntimeError, match="validator boom"): 1658 _build_ready(tmp_path) 1659 1660 assert not ready.exists() 1661 assert not (tmp_path / "ready.staging").exists() 1662 assert not (tmp_path / "ready.quarantine").exists() 1663 1664 monkeypatch.undo() 1665 ready, failures = _build_ready(tmp_path) 1666 assert failures == [] 1667 assert ready.is_dir() 1668 1669 1670def test_build_and_promote_candidate_leaves_quarantine_not_ready_when_quarantine_delete_fails( 1671 tmp_path: Path, monkeypatch: pytest.MonkeyPatch 1672) -> None: 1673 ready = tmp_path / "ready" 1674 quarantine = tmp_path / "ready.quarantine" 1675 hook_error = RuntimeError("hook boom") 1676 real_rmtree = checker.shutil.rmtree 1677 1678 def fail_after_promote(_path: Path) -> None: 1679 raise hook_error 1680 1681 def rmtree(path: Path, *args: Any, **kwargs: Any) -> None: 1682 if Path(path) == quarantine: 1683 raise OSError("delete failed") 1684 real_rmtree(path, *args, **kwargs) 1685 1686 monkeypatch.setattr(checker.shutil, "rmtree", rmtree) 1687 with pytest.raises( 1688 RuntimeError, match="release candidate quarantine could not be removed" 1689 ) as exc_info: 1690 _build_ready(tmp_path, hook=fail_after_promote) 1691 1692 assert exc_info.value.__cause__ is hook_error 1693 assert not ready.exists() 1694 assert not (tmp_path / "ready.staging").exists() 1695 assert quarantine.is_dir() 1696 assert checker.validate_release_dir(ready, expected_source_commit=VALID_COMMIT) 1697 new_ready = tmp_path / "new-ready" 1698 failures = checker.build_and_promote_candidate( 1699 _source_dist(tmp_path / "retry"), 1700 new_ready, 1701 source_commit=VALID_COMMIT, 1702 evidence_by_lane=checker.fixture_evidence_by_lane(), 1703 include_models=False, 1704 ) 1705 assert failures == [] 1706 assert new_ready.is_dir() 1707 1708 1709def test_fixtures_mode_runs_without_tree_artifacts() -> None: 1710 before = {path.name for path in checker.ROOT.iterdir()} 1711 1712 failures = checker.run_fixtures_mode() 1713 1714 after = {path.name for path in checker.ROOT.iterdir()} 1715 assert failures == [] 1716 assert after == before