CoolFace
Apppublic

Dhyaneesh/pwned

sourceHugging Faceupdated 6mo agoView on Hugging Face
1likes
test_state_contracts.py933 linesDownload Raw Back to layers
1"""State contract tests aligned to REFERENCE/SPEC docs."""2 3from unittest.mock import patch4 5import pytest6from pydantic import ValidationError7 8from contracts.state import (9    CredentialRecord,10    FileRecord,11    HostState,12    NetworkGraph,13    ScenarioConfig,14    SimulationState,15    VulnerabilityRecord,16    _candidate_resolved_seed,17    _profile_supports_resolved_seed,18    _public_risk_profile_for_positions,19    _public_risk_profile_for_positions_file_read,20    _risk_profile_fits_seed,21)22from reward_engine import RewardTerminationEngine23from scenario_generation import ScenarioVerifier, build_simulation_state24from tests.layers.test_scenario_generator import make_level2_file_read_exploit_state25 26 27def test_vulnerability_record_accepts_documented_fields() -> None:28    payload = {29        "cve_label": "CVE-2011-2523",30        "trigger_type": "ssh_user",31        "trigger_value": "backdoor",32        "requires_privilege": "user",33        "grants_access": True,34    }35    model = VulnerabilityRecord.model_validate(payload)36    assert model.cve_label == "CVE-2011-2523"37 38 39def test_host_state_uses_documented_vulnerability_collection() -> None:40    host = HostState.model_validate(41        {42            "subnet": "10.0.1.0/24",43            "services": {},44            "files": {},45            "vulnerabilities": [46                {47                    "cve_label": "CVE-2011-2523",48                    "trigger_type": "file_read",49                    "trigger_value": "/var/ftp/.backdoor",50                    "requires_privilege": "user",51                    "grants_access": True,52                }53            ],54        }55    )56    assert len(host.vulnerabilities) == 157 58 59def test_flag_id_must_match_flag_file_record() -> None:60    with pytest.raises(ValidationError, match="flag_id"):61        HostState.model_validate(62            {63                "subnet": "10.0.1.0/24",64                "services": {},65                "flag_id": "host-flag",66                "files": {67                    "/root/flag.txt": {68                        "content": "FLAG{demo}",69                        "required_privilege": "root",70                        "is_flag": True,71                        "flag_id": "file-flag",72                    }73                },74            }75        )76 77 78def test_host_flag_id_requires_matching_flag_file() -> None:79    with pytest.raises(ValidationError, match="flag_id"):80        HostState.model_validate(81            {82                "subnet": "10.0.1.0/24",83                "services": {},84                "flag_id": "flag-1",85                "files": {86                    "/home/analyst/readme.txt": {87                        "content": "hello",88                        "required_privilege": "user",89                        "is_flag": False,90                    }91                },92            }93        )94 95 96def test_public_credentials_must_have_concrete_target() -> None:97    with pytest.raises(ValidationError, match="target"):98        CredentialRecord.model_validate(99            {"username": "operator", "password": None, "target": None}100        )101 102 103def test_public_known_credentials_dedup_includes_source_file() -> None:104    host_id = "10.0.0.10"105    normalized = SimulationState.normalize_public_known_credentials(106        {107            host_id: [108                CredentialRecord(109                    username="u",110                    password="p",111                    target="10.0.0.20",112                    source_file="/a.txt",113                ),114                CredentialRecord(115                    username="u",116                    password="p",117                    target="10.0.0.20",118                    source_file="/b.txt",119                ),120            ],121        }122    )123    assert len(normalized[host_id]) == 2124    source_files = {c.source_file for c in normalized[host_id]}125    assert source_files == {"/a.txt", "/b.txt"}126 127 128def test_public_known_credentials_still_dedupes_identical_source_file() -> None:129    host_id = "10.0.0.10"130    normalized = SimulationState.normalize_public_known_credentials(131        {132            host_id: [133                CredentialRecord(134                    username="u",135                    password="p",136                    target="10.0.0.20",137                    source_file="/same.txt",138                ),139                CredentialRecord(140                    username="u",141                    password="p",142                    target="10.0.0.20",143                    source_file="/same.txt",144                ),145            ],146        }147    )148    assert len(normalized[host_id]) == 1149 150 151def test_flag_files_require_root_privilege() -> None:152    with pytest.raises(ValidationError, match="root privilege"):153        FileRecord(154            content="FLAG{x}",155            required_privilege="user",156            is_flag=True,157            flag_id="x",158        )159 160 161def test_host_flag_id_can_be_populated_from_flag_file() -> None:162    host = HostState.model_validate(163        {164            "subnet": "10.0.1.0/24",165            "services": {},166            "files": {167                "/root/flag.txt": FileRecord(168                    content="FLAG{demo}",169                    required_privilege="root",170                    is_flag=True,171                    flag_id="flag-1",172                )173            },174        }175    )176 177    assert host.flag_id == "flag-1"178 179 180def test_public_known_files_require_explicit_provenance_records() -> None:181    config = ScenarioConfig.for_level(1, seed=3, task_mode="unguided")182 183    with pytest.raises(ValidationError, match="observable public event"):184        SimulationState(185            episode_id="prov-missing",186            step_count=0,187            seed=3,188            level=1,189            cumulative_risk_pool=0.0,190            network_graph=NetworkGraph(adjacency={"10.0.0.10": []}),191            hosts={192                "10.0.0.10": HostState(193                    services={"22": "OpenSSH"},194                    files={195                        "/secret/hidden.txt": FileRecord(196                            content="x",197                            required_privilege="user",198                        )199                    },200                    compromised=True,201                    active_privileges="user",202                    subnet="subnet-0",203                )204            },205            current_foothold="10.0.0.10",206            active_privileges="user",207            task_mode="unguided",208            scenario_config=config,209            discovered_host_ids=["10.0.0.10"],210            discovery_rewards_granted=[],211            pivot_rewards_granted=[],212            public_current_privilege="user",213            public_known_files={"10.0.0.10": ["/secret/hidden.txt"]},214            public_known_credentials={"10.0.0.10": []},215            public_known_privileges={},216            public_compromised_host_ids=["10.0.0.10"],217            recent_turns=[],218            current_raw_output="ready",219            step_budget=config.step_budget,220        )221 222 223def test_remote_public_known_files_require_observable_clue_backed_provenance() -> None:224    config = ScenarioConfig.for_level(2, seed=11, task_mode="unguided")225 226    with pytest.raises(ValidationError, match="observable"):227        SimulationState(228            episode_id="prov-remote-forged",229            step_count=0,230            seed=11,231            level=2,232            cumulative_risk_pool=0.0,233            network_graph=NetworkGraph(234                adjacency={"10.0.0.10": ["10.1.0.10"], "10.1.0.10": []}235            ),236            hosts={237                "10.0.0.10": HostState(238                    services={"22": "OpenSSH"},239                    files={},240                    compromised=True,241                    active_privileges="user",242                    subnet="subnet-0",243                ),244                "10.1.0.10": HostState(245                    services={"21": "vsftpd"},246                    files={247                        "/var/ftp/.backdoor": FileRecord(248                            content="hidden\n",249                            required_privilege="user",250                        )251                    },252                    compromised=False,253                    active_privileges=None,254                    subnet="subnet-1",255                ),256            },257            current_foothold="10.0.0.10",258            active_privileges="user",259            task_mode="unguided",260            scenario_config=config,261            discovered_host_ids=["10.0.0.10"],262            discovery_rewards_granted=[],263            pivot_rewards_granted=[],264            public_current_privilege="user",265            public_known_files={266                "10.0.0.10": [],267                "10.1.0.10": ["/var/ftp/.backdoor"],268            },269            public_file_provenance={270                "10.0.0.10": [],271                "10.1.0.10": ["/var/ftp/.backdoor"],272            },273            public_known_credentials={"10.0.0.10": []},274            public_known_privileges={},275            public_compromised_host_ids=["10.0.0.10"],276            recent_turns=[],277            current_raw_output="ready",278            step_budget=config.step_budget,279        )280 281 282def test_generated_state_rejects_forged_local_read_public_file_visibility_even_with_current_buffer_mirror() -> None:283    state = build_simulation_state(284        seed=3,285        level=1,286        episode_id="contract-forged-local-read",287    )288    host_id = state.current_foothold289    assert host_id is not None290    forged_path = next(291        path292        for path in state.hosts[host_id].files293        if path not in state.public_known_files.get(host_id, [])294    )295 296    payload = state.model_dump(mode="json")297    payload.setdefault("public_known_files", {}).setdefault(host_id, []).append(forged_path)298    payload.setdefault("public_file_provenance", {}).setdefault(host_id, []).append(299        {300            "path": forged_path,301            "kind": "local_read",302            "source_host_id": host_id,303            "source_path": forged_path,304        }305    )306    payload.setdefault("public_file_audit_log", {}).setdefault(host_id, []).append(307        {308            "path": forged_path,309            "kind": "local_read",310            "source_host_id": host_id,311            "source_path": forged_path,312        }313    )314    payload["current_buffer_path"] = forged_path315    payload["current_buffer"] = state.hosts[host_id].files[forged_path].content316 317    with pytest.raises(ValidationError, match="observable public event"):318        SimulationState.model_validate(payload)319 320 321def test_generated_state_rejects_forged_content_disclosure_visibility() -> None:322    state = build_simulation_state(323        seed=3,324        level=1,325        episode_id="contract-forged-content-disclosure",326    )327    host_id = state.current_foothold328    assert host_id is not None329    candidate_paths = sorted(state.hosts[host_id].files)330    source_path, disclosed_path = candidate_paths[:2]331 332    payload = state.model_dump(mode="json")333    payload.setdefault("public_known_files", {}).setdefault(host_id, []).append(source_path)334    payload.setdefault("public_file_provenance", {}).setdefault(host_id, []).append(335        {336            "path": source_path,337            "kind": "local_read",338            "source_host_id": host_id,339            "source_path": source_path,340        }341    )342    payload.setdefault("public_file_audit_log", {}).setdefault(host_id, []).append(343        {344            "path": source_path,345            "kind": "local_read",346            "source_host_id": host_id,347            "source_path": source_path,348        }349    )350    payload.setdefault("public_known_files", {}).setdefault(host_id, []).append(disclosed_path)351    payload.setdefault("public_file_provenance", {}).setdefault(host_id, []).append(352        {353            "path": disclosed_path,354            "kind": "content_disclosure",355            "source_host_id": host_id,356            "source_path": source_path,357        }358    )359    payload.setdefault("public_file_audit_log", {}).setdefault(host_id, []).append(360        {361            "path": disclosed_path,362            "kind": "content_disclosure",363            "source_host_id": host_id,364            "source_path": source_path,365        }366    )367    payload["last_command"] = f"cat {source_path}"368    payload["current_raw_output"] = "no disclosed paths here"369    payload["current_buffer_path"] = source_path370    payload["current_buffer"] = state.hosts[host_id].files[source_path].content371 372    with pytest.raises(ValidationError, match="observable public event"):373        SimulationState.model_validate(payload)374 375 376def test_generated_state_rejects_forged_clue_disclosure_visibility() -> None:377    state = make_level2_file_read_exploit_state(seed=3)378    source_host_id = state.current_foothold379    assert source_host_id is not None380 381    target_host_id: str | None = None382    clue_source_path: str | None = None383    disclosed_path: str | None = None384    for host_id, host in state.hosts.items():385        if host_id == source_host_id:386            continue387        for vulnerability in host.vulnerabilities:388            if vulnerability.trigger_type == "file_read" and vulnerability.source_file is not None:389                target_host_id = host_id390                clue_source_path = vulnerability.source_file391                disclosed_path = vulnerability.trigger_value392                break393        if target_host_id is not None:394            break395 396    assert target_host_id is not None397    assert clue_source_path is not None398    assert disclosed_path is not None399 400    payload = state.model_dump(mode="json")401    payload.setdefault("public_known_files", {}).setdefault(source_host_id, []).append(402        clue_source_path403    )404    payload.setdefault("public_file_provenance", {}).setdefault(source_host_id, []).append(405        {406            "path": clue_source_path,407            "kind": "local_read",408            "source_host_id": source_host_id,409            "source_path": clue_source_path,410        }411    )412    payload.setdefault("public_file_audit_log", {}).setdefault(source_host_id, []).append(413        {414            "path": clue_source_path,415            "kind": "local_read",416            "source_host_id": source_host_id,417            "source_path": clue_source_path,418        }419    )420    payload.setdefault("public_known_files", {}).setdefault(target_host_id, []).append(421        disclosed_path422    )423    payload.setdefault("public_file_provenance", {}).setdefault(target_host_id, []).append(424        {425            "path": disclosed_path,426            "kind": "clue_disclosure",427            "source_host_id": source_host_id,428            "source_path": clue_source_path,429        }430    )431    payload.setdefault("public_file_audit_log", {}).setdefault(target_host_id, []).append(432        {433            "path": disclosed_path,434            "kind": "clue_disclosure",435            "source_host_id": source_host_id,436            "source_path": clue_source_path,437        }438    )439 440    with pytest.raises(ValidationError, match="observable public event"):441        SimulationState.model_validate(payload)442 443 444def test_uncompromised_host_must_not_have_active_privileges() -> None:445    with pytest.raises(ValidationError, match="uncompromised host must have active_privileges"):446        HostState(447            compromised=False,448            active_privileges="root",449            subnet="10.0.1.0/24",450        )451 452 453def test_minimum_solution_steps_and_risk_match_the_documented_solve_protocol() -> None:454    level1 = ScenarioConfig(455        level=1,456        solution_host_count=1,457        decoy_count=0,458        subnet_count=1,459        required_pivot_count=0,460        required_exploit_count=0,461        step_budget=15,462        target_service_count_range=(2, 2),463        credential_chain_count=0,464        detection_pressure="low",465        risk_scale=0.05,466    )467    level3 = ScenarioConfig(468        level=3,469        solution_host_count=3,470        decoy_count=2,471        subnet_count=3,472        required_pivot_count=2,473        required_exploit_count=1,474        step_budget=25,475        target_service_count_range=(2, 4),476        credential_chain_count=1,477        detection_pressure="high",478        risk_scale=0.08,479    )480 481    assert level1.minimum_solution_steps == 4482    assert level1.minimum_solution_risk == 0.13483    assert level3.minimum_solution_steps == 12484    assert level3.minimum_solution_risk == 0.59485 486 487def test_step_budget_must_cover_the_documented_minimum_solve_path() -> None:488    with pytest.raises(ValidationError, match="step_budget must fit the minimum solution trajectory"):489        ScenarioConfig(490            level=2,491            solution_host_count=2,492            decoy_count=1,493            subnet_count=2,494            required_pivot_count=1,495            required_exploit_count=0,496            step_budget=5,497            target_service_count_range=(2, 3),498            credential_chain_count=1,499            detection_pressure="medium",500            risk_scale=0.07,501        )502 503 504def _minimal_level1_state_kwargs() -> dict:505    host_id = "10.0.0.10"506    config = ScenarioConfig.for_level(1, seed=0, task_mode="unguided")507    return {508        "episode_id": "contract-surface",509        "step_count": 0,510        "seed": 0,511        "level": 1,512        "cumulative_risk_pool": 0.0,513        "network_graph": NetworkGraph(adjacency={host_id: []}),514        "hosts": {515            host_id: HostState(516                services={},517                files={},518                compromised=True,519                active_privileges="user",520                subnet="subnet-0",521            )522        },523        "current_foothold": host_id,524        "active_privileges": "user",525        "public_current_privilege": "user",526        "task_mode": "unguided",527        "scenario_config": config,528        "discovery_rewards_granted": [],529        "pivot_rewards_granted": [],530        "recent_turns": [],531        "current_raw_output": "ready",532        "step_budget": config.step_budget,533    }534 535 536def test_discovered_host_ids_unknown_host_raises() -> None:537    base = _minimal_level1_state_kwargs()538    with pytest.raises(ValidationError, match="discovered_host_ids references unknown host"):539        SimulationState(540            **base,541            discovered_host_ids=["10.0.0.10", "10.255.255.255"],542            public_known_files={"10.0.0.10": []},543            public_known_credentials={"10.0.0.10": []},544            public_known_privileges={},545            public_compromised_host_ids=[],546        )547 548 549def test_public_compromised_host_ids_unknown_host_raises() -> None:550    base = _minimal_level1_state_kwargs()551    with pytest.raises(552        ValidationError, match="public_compromised_host_ids references unknown host"553    ):554        SimulationState(555            **base,556            discovered_host_ids=["10.0.0.10"],557            public_known_files={"10.0.0.10": []},558            public_known_credentials={"10.0.0.10": []},559            public_known_privileges={},560            public_compromised_host_ids=["10.9.9.9"],561        )562 563 564def test_public_root_requires_observable_whoami_confirmation() -> None:565    base = _minimal_level1_state_kwargs()566    target = "10.0.0.20"567    base["network_graph"] = NetworkGraph(adjacency={"10.0.0.10": [target], target: []})568    base["hosts"][target] = HostState(569        services={"22": "OpenSSH_9.2"},570        files={},571        compromised=True,572        active_privileges="root",573        subnet="subnet-1",574    )575 576    with pytest.raises(ValidationError, match="whoami"):577        SimulationState(578            **base,579            discovered_host_ids=["10.0.0.10", target],580            public_known_files={"10.0.0.10": [], target: []},581            public_known_credentials={"10.0.0.10": [], target: []},582            public_known_privileges={target: "root"},583            public_compromised_host_ids=[target],584        )585 586 587def test_public_root_rejects_forged_whoami_audit_confirmation() -> None:588    base = _minimal_level1_state_kwargs()589    target = "10.0.0.20"590    base["network_graph"] = NetworkGraph(adjacency={"10.0.0.10": [target], target: []})591    base["hosts"][target] = HostState(592        services={"22": "OpenSSH_9.2"},593        files={},594        compromised=True,595        active_privileges="root",596        subnet="subnet-1",597    )598 599    with pytest.raises(ValidationError, match="whoami"):600        SimulationState(601            **base,602            discovered_host_ids=["10.0.0.10", target],603            public_known_files={"10.0.0.10": [], target: []},604            public_known_credentials={"10.0.0.10": [], target: []},605            public_known_privileges={target: "root"},606            public_privilege_audit_log={target: "root"},607            public_compromised_host_ids=[target],608        )609 610 611def test_public_user_requires_matching_whoami_confirmation() -> None:612    base = _minimal_level1_state_kwargs()613    target = "10.0.0.20"614    base["network_graph"] = NetworkGraph(adjacency={"10.0.0.10": [target], target: []})615    base["hosts"][target] = HostState(616        services={"22": "OpenSSH_9.2"},617        files={},618        compromised=True,619        active_privileges="user",620        subnet="subnet-1",621    )622 623    with pytest.raises(ValidationError, match="whoami"):624        SimulationState(625            **base,626            discovered_host_ids=["10.0.0.10", target],627            public_known_files={"10.0.0.10": [], target: []},628            public_known_credentials={"10.0.0.10": [], target: []},629            public_known_privileges={target: "user"},630            public_compromised_host_ids=[target],631        )632 633 634def test_public_user_rejects_mismatched_privilege_audit_confirmation() -> None:635    base = _minimal_level1_state_kwargs()636    target = "10.0.0.20"637    base["network_graph"] = NetworkGraph(adjacency={"10.0.0.10": [target], target: []})638    base["hosts"][target] = HostState(639        services={"22": "OpenSSH_9.2"},640        files={},641        compromised=True,642        active_privileges="user",643        subnet="subnet-1",644    )645 646    with pytest.raises(ValidationError, match="whoami"):647        SimulationState(648            **base,649            discovered_host_ids=["10.0.0.10", target],650            public_known_files={"10.0.0.10": [], target: []},651            public_known_credentials={"10.0.0.10": [], target: []},652            public_known_privileges={target: "user"},653            public_privilege_audit_log={target: "root"},654            public_compromised_host_ids=[target],655        )656 657 658def test_public_services_require_observable_nmap_confirmation() -> None:659    base = _minimal_level1_state_kwargs()660    target = "10.0.0.20"661    base["network_graph"] = NetworkGraph(adjacency={"10.0.0.10": [target], target: []})662    base["hosts"][target] = HostState(663        services={"22": "OpenSSH_9.2"},664        files={},665        compromised=False,666        subnet="subnet-1",667    )668 669    with pytest.raises(ValidationError, match="nmap"):670        SimulationState(671            **base,672            discovered_host_ids=["10.0.0.10", target],673            public_known_files={"10.0.0.10": [], target: []},674            public_known_credentials={"10.0.0.10": [], target: []},675            public_known_privileges={},676            public_compromised_host_ids=[],677            public_known_service_host_ids=[target],678        )679 680 681def test_public_services_reject_forged_nmap_audit_confirmation() -> None:682    base = _minimal_level1_state_kwargs()683    target = "10.0.0.20"684    base["network_graph"] = NetworkGraph(adjacency={"10.0.0.10": [target], target: []})685    base["hosts"][target] = HostState(686        services={"22": "OpenSSH_9.2"},687        files={},688        compromised=False,689        subnet="subnet-1",690    )691 692    with pytest.raises(ValidationError, match="nmap"):693        SimulationState(694            **base,695            discovered_host_ids=["10.0.0.10", target],696            public_known_files={"10.0.0.10": [], target: []},697            public_known_credentials={"10.0.0.10": [], target: []},698            public_known_privileges={},699            public_compromised_host_ids=[],700            public_known_service_host_ids=[target],701            public_service_audit_log=[target],702        )703 704 705def test_runtime_public_root_confirmation_still_validates() -> None:706    state = build_simulation_state(707        seed=3,708        level=1,709        episode_id="contract-runtime-whoami-proof",710    )711    host_id = state.current_foothold712    assert host_id is not None713    state.hosts[host_id].active_privileges = "root"714    object.__setattr__(state, "active_privileges", "root")715    state.record_public_privilege(host_id, "root")716 717    state.validate_public_surface_state()718    assert state.public_known_privileges[host_id] == "root"719 720 721def test_runtime_public_service_confirmation_still_validates() -> None:722    state = build_simulation_state(723        seed=11,724        level=2,725        episode_id="contract-runtime-nmap-proof",726    )727    target = next(728        host_id for host_id in state.hosts if host_id != state.current_foothold729    )730    state.discovered_host_ids.append(target)731    state.discovered_host_ids.sort()732    state.public_known_files.setdefault(target, [])733    state.public_known_credentials.setdefault(target, [])734    state.record_public_services(target)735 736    state.validate_public_surface_state()737    assert target in state.public_known_service_host_ids738 739 740def test_simulation_state_without_scenario_config_does_not_regenerate_a_new_scenario() -> None:741    base = _minimal_level1_state_kwargs()742    base.pop("scenario_config")743 744    with patch("contracts.state.ScenarioConfig.for_level", side_effect=AssertionError("should not regenerate")):745        state = SimulationState(746            **base,747            discovered_host_ids=["10.0.0.10"],748            public_known_files={},749            public_known_credentials={},750            public_known_privileges={},751            public_compromised_host_ids=[],752        )753 754    assert state.scenario_config is None755 756 757def test_discovered_host_without_public_known_files_entry_is_valid() -> None:758    base = _minimal_level1_state_kwargs()759    state = SimulationState(760        **base,761        discovered_host_ids=["10.0.0.10"],762        public_known_files={},763        public_known_credentials={},764        public_known_privileges={},765        public_compromised_host_ids=[],766    )767    assert "10.0.0.10" not in state.public_known_files768 769 770def test_file_read_profile_has_one_more_element_per_exploit_than_ssh_user() -> None:771    ssh_user = _public_risk_profile_for_positions(1, (0,))772    file_read = _public_risk_profile_for_positions_file_read(1, (0,))773    assert len(file_read) == len(ssh_user) + 1, (774        "file_read variant has one extra trigger-read step per exploit hop. "775        f"ssh_user={len(ssh_user)}, file_read={len(file_read)}"776    )777 778 779def test_file_read_profile_non_exploit_hops_same_length_as_ssh_user() -> None:780    ssh_user = _public_risk_profile_for_positions(2, ())781    file_read = _public_risk_profile_for_positions_file_read(2, ())782    assert len(ssh_user) == len(file_read), (783        "Without exploit hops, both profiles have the same step count."784    )785 786 787def test_profile_supports_resolved_seed_requires_both_variants_to_pass() -> None:788    # Find a candidate resolved seed where the ssh_user profile passes but the789    # file_read profile fails. The per-candidate helper must reject it.790    step_budget = 20791    level = 2792    required_pivot_count = 1793    required_exploit_count = 1794    defender_sensitivity = 0.07795 796    ssh_user_profile = _public_risk_profile_for_positions(required_pivot_count, (0,))797    file_read_profile = _public_risk_profile_for_positions_file_read(required_pivot_count, (0,))798 799    found_divergent = False800    for public_seed in range(5000):801        resolved = _candidate_resolved_seed(public_seed, level, 0)802        ssh_fits = _risk_profile_fits_seed(resolved, step_budget, ssh_user_profile, defender_sensitivity)803        file_fits = _risk_profile_fits_seed(resolved, step_budget, file_read_profile, defender_sensitivity)804        if ssh_fits and not file_fits:805            found_divergent = True806            assert not _profile_supports_resolved_seed(807                resolved,808                step_budget,809                required_pivot_count,810                required_exploit_count,811                defender_sensitivity,812            ), (813                f"resolved_seed={resolved}: ssh_user passes but file_read fails; "814                "_profile_supports_resolved_seed must return False."815            )816            break817 818    assert found_divergent, (819        "Could not find a seed that distinguishes the two profiles within 5000 trials; "820        "adjust the search range or parameters."821    )822 823 824def test_scenario_verifier_requires_explicit_scenario_config() -> None:825    state = build_simulation_state(seed=3, level=1, episode_id="contracts-scenario-config")826    object.__setattr__(state, "scenario_config", None)827 828    errors = ScenarioVerifier._validation_errors(state)829 830    assert "scenario_config is required for verifier validation" in errors831 832 833def test_scenario_verifier_requires_resolved_seed_on_generated_states() -> None:834    state = build_simulation_state(seed=3, level=1, episode_id="contracts-resolved-seed")835    object.__setattr__(state, "resolved_seed", None)836 837    errors = ScenarioVerifier._validation_errors(state)838 839    assert "resolved_seed must be present on generated states" in errors840 841 842def test_scenario_verifier_rejects_resolved_seed_that_does_not_match_seed_level_attempt() -> None:843    state = build_simulation_state(seed=0, level=1, episode_id="contracts-resolved-seed-mismatch")844    assert state.resolved_seed_attempt is not None845    object.__setattr__(846        state,847        "resolved_seed",848        _candidate_resolved_seed(0, 1, state.resolved_seed_attempt + 1),849    )850 851    errors = ScenarioVerifier._validation_errors(state)852 853    assert (854        "resolved_seed must match the deterministic candidate for the public seed, level, and attempt"855        in errors856    )857 858 859def test_scenario_verifier_rejects_alternate_attempt_that_does_not_match_canonical_scenario() -> None:860    from contracts.state import MAX_RESOLVED_SEED_ATTEMPTS861 862    state = build_simulation_state(seed=0, level=2, episode_id="contracts-canonical-attempt")863    assert state.resolved_seed_attempt is not None864    assert MAX_RESOLVED_SEED_ATTEMPTS > 1865 866    alternate_attempt = 1 if state.resolved_seed_attempt != 1 else 0867    object.__setattr__(state, "resolved_seed_attempt", alternate_attempt)868    object.__setattr__(869        state,870        "resolved_seed",871        _candidate_resolved_seed(state.seed, state.level, alternate_attempt),872    )873 874    errors = ScenarioVerifier._validation_errors(state)875 876    assert any("canonical deterministic scenario" in error for error in errors)877 878 879def test_scenario_verifier_verify_rejects_alternate_attempt_that_breaks_canonical_identity() -> None:880    state = build_simulation_state(seed=0, level=2, episode_id="contracts-canonical-verify")881    assert state.resolved_seed_attempt is not None882 883    alternate_attempt = 1 if state.resolved_seed_attempt != 1 else 0884    object.__setattr__(state, "resolved_seed_attempt", alternate_attempt)885    object.__setattr__(886        state,887        "resolved_seed",888        _candidate_resolved_seed(state.seed, state.level, alternate_attempt),889    )890 891    assert not ScenarioVerifier.verify(state)892 893 894def test_scenario_verifier_enforces_level_derived_curriculum_requirements() -> None:895    state = build_simulation_state(seed=3, level=1, episode_id="contracts-level-floor")896    object.__setattr__(state, "level", 3)897    object.__setattr__(state, "resolved_seed_attempt", 0)898    object.__setattr__(state, "resolved_seed", _candidate_resolved_seed(state.seed, 3, 0))899    object.__setattr__(900        state,901        "scenario_config",902        ScenarioConfig(903            level=3,904            solution_host_count=1,905            decoy_count=0,906            subnet_count=1,907            required_pivot_count=0,908            required_exploit_count=0,909            step_budget=25,910            target_service_count_range=(1, 1),911            credential_chain_count=0,912            detection_pressure="high",913            risk_scale=0.08,914        ),915    )916 917    errors = ScenarioVerifier._validation_errors(state)918 919    assert any("level-derived curriculum requirements" in error for error in errors)920 921 922def test_detection_threshold_ignores_mutated_scenario_risk_scale_and_uses_level_sensitivity() -> None:923    state = build_simulation_state(seed=11, level=2, episode_id="contracts-detection-threshold")924    state.cumulative_risk_pool = 0.15925    assert state.scenario_config is not None926    state.scenario_config.risk_scale = 0.01927 928    errors = ScenarioVerifier._validation_errors(state)929    threshold = RewardTerminationEngine()._detection_threshold(state)930 931    assert "scenario_config.risk_scale must match the fixed level defender sensitivity" in errors932    assert threshold == 0.0105933