diff --git a/docs/decisions/0169-phase7q-scaffold-model-pair-recovery.md b/docs/decisions/0169-phase7q-scaffold-model-pair-recovery.md new file mode 100644 index 00000000..c3153211 --- /dev/null +++ b/docs/decisions/0169-phase7q-scaffold-model-pair-recovery.md @@ -0,0 +1,964 @@ +# 0169 — Phase 7Q scaffold/model pair qualification recovery + +Date: 2026-09-30 + +## Status + +IN_PROGRESS / L1_ARTIFACT_IDENTITY_PREFLIGHT_NEXT / NO_EXPERIMENTAL_CELL_AUTHORIZED + +## Context + +Phase 7 completed with zero compatible scaffold survivors. Phase 7R then performed a bounded requalification of S4 OpenHands against the frozen L3 Granite 4.2 3B Phase 5 fixture and also closed with no compatible survivor. + +The terminal S4-R10 evidence removed the earlier runtime, transport, timeout, and harness ambiguities. The remaining failure was persistent Windows tool-protocol noncompliance under the exact S4 + L3 pair: + +- POSIX-shaped terminal commands were still emitted despite a Windows-native PowerShell contract and embedded Windows system prompt; +- slash-rooted file-editor paths were still attempted before recovery; +- the correct source edit was reached; +- post-edit verification attempts were again POSIX-shaped and failed; +- max_iterations=10 was observed and exhausted; +- the independent verifier was therefore not reached; +- S4 remained INCOMPATIBLE. + +Phase 8 remains blocked because it requires at least one compatible scaffold execution path before a scaffold screen is meaningful. + +Phase 4 independently established runtime compatibility for the frozen compact-model pool: + +```text +L1 Nanbeige4.2-3B COMPATIBLE +L2 Qwen3.5-4B COMPATIBLE +L3 Granite 4.2 3B COMPATIBLE +L4 SWE-Dev-7B COMPATIBLE (stretch) +``` + +Those Phase 4 classifications are runtime/model-format compatibility only. They are not task-solving selection, ranking, or promotion. + +## Decision + +Open a new decision stream, Phase 7Q, to test whether compatibility is a property of the exact scaffold/model pair rather than scaffold identity alone. + +This is not a continuation of Phase 7R and does not rewrite its result. + +The first candidate model is L1 Nanbeige4.2-3B solely because L1 is first in the already frozen Phase 4 candidate order. This ordering is operational and must not be interpreted as a model ranking. + +Before any experimental cell is authorized, recover the exact local L1 artifact identity from retained local artifacts/evidence. + +Run only the non-experimental preflight: + +```powershell +uv run --python 3.12 --no-project python .\tools\run_phase7q_model_artifact_preflight.py +``` + +The preflight: + +- performs no model invocation; +- starts no llama.cpp server; +- starts no scaffold; +- changes no repository files; +- enumerates `D:\projetos\codepro-mini-runtime\models\phase4\**\*.gguf`; +- records path, byte size, and SHA256 for every GGUF; +- searches retained local Phase 4 evidence for references to the frozen model names. + +## Gate + +No Phase 7Q experimental cell is authorized until the verified mainline-compatible L1 artifact is unambiguously identified. + +Required identity evidence: + +```text +L1_ARTIFACT_PATH = known +L1_ARTIFACT_BYTES = known +L1_ARTIFACT_SHA256 = known +PHASE4_COMPATIBLE_ARTIFACT_LINKAGE = established +``` + +If multiple L1-looking artifacts exist and the retained evidence does not disambiguate them, stop at `IDENTITY_AMBIGUOUS`. Do not choose by filename, recency, file size, throughput, or convenience. + +## Prospective first experimental cell + +Only after the identity gate passes may a separately recorded Phase 7Q cell be authorized with a single treatment: + +```text +BASE = S4-R10 +SOLE TREATMENT = MODEL L3 Granite 4.2 3B -> L1 Nanbeige4.2-3B +``` + +Everything else would remain frozen from S4-R10 unless separately documented: + +```text +SCAFFOLD = OpenHands Agent Canvas v1.21.0 +OPENHANDS_COMMIT = fc6d890f7b21c71a17de60d50597c00355e235ea +AGENT_SERVER = 1.49.3 +AUTOMATION = 1.13.3 +LLAMA_CPP_BUILD = 11205 +LLAMA_CPP_COMMIT = 95887577ab5fead779581a7030a83c7752ff3234 +PLATFORM = WINDOWS_NATIVE +SYSTEM_PROMPT_PROFILE = windows-embedded-v1 +PLATFORM_CONTRACT = windows-powershell-v2 +CONVERSATION_WORKTREE = false +CTX_SIZE = 16384 +PARALLEL = 1 +LITELLM = 1.94.3 +TIME_BUDGETS = 600 / 660 / 720 +MAX_ITERATIONS = 10 +FALLBACK = DISABLED +TASK = unchanged +TOOLSET = unchanged +TOOL_METADATA = unchanged +BYTECODE_CLEANUP = untracked-python-bytecode-only-v1 +``` + +A model swap would not by itself authorize Phase 8. Compatibility would still require the existing exact patch, independent verifier, source cleanliness, and workspace-cleanup gates. + +## Invariants + +```text +COMPATIBLE != SELECTED +SELECTED != PROMOTED +AVAILABLE != QUALIFIED +EXECUTION != VERIFICATION +VERIFICATION != ACCEPTANCE +ACCEPTED != PROMOTED + +NO_SILENT_FALLBACK +NO_SILENT_EXECUTOR_SWITCH +NO_SILENT_SCOPE_EXPANSION +NO_MODEL_RANKING_FROM_PHASE4_COMPATIBILITY +``` + + +## L1 artifact identity result + +The non-experimental Phase 7Q preflight completed without starting llama.cpp, OpenHands, or any model invocation. + +Two local Nanbeige GGUF artifacts were present: + +```text +Nanbeige4.2-3B-Q4_K_M-mainline-e31e82d.gguf +bytes = 2574807872 +sha256 = FBCE43B8977DD73A86126E85AE0BFE219EF3131498AE7C37571DEA58F3C6AA34 + +Nanbeige4.2-3B-Q4_K_M.gguf +bytes = 2574807840 +sha256 = 99C7BFB88907F7EEE0A04C4314F1C46BCA391819478D8CB90B3E164F09576489 +``` + +The retained Phase 4 evidence unambiguously links the compatible L1 qualification to the mainline artifact: + +```text +artifact_repository = iamimmanuelraj/Nanbeige4.2-3B-GGUF +artifact = Nanbeige4.2-3B-Q4_K_M-mainline-e31e82d.gguf +``` + +The same artifact path appears in the retained context-4096, CPU, GPU, and tool-constrained stdout evidence. + +Therefore the identity gate is satisfied: + +```text +L1_ARTIFACT_PATH = +D:\projetos\codepro-mini-runtime\models\phase4\L1-Nanbeige4.2-3B\Nanbeige4.2-3B-Q4_K_M-mainline-e31e82d.gguf + +L1_ARTIFACT_BYTES = 2574807872 + +L1_ARTIFACT_SHA256 = +FBCE43B8977DD73A86126E85AE0BFE219EF3131498AE7C37571DEA58F3C6AA34 + +PHASE4_COMPATIBLE_ARTIFACT_LINKAGE = ESTABLISHED +``` + +The non-mainline Nanbeige artifact is not selected and must not be substituted silently. + +## Phase 7Q-Q1 decision + +Authorize one controlled pair-qualification cell: + +```text +CELL = S4-L1-Q1 +BASE = S4-R10 +SOLE TREATMENT = MODEL L3 Granite 4.2 3B -> L1 Nanbeige4.2-3B +``` + +Frozen L1 identity: + +```text +MODEL_ALIAS = codepro-phase7q-nanbeige42-3b +MODEL_PATH = D:\projetos\codepro-mini-runtime\models\phase4\L1-Nanbeige4.2-3B\Nanbeige4.2-3B-Q4_K_M-mainline-e31e82d.gguf +MODEL_BYTES = 2574807872 +MODEL_SHA256 = FBCE43B8977DD73A86126E85AE0BFE219EF3131498AE7C37571DEA58F3C6AA34 +``` + +Everything else remains frozen from S4-R10: + +```text +SCAFFOLD = OpenHands Agent Canvas v1.21.0 +OPENHANDS_COMMIT = fc6d890f7b21c71a17de60d50597c00355e235ea +AGENT_SERVER = 1.49.3 +AUTOMATION = 1.13.3 +LLAMA_CPP_BUILD = 11205 +LLAMA_CPP_COMMIT = 95887577ab5fead779581a7030a83c7752ff3234 +PLATFORM = WINDOWS_NATIVE +SYSTEM_PROMPT_PROFILE = windows-embedded-v1 +PLATFORM_CONTRACT = windows-powershell-v2 +CONVERSATION_WORKTREE = false +CTX_SIZE = 16384 +PARALLEL = 1 +LITELLM = 1.94.3 +TIME_BUDGETS = 600 / 660 / 720 +MAX_ITERATIONS = 10 +FALLBACK = DISABLED +TASK = unchanged +TOOLSET = unchanged +TOOL_METADATA = unchanged +BYTECODE_CLEANUP = untracked-python-bytecode-only-v1 +``` + +Compatibility still requires the unchanged fail-closed gates: exact model identity, local binding, observed model calls, inspect/edit/command, exact patch capture, independent verifier, source repository unchanged, and workspace cleanup. + +No S4-L2 or other Phase 7Q cell is authorized by this decision. + + +## S4-L1-Q1 pre-execution environment failure + +The first local Q1 invocation did not enter the runner because the selected Python 3.12 environment failed importing the standard-library `select` extension: + +```text +ImportError: DLL load failed while importing select: +Uma política de Controle de Aplicativo bloqueou este arquivo. +``` + +A second invocation entered the runner, completed the upstream/npm preflight, and then the frozen llama.cpp server exited before readiness: + +```text +exit code = 3236495362 +hex = 0xC0E90002 +classification = BLOCKED_HARNESS_OR_ENVIRONMENT +adapter = null +vertical = null +``` + +Windows reports `0xC0E90002` as an Application Control policy block. Because no Agent Canvas conversation, model call, repository action, patch, or verifier execution occurred, this is not a valid S4-L1-Q1 experimental result. + +Therefore: + +```text +S4-L1-Q1_RESULT = NOT_ESTABLISHED +FAILURE_CLASS = HARNESS_ENVIRONMENT +PRIMARY = WINDOWS_APPLICATION_CONTROL_BLOCK +PROMOTION = NOT_AUTHORIZED +S4-L2 = NOT_AUTHORIZED +``` + +No security policy relaxation is authorized by Phase 7Q. + +The next authorized action is read-only diagnosis of the Windows Code Integrity operational log using: + +```powershell +uv run --python 3.12 --no-project python .\tools\run_phase7q_windows_application_control_diagnostic.py +``` + +This diagnostic performs no policy changes and must be used only to identify the exact blocked file(s) and event IDs before deciding whether the environment can be repaired without changing experimental semantics. + + +## Windows Code Integrity root cause + +The read-only Code Integrity diagnostic established the blocked runtime component exactly. + +Observed Windows Code Integrity events: + +```text +process = +D:\projetos\codepro-mini-runtime\downloads\llama-b11205-bin-win-cuda-13.4-x64\llama-server.exe + +blocked module = +D:\projetos\codepro-mini-runtime\downloads\llama-b11205-bin-win-cuda-13.4-x64\llama-server-impl.dll + +event IDs = 3077 and 3033 +policy ID = {0283ac0f-fff1-49ae-ada1-8a933130cad6} +reason = module did not meet Enterprise signing level requirements or violated code integrity policy +``` + +The diagnostic also observed the same Code Integrity policy blocking the uv-managed Python 3.12 standard-library extension: + +```text +python.exe -> DLLs\select.pyd +event IDs = 3077 and 3033 +policy ID = {0283ac0f-fff1-49ae-ada1-8a933130cad6} +``` + +Therefore the repeated Q1 startup failures are attributable to Windows application-control policy enforcement, not to the L1 Nanbeige model artifact, OpenHands behavior, task semantics, or pair compatibility. + +Canonical state: + +```text +S4-L1-Q1_RESULT = NOT_ESTABLISHED +FAILURE_CLASS = ENVIRONMENT_DRIFT +PRIMARY = WINDOWS_CODE_INTEGRITY_BLOCKS_FROZEN_RUNTIME_MODULE +BLOCKED_MODULE = llama-server-impl.dll +POLICY_ID = {0283ac0f-fff1-49ae-ada1-8a933130cad6} +MODEL_COMPATIBILITY_CONCLUSION = NOT_REACHED +SCAFFOLD_MODEL_PAIR_CONCLUSION = NOT_REACHED +``` + +This environment differs materially from the environment in which the same frozen llama.cpp build completed prior Phase 2-7 qualification work. + +No security-policy relaxation is authorized. No runtime binary replacement is authorized. Either action would be a new treatment requiring an explicit decision because: + +```text +POLICY_CHANGE != ENVIRONMENT_REPAIR_WITHOUT_SEMANTIC_DELTA +RUNTIME_BINARY_CHANGE != SAME_FROZEN_RUNTIME +``` + +The next action is read-only identity/signature inspection of the exact blocked frozen runtime files before choosing a new decision stream. + + +## Frozen runtime signature preflight result + +The read-only signature preflight observed the current frozen runtime files as follows: + +```text +llama-server.exe +bytes = 9216 +sha256 = 66C0EBF7CFF9053EABE208CCF1329AB46BFF5B1663750B4979AEF5C3D32DA555 +Authenticode = NotSigned +Zone.Identifier = present + +llama-server-impl.dll +bytes = 8916480 +sha256 = D1EECF41A8CA5D7BD972FDCFDF69270D2D056363CA66FD2EF1E7F5676DCED77A +Authenticode = NotSigned +Zone.Identifier = present +``` + +The repository does not preserve historical SHA256 values for these two individual files, so the current bytes cannot yet be proven identical to the earlier successful Phase 2-7 runtime bytes. + +Supported conclusion: + +```text +RUNTIME_BUILD_PATH = PRESERVED +CURRENT_RUNTIME_BYTES = KNOWN +AUTHENTICODE = NOT_SIGNED +ZONE_IDENTIFIER = PRESENT +WINDOWS_POLICY_BLOCK = CONFIRMED +HISTORICAL_BYTE_IDENTITY = UNKNOWN +``` + +Do not collapse this into `RUNTIME_IDENTITY_INTACT` or `RUNTIME_IDENTITY_CHANGED` without additional evidence. + +The next authorized action is another read-only provenance preflight: inspect the runtime download directory for retained archives, checksums, extraction metadata, or download artifacts that can bind the current extracted files back to the frozen b11205 package without changing any file or policy. + + +## Frozen runtime package provenance result + +The read-only provenance preflight found the retained original llama.cpp b11205 package: + +```text +archive = +D:\projetos\codepro-mini-runtime\downloads\llama-b11205-bin-win-cuda-13.4-x64.zip + +archive_bytes = 152343527 +archive_sha256 = D91178299D1007E162ACAD2776A24D5EF8C834A73F3DB282139ADDC12123BD80 +archive_zone_identifier = present +``` + +The extracted target directory is also present and contains the current runtime files with known hashes, including: + +```text +llama-server.exe +sha256 = 66C0EBF7CFF9053EABE208CCF1329AB46BFF5B1663750B4979AEF5C3D32DA555 + +llama-server-impl.dll +sha256 = D1EECF41A8CA5D7BD972FDCFDF69270D2D056363CA66FD2EF1E7F5676DCED77A +``` + +This establishes retained package provenance at the archive level, but not yet byte-for-byte linkage between the current extracted files and the retained ZIP members. + +Current state: + +```text +PACKAGE_PROVENANCE = RETAINED +ARCHIVE_IDENTITY = KNOWN +EXTRACTED_RUNTIME_BYTES = KNOWN +ARCHIVE_TO_EXTRACTED_BYTE_LINKAGE = NOT_YET_PROVEN +``` + +The next authorized action is read-only comparison of the SHA256 of the corresponding members inside the retained ZIP against the current extracted files. + + +## Runtime archive linkage result + +The read-only archive-member linkage check completed successfully: + +```text +archive = +D:\projetos\codepro-mini-runtime\downloads\llama-b11205-bin-win-cuda-13.4-x64.zip + +archive_sha256 = +D91178299D1007E162ACAD2776A24D5EF8C834A73F3DB282139ADDC12123BD80 + +all_targets_match = true +``` + +Exact member linkage: + +```text +llama-server.exe +zip_member_sha256 = 66C0EBF7CFF9053EABE208CCF1329AB46BFF5B1663750B4979AEF5C3D32DA555 +extracted_sha256 = 66C0EBF7CFF9053EABE208CCF1329AB46BFF5B1663750B4979AEF5C3D32DA555 +byte_identity_match = true + +llama-server-impl.dll +zip_member_sha256 = D1EECF41A8CA5D7BD972FDCFDF69270D2D056363CA66FD2EF1E7F5676DCED77A +extracted_sha256 = D1EECF41A8CA5D7BD972FDCFDF69270D2D056363CA66FD2EF1E7F5676DCED77A +byte_identity_match = true +``` + +Therefore: + +```text +PACKAGE_PROVENANCE = ESTABLISHED +ARCHIVE_TO_EXTRACTED_BYTE_LINKAGE = ESTABLISHED +RUNTIME_IDENTITY_INTACT_RELATIVE_TO_RETAINED_PACKAGE = TRUE +RUNTIME_CORRUPTION_OR_LOCAL_SUBSTITUTION = NOT_OBSERVED +``` + +Combined with the prior Code Integrity events, the supported attribution is now: + +```text +Q1_STARTUP_BLOCKER = WINDOWS_APPLICATION_CONTROL_ENVIRONMENT +FROZEN_RUNTIME_BYTES = INTACT +MODEL = NOT_REACHED +SCAFFOLD_MODEL_PAIR = NOT_REACHED +``` + +No Windows security-policy change, file unblocking, alternate runtime binary, re-extraction treatment, or Q1 retry is authorized by this finding alone. + +The next authorized action is read-only inspection of the current Windows application-control posture to determine which active enforcement surface owns Policy ID `{0283ac0f-fff1-49ae-ada1-8a933130cad6}`. + + +## Application-control posture diagnostic output correction + +The first posture-diagnostic run produced an unusably large serialized PowerShell object because unrestricted `Select-Object *` expanded provider/CIM metadata. The captured terminal output was therefore incomplete for policy-owner attribution. + +This is a diagnostic-output defect, not an experimental result and not a change in the Windows policy state. + +The posture script is constrained to scalar security-state fields, active Code Integrity policy-file metadata, and non-PowerShell registry values only. + +Canonical state remains: + +```text +POLICY_OWNER_ATTRIBUTION = NOT_ESTABLISHED +Q1_RETRY = NOT_AUTHORIZED +POLICY_CHANGE = NOT_AUTHORIZED +RUNTIME_REPLACEMENT = NOT_AUTHORIZED +``` + + +## Smart App Control policy-owner attribution + +The constrained posture diagnostic established the active Windows application-control owner. + +Observed local state: + +```text +C:\WINDOWS\System32\CodeIntegrity\CiPolicies\Active\{0283AC0F-FFF1-49AE-ADA1-8A933130CAD6}.cip = present +CodeIntegrityPolicyEnforcementStatus = 2 +UsermodeCodeIntegrityPolicyEnforcementStatus = 2 +VirtualizationBasedSecurityStatus = 2 +SecurityServicesRunning = [2] +VerifiedAndReputablePolicyState = 1 +SAC_EnforcementReason = 1 +``` + +Microsoft identifies policy ID `{0283ac0f-fff1-49ae-ada1-8a933130cad6}` as the inbox `VerifiedAndReputableDesktop` base policy used when Windows 11 Smart App Control is turned on. Microsoft documents `VerifiedAndReputablePolicyState = 1` as Smart App Control enforcement mode. + +Therefore: + +```text +POLICY_OWNER = SMART_APP_CONTROL +POLICY_NAME = VerifiedAndReputableDesktop +POLICY_ID = {0283AC0F-FFF1-49AE-ADA1-8A933130CAD6} +UMCI = ENFORCED +CODE_INTEGRITY = ENFORCED +VBS = RUNNING +Q1_STARTUP_BLOCKER = SMART_APP_CONTROL_REJECTION_OF_UNSIGNED_FROZEN_RUNTIME_MODULE +``` + +The previously observed Event ID 3077 block of `llama-server-impl.dll` is therefore attributable to Smart App Control enforcement, not to runtime corruption, model incompatibility, scaffold behavior, or task semantics. + +No Smart App Control disablement, registry mutation, policy removal, file unblocking, runtime replacement, code signing, or supplemental policy deployment is authorized by this attribution. + +The next authorized step is read-only `CiTool -lp -json` inventory to capture the active policy's own metadata before deciding whether a security-preserving exception route exists. + + +## CiTool inventory privilege gate + +The first read-only `CiTool.exe -lp -json` inventory attempt returned decimal `2147942405`, which is Windows HRESULT `0x80070005 (Access denied)`. + +Therefore the empty policy arrays from that run are not evidence that no policies are active. + +Canonical result: + +```text +CITOOL_INVENTORY = NOT_ESTABLISHED +FAILURE_CLASS = BLOCKED_BY_PRIVILEGE +FAILURE_DETAIL = ACCESS_DENIED +TARGET_POLICY_ABSENCE = NOT_INFERRED +``` + +A rerun from an elevated Administrator PowerShell is authorized because the command remains read-only and performs no policy mutation. + +The following remain unauthorized: + +```text +Q1_RETRY = NOT_AUTHORIZED +SMART_APP_CONTROL_DISABLE = NOT_AUTHORIZED +POLICY_REMOVAL = NOT_AUTHORIZED +SUPPLEMENTAL_POLICY_DEPLOYMENT = NOT_AUTHORIZED +RUNTIME_REPLACEMENT = NOT_AUTHORIZED +``` + + +## CiTool elevated inventory remains access denied + +A second read-only `CiTool.exe -lp -json` attempt was run from an Administrator PowerShell and again returned: + +```text +returncode = 2147942405 +returncode_hex = 0x80070005 +failure_class = BLOCKED_BY_PRIVILEGE +failure_detail = ACCESS_DENIED +inventory_established = false +``` + +This rules out the simple explanation that the first failure was caused only by a non-elevated shell. The Microsoft CiTool reference documents `-lp` / `--list-policies` as the supported policy inventory command. + +Canonical result: + +```text +CITOOL_INVENTORY = NOT_ESTABLISHED +CITOOL_ACCESS = DENIED_EVEN_FROM_REPORTED_ELEVATED_SHELL +TARGET_POLICY_ABSENCE = NOT_INFERRED +POLICY_OWNER = SMART_APP_CONTROL +``` + +The next authorized action is read-only token/elevation and direct-command diagnostics. No policy mutation is authorized. + + +## Effective elevation diagnosis + +The direct diagnostic captured the actual access token presented to `CiTool`. + +Observed: + +```text +mandatory integrity SID = S-1-16-8192 (Medium) +BUILTIN\Administrators = deny-only +local account and member of Administrators = deny-only +CiTool -h = SUCCESS +CiTool -lp -json = 0x80070005 ACCESS_DENIED +``` + +Microsoft documents that standard/filtered processes run at Medium integrity while elevated processes run at High integrity. A deny-only Administrators SID is consistent with the filtered UAC token rather than the full elevated administrator token. + +Canonical result: + +```text +EFFECTIVE_ELEVATION = FALSE +SHELL_TOKEN = UAC_FILTERED_MEDIUM_INTEGRITY +CITOOL_ACCESS_DENIED = EXPLAINED_BY_NON_ELEVATED_EFFECTIVE_TOKEN +CITOOL_INVENTORY = NOT_ESTABLISHED +``` + +The next authorized action is to rerun the same read-only diagnostic from a process whose effective token is High integrity (SID `S-1-16-12288`) and whose Administrators SID is enabled, not deny-only. + +No UAC policy changes, Smart App Control changes, Code Integrity changes, or other security relaxation are authorized. + + +## High-integrity CiTool inventory result + +The read-only diagnostic was rerun with an effective High integrity administrator token. + +Observed: + +```text +integrity SID = S-1-16-12288 +BUILTIN\Administrators = enabled +effective_elevation = true +CiTool -lp -json = SUCCESS +``` + +The active Smart App Control base policy is: + +```text +PolicyID = 0283ac0f-fff1-49ae-ada1-8a933130cad6 +BasePolicyID = 0283ac0f-fff1-49ae-ada1-8a933130cad6 +FriendlyName = VerifiedAndReputableDesktop +IsSystemPolicy = true +IsSignedPolicy = true +IsOnDisk = true +IsEnforced = true +IsAuthorized = true +PolicyOptions includes Enabled:Allow Supplemental Policies +``` + +An active Microsoft supplemental policy is also present for the same base: + +```text +FriendlyName = VerifiedAndReputableDesktopFlightSupplemental +BasePolicyID = 0283ac0f-fff1-49ae-ada1-8a933130cad6 +IsSystemPolicy = true +IsSignedPolicy = true +IsEnforced = true +IsAuthorized = true +``` + +Canonical result: + +```text +EFFECTIVE_ELEVATION = TRUE +CITOOL_INVENTORY = ESTABLISHED +POLICY_OWNER = SMART_APP_CONTROL +SMART_APP_CONTROL_BASE = VerifiedAndReputableDesktop +SMART_APP_CONTROL_BASE_SIGNED = TRUE +SMART_APP_CONTROL_BASE_ENFORCED = TRUE +SUPPLEMENTAL_CAPABILITY = PRESENT +``` + +Microsoft's Smart App Control support guidance states that there is no supported per-app bypass for Smart App Control. If Smart App Control cannot establish reputation, unsigned code is blocked; the security-preserving supported route is a valid code signature. Therefore the generic App Control supplemental-policy capability must not be treated as authorization for a local per-app SAC bypass. + +For the frozen Phase7Q runtime: + +```text +CURRENT_LLAMA_RUNTIME_SIGNATURE = NOT_SIGNED +CURRENT_LLAMA_RUNTIME_BYTES = FROZEN_AND_LINKED_TO_RETAINED_PACKAGE +PER_APP_SAC_BYPASS = NOT_SUPPORTED +SAC_DISABLEMENT = NOT_AUTHORIZED +LOCAL_ALLOWLIST_TREATMENT = NOT_AUTHORIZED +``` + +A validly signed runtime would be a distinct runtime-byte treatment and cannot be substituted into Q1 silently. + + +## Authenticode signing capability preflight result + +The read-only local signing-capability preflight completed successfully. + +Observed: + +```text +code_signing_certificates = [] +usable_code_signing_certificate_count = 0 +signtool.exe = PRESENT +modifies_files = false +modifies_policy = false +``` + +Canonical result: + +```text +SIGNTOOL_AVAILABILITY = PRESENT +LOCAL_CODE_SIGNING_CERTIFICATE = ABSENT +LOCAL_SIGNING_CAPABILITY = ABSENT +SIGNED_RUNTIME_REMEDIATION = NOT_FEASIBLE_WITH_CURRENT_LOCAL_CREDENTIALS +``` + +This does not mean signed-runtime remediation is impossible in principle. It means it is not available from the current environment without introducing a new external input, such as a trusted code-signing certificate/private key or a separately supplied already-signed runtime artifact. + +Either path would be a distinct treatment and would require explicit authorization, identity capture, provenance, and isolated evidence. It must not be substituted into Q1 silently. + +Therefore the current Phase7Q Q1 remains not established and blocked before model/scaffold compatibility is reached: + +```text +Q1_RESULT = NOT_ESTABLISHED +BLOCKER = SMART_APP_CONTROL_ENFORCEMENT +RUNTIME_BYTE_DRIFT = NOT_OBSERVED_RELATIVE_TO_RETAINED_PACKAGE +MODEL_PAIR_CONCLUSION = NOT_REACHED +Q1_RETRY = NOT_AUTHORIZED +ENVIRONMENT_REMEDIATION_DECISION = REQUIRED +``` + + +## Authorized signed-runtime remediation treatment + +The environment-remediation decision is explicitly authorized for a new, isolated treatment whose objective is to restore execution under Smart App Control without disabling or weakening the control. + +Authorization boundary: + +```text +REMEDIATION_TREATMENT = AUTHORIZED +TREATMENT_CLASS = SIGNED_RUNTIME +SMART_APP_CONTROL_CHANGE = NOT_AUTHORIZED +POLICY_RELAXATION = NOT_AUTHORIZED +Q1_REUSE = NOT_AUTHORIZED +Q1_RETRY = NOT_AUTHORIZED +RUNTIME_SUBSTITUTION = NOT_AUTHORIZED_UNTIL_CANDIDATE_IDENTITY_AND_SIGNATURE_ARE_ESTABLISHED +``` + +Current upstream discovery does not establish a suitable signed candidate. The current official llama.cpp release workflow builds and packages Windows artifacts directly and contains no observed `signtool`, Authenticode, or equivalent Windows code-signing step. GitHub release attestations establish provenance, not Windows Authenticode trust for Smart App Control. + +Therefore: + +```text +OFFICIAL_LLAMA_CPP_RELEASE_ARTIFACTS = AVAILABLE +OFFICIAL_RELEASE_PROVENANCE_ATTESTATION = AVAILABLE +OFFICIAL_RELEASE_AUTHENTICODE_SIGNING = NOT_OBSERVED +SIGNED_RUNTIME_CANDIDATE = NOT_ESTABLISHED +``` + +Any candidate introduced next must be treated as a distinct runtime identity. Before execution it requires at minimum: + +- exact source/provenance, +- archive and extracted SHA-256, +- Authenticode status and signer chain, +- version/build identity, +- byte-level separation from the frozen Q1 runtime, +- isolated evidence directory, +- no silent substitution into Q1. + + +## R1 signed/reputable upstream runtime candidate + +The authorized remediation stream selects one exact upstream candidate for identity/signature preflight only. + +Frozen candidate: + +```text +CELL = PHASE7Q-ENV-R1 +SOLE_TREATMENT = LLAMA_CPP_RUNTIME_BUILD +FROM = b11205 +TO = b11295 +UPSTREAM_REPOSITORY = ggml-org/llama.cpp +UPSTREAM_COMMIT = 3b3d022b823abaa62a467b26a44e10659e080ee7 +RELEASE_PUBLISHED_AT = 2026-09-30T18:50:02Z +ASSET = llama-b11295-bin-win-cuda-13.4-x64.zip +ASSET_BYTES = 152769566 +ASSET_SHA256 = 5F2EC28C4DED2986499D6B7B9DBD1FECB6EE7182208F3167C40E8EE3D585D1DC +``` + +This selection is not a compatibility conclusion and does not promote b11295. It is chosen because it is the current official upstream Windows x64 CUDA 13.4 artifact matching the existing backend family. + +All other Q1 dimensions remain frozen. R1 is a remediation preflight, not a Q1 retry. + +Authorized next action: + +- download the exact upstream asset into an isolated remediation staging directory, +- verify the archive SHA-256 against the GitHub-published digest, +- extract into a separate candidate directory, +- record member SHA-256 and Authenticode state for runtime executables/DLLs, +- do not execute candidate binaries, +- do not replace the frozen b11205 runtime, +- do not change Smart App Control or Code Integrity policy. + +Only after identity/signature/reputation-relevant evidence is captured may an execution preflight be considered. + + +## R1 b11295 identity and Authenticode preflight result + +The isolated remediation candidate preflight completed without executing candidate binaries. + +Observed: + +```text +CELL = PHASE7Q-ENV-R1 +TAG = b11295 +COMMIT = 3b3d022b823abaa62a467b26a44e10659e080ee7 +ARCHIVE_BYTES = 152769566 +ARCHIVE_SHA256 = 5F2EC28C4DED2986499D6B7B9DBD1FECB6EE7182208F3167C40E8EE3D585D1DC +ARCHIVE_DIGEST_MATCH = TRUE +RUNTIME_REPLACEMENT = FALSE +CANDIDATE_EXECUTION = FALSE +POLICY_CHANGES = FALSE +``` + +Critical candidate members: + +```text +llama-server.exe + bytes = 9216 + sha256 = FC7CB1C0252B2A98768EE15F3EA79AE59216243DF7F2D69A07A52A50E2A86762 + Authenticode = NotSigned + +llama-server-impl.dll + bytes = 8945664 + sha256 = B85BB48F0B1E684F0C722568651F23E4F17F49DF69C6332F80011D523A003155 + Authenticode = NotSigned +``` + +The broader extracted executable/DLL set inspected by the preflight is likewise unsigned. The localized Authenticode StatusMessage text is not used as the classification signal; the structured `Status = NotSigned` field is canonical. + +Therefore: + +```text +R1_IDENTITY = ESTABLISHED +R1_UPSTREAM_ARCHIVE_INTEGRITY = ESTABLISHED +R1_AUTHENTICODE = NOT_SIGNED +R1_SIGNED_RUNTIME_HYPOTHESIS = REJECTED +R1_SAC_REPUTATION_OR_ISG_HYPOTHESIS = NOT_TESTED +``` + +Because Smart App Control can also authorize code through reputation/Intelligent Security Graph, unsigned status alone does not establish whether this exact official b11295 artifact will execute on the current machine. + +The next authorized action is an isolated execution preflight of the candidate `llama-server.exe --version`, with Code Integrity event capture. This is not a runtime replacement, not a Q1 retry, and does not modify policy. + + +## R1 b11295 isolated execution preflight result + +The isolated candidate execution preflight ran only: + +```text +llama-server.exe --version +``` + +Observed: + +```text +CELL = PHASE7Q-ENV-R1 +candidate_execution = true +model_invocation = false +runtime_replacement = false +policy_changes = false +returncode = 3236495362 +returncode_hex = 0xC0E90002 +stdout = empty +stderr = empty +Zone.Identifier on llama-server.exe = absent +Code Integrity query returncode = 0 +Code Integrity matched events = not established by this capture +``` + +This reproduces the same process-level failure code previously observed with the frozen b11205 runtime, but the current event-capture filter did not return matching Code Integrity events. Therefore the exact blocked member/policy attribution for R1 is not yet established from this run alone. + +Canonical result: + +```text +R1_EXECUTION_PREFLIGHT = FAILED +R1_PROCESS_RETURN = 0xC0E90002 +R1_ZONE_IDENTIFIER = ABSENT +R1_CODE_INTEGRITY_EVENT_ATTRIBUTION = NOT_ESTABLISHED +R1_SAC_REPUTATION_OR_ISG_HYPOTHESIS = NOT_ESTABLISHED +Q1_RETRY = NOT_AUTHORIZED +RUNTIME_REPLACEMENT = NOT_AUTHORIZED +``` + +The next authorized action is a broader read-only Code Integrity event capture around the R1 execution window, matching the candidate directory and critical filenames without re-running Q1 or changing policy. + + +## R1 broad Code Integrity capture script defect + +The first broad Code Integrity capture attempt did not query the event log successfully. The embedded PowerShell filter contained a parser error caused by assignment inside a boolean expression. + +Observed: + +```text +candidate_execution = false +file_changes = false +policy_changes = false +returncode = 1 +failure_class = DIAGNOSTIC_SCRIPT_DEFECT +event_absence = NOT_INFERRED +``` + +This is a diagnostic-output defect only. It is not an R1 execution result and does not change the previously established `R1_EXECUTION_PREFLIGHT = FAILED`. + +The filter was corrected without changing the query window, event IDs, candidate identity, or authorization boundary. The corrected rerun remains read-only and does not execute the candidate. + + +## Consolidated R1 closure collector + +To avoid further fragmentation after the first broad-capture script defect, R1 closure evidence is consolidated into one read-only collector. + +The collector: + +- does not execute candidate binaries, +- does not invoke a model, +- does not replace the frozen runtime, +- does not modify Windows policy or files, +- re-verifies SHA-256 and Authenticode for `llama-server.exe` and `llama-server-impl.dll`, +- reads the original R1 execution window through `Get-WinEvent`, +- independently reads the same window through `wevtutil`, +- checks both evidence paths for the b11295 candidate path, `llama-server-impl.dll`, policy ID `{0283ac0f-fff1-49ae-ada1-8a933130cad6}`, and event IDs 3033/3076/3077. + +If both independent event readers attribute the R1 window to the candidate and the Smart App Control policy, the treatment can be classified and closed without another candidate execution. + +If the historical event window no longer contains sufficient evidence, the treatment stops at an external evidence limitation rather than silently re-running or changing the treatment. + + +## R1 final classification + +The consolidated closure collector established the exact Phase7Q ENV-R1 failure attribution with two independent Windows event readers. + +Critical runtime identity remained stable: + +```text +llama-server.exe + bytes = 9216 + sha256 = FC7CB1C0252B2A98768EE15F3EA79AE59216243DF7F2D69A07A52A50E2A86762 + Authenticode = NotSigned + +llama-server-impl.dll + bytes = 8945664 + sha256 = B85BB48F0B1E684F0C722568651F23E4F17F49DF69C6332F80011D523A003155 + Authenticode = NotSigned +``` + +The original isolated execution window contains both Code Integrity Event ID 3033 and Event ID 3077 for: + +```text +process = +D:\projetos\codepro-mini-runtime\downloads\phase7q-remediation\b11295\extracted\llama-server.exe + +blocked module = +D:\projetos\codepro-mini-runtime\downloads\phase7q-remediation\b11295\extracted\mtmd.dll + +status = 0xC0E90002 +policy = VerifiedAndReputableDesktop +policy GUID = {0283ac0f-fff1-49ae-ada1-8a933130cad6} +``` + +Both `Get-WinEvent` and an independent `wevtutil` read of the same historical window agree on: + +```text +candidate_path_observed = true +server_exe_observed = true +event_3033_observed = true +event_3077_observed = true +target_policy_id_observed = true +``` + +The originally hypothesized `llama-server-impl.dll` was not the first blocked member in R1. The exact R1 blocked member is `mtmd.dll`. This does not alter the causal classification: the candidate runtime remains unable to start under the active Smart App Control base policy. + +Final canonical result: + +```text +RESULT = INCOMPATIBLE + +PRIMARY = SMART_APP_CONTROL_BLOCKS_B11295_RUNTIME_DEPENDENCY_MTMD_DLL + +SECONDARY = OFFICIAL_B11295_WINDOWS_RUNTIME_IS_UNSIGNED_AND_NOT_AUTHORIZED_BY_CURRENT_VERIFIED_AND_REPUTABLE_POLICY + +EVIDENCE = + ARCHIVE_IDENTITY_VERIFIED + CRITICAL_MEMBER_HASHES_REVERIFIED + AUTHENTICODE_NOT_SIGNED + ISOLATED_EXECUTION_RETURN_0xC0E90002 + CODE_INTEGRITY_EVENT_3033 + CODE_INTEGRITY_EVENT_3077 + POLICY_VERIFIED_AND_REPUTABLE_DESKTOP + POLICY_GUID_{0283ac0f-fff1-49ae-ada1-8a933130cad6} + INDEPENDENT_GET_WINEVENT_AND_WEVTUTIL_AGREEMENT + +ACCEPTANCE = FAILED +PROMOTION = NOT_AUTHORIZED + +NEXT_EXPERIMENTAL_CELL = NOT_AUTHORIZED + +PHASE8 = BLOCKED_BY_NO_COMPATIBLE_SCAFFOLD_RUNTIME_PATH +``` + +Interpretation: + +- R1 changed only the llama.cpp runtime build from b11205 to b11295. +- The newer official upstream artifact does not restore executable compatibility under the current Smart App Control environment. +- The failure occurs before model invocation, so it does not provide evidence about Nanbeige L1 compatibility or scaffold/model interaction. +- The R1 treatment is closed. Repeating additional unsigned llama.cpp builds under the same policy would not be a justified next cell without a new treatment rationale. +- Any next treatment must introduce a materially different execution-trust mechanism or environment and therefore requires separate authorization. diff --git a/roadmap.md b/roadmap.md index 5ba611ae..16e5ad6a 100644 --- a/roadmap.md +++ b/roadmap.md @@ -602,6 +602,7 @@ PHASE 5 CodePro local plumbing COMPLETE PHASE 6 Execution/verifier plumbing COMPLETE PHASE 7 Scaffold compatibility COMPLETE PHASE 7R S4 runtime requalification COMPLETE +PHASE 7Q Scaffold/model pair recovery IN_PROGRESS PHASE 8 Scaffold screen NOT_STARTED PHASE 9 Scaffold pruning NOT_STARTED PHASE 10 Model screen NOT_STARTED @@ -619,8 +620,21 @@ PHASE 19 Cloud tier DEFERRED Current task: ```text -PHASE 7R = S4 RUNTIME REQUALIFICATION -ACTIVE_PR = #91 -STATUS = COMPLETE / NO_COMPATIBLE_SURVIVOR +PHASE 7Q = SCAFFOLD/MODEL PAIR QUALIFICATION RECOVERY +STATUS = BLOCKED / R1_CLOSED_SMART_APP_CONTROL_INCOMPATIBLE PHASE 8 = BLOCKED_BY_NO_COMPATIBLE_SCAFFOLD +NEXT_EXPERIMENTAL_CELL = NOT_AUTHORIZED +``` + + +Phase7Q environment remediation R1 closure: + +```text +R1_RESULT = INCOMPATIBLE +R1_PRIMARY = SMART_APP_CONTROL_BLOCKS_B11295_RUNTIME_DEPENDENCY_MTMD_DLL +R1_SECONDARY = OFFICIAL_B11295_WINDOWS_RUNTIME_IS_UNSIGNED_AND_NOT_AUTHORIZED_BY_CURRENT_VERIFIED_AND_REPUTABLE_POLICY +R1_ACCEPTANCE = FAILED +R1_PROMOTION = NOT_AUTHORIZED +NEXT_EXPERIMENTAL_CELL = NOT_AUTHORIZED +PHASE8 = BLOCKED_BY_NO_COMPATIBLE_SCAFFOLD_RUNTIME_PATH ``` diff --git a/tools/run_phase7_s4_openhands.py b/tools/run_phase7_s4_openhands.py index 4e47d75b..de1571e7 100644 --- a/tools/run_phase7_s4_openhands.py +++ b/tools/run_phase7_s4_openhands.py @@ -61,10 +61,10 @@ def sha256(path:Path)->str: for chunk in iter(lambda:h.read(1024*1024),b""): d.update(chunk) return d.hexdigest().upper() -def summary_base()->dict[str,Any]: +def summary_base(*, model_alias:str=MODEL_ALIAS)->dict[str,Any]: return {"schema_version":1,"phase":7,"candidate":"S4","scaffold":"OpenHands Agent Canvas","version":VERSION,"commit":COMMIT, "agent_server_version":AGENT_SERVER_VERSION,"automation_version":AUTOMATION_VERSION,"classification":"BLOCKED_UNCLASSIFIED", - "local_runtime":{"base_url":LLAMA_BASE,"model_alias":MODEL_ALIAS,"fallback":"DISABLED"}, + "local_runtime":{"base_url":LLAMA_BASE,"model_alias":model_alias,"fallback":"DISABLED"}, "native_contract":"Agent Canvas request builder -> Agent Server 1.49.3 -> OpenHands agent tools -> isolated repository -> CodePro verifier", "gates":{},"telemetry":{},"vertical":None,"adapter":None,"selected_scaffold":False,"executor_promotion":"NOT_AUTHORIZED"} @@ -119,10 +119,17 @@ def ensure_install(evidence:Path,summary:dict[str,Any])->bool: if not vite.is_file(): summary["blocker"]={"stage":"vite_node","detail":str(vite)}; return False return True -def find_model()->Path: +def find_model(*, model_path:str|None=None, model_bytes:int=MODEL_BYTES, model_sha256:str=MODEL_SHA256)->Path: + expected_sha=model_sha256.upper() + if model_path is not None: + p=Path(model_path).expanduser().resolve() + if not p.is_file(): raise RuntimeError(f"frozen model artifact not found: {p}") + if p.stat().st_size!=model_bytes: raise RuntimeError(f"frozen model byte-size mismatch: {p}") + if sha256(p)!=expected_sha: raise RuntimeError(f"frozen model SHA256 mismatch: {p}") + return p for p in (RUNTIME_ROOT/"models"/"phase4").rglob("*.gguf"): - if p.stat().st_size==MODEL_BYTES and sha256(p)==MODEL_SHA256: return p.resolve() - raise RuntimeError("frozen L3 artifact not found by SHA256") + if p.stat().st_size==model_bytes and sha256(p)==expected_sha: return p.resolve() + raise RuntimeError("frozen model artifact not found by SHA256") def find_server()->Path: matches=list((RUNTIME_ROOT/"downloads"/"llama-b11205-bin-win-cuda-13.4-x64").rglob("llama-server.exe")) @@ -140,13 +147,13 @@ def get_json(url:str,headers:dict[str,str]|None=None,timeout:float=3)->Any: req=Request(url,headers=headers or {}) with urlopen(req,timeout=timeout) as r: return json.loads(r.read().decode("utf-8")) -def wait_llama(process:subprocess.Popen[Any],timeout_seconds:int=120)->dict[str,Any]: +def wait_llama(process:subprocess.Popen[Any],model_alias:str=MODEL_ALIAS,timeout_seconds:int=120)->dict[str,Any]: deadline=time.monotonic()+timeout_seconds; last="" while time.monotonic() return "INCOMPATIBLE" def main(argv:list[str]|None=None)->int: - parser=argparse.ArgumentParser(description=__doc__); parser.add_argument("--evidence-dir",required=True); parser.add_argument("--llama-parallel",type=int,default=None); parser.add_argument("--llama-context",type=int,default=4096); parser.add_argument("--litellm-version",default=None); parser.add_argument("--completion-log-dir",default=None); parser.add_argument("--study-id",default="phase7"); parser.add_argument("--conversation-timeout-seconds",type=int,default=240); parser.add_argument("--executor-timeout-seconds",type=int,default=280); parser.add_argument("--vertical-wall-time-seconds",type=int,default=320); parser.add_argument("--platform-contract",choices=("windows-powershell-v1","windows-powershell-v2"),default=None); parser.add_argument("--system-prompt-profile",choices=("windows-minimal-v1","windows-embedded-v1"),default=None); parser.add_argument("--conversation-worktree",choices=("true","false"),default="true"); parser.add_argument("--cleanup-untracked-python-bytecode",choices=("true","false"),default="false"); parser.add_argument("--max-iterations",type=int,default=8); args=parser.parse_args(argv) + parser=argparse.ArgumentParser(description=__doc__); parser.add_argument("--evidence-dir",required=True); parser.add_argument("--llama-parallel",type=int,default=None); parser.add_argument("--llama-context",type=int,default=4096); parser.add_argument("--litellm-version",default=None); parser.add_argument("--completion-log-dir",default=None); parser.add_argument("--study-id",default="phase7"); parser.add_argument("--conversation-timeout-seconds",type=int,default=240); parser.add_argument("--executor-timeout-seconds",type=int,default=280); parser.add_argument("--vertical-wall-time-seconds",type=int,default=320); parser.add_argument("--platform-contract",choices=("windows-powershell-v1","windows-powershell-v2"),default=None); parser.add_argument("--system-prompt-profile",choices=("windows-minimal-v1","windows-embedded-v1"),default=None); parser.add_argument("--conversation-worktree",choices=("true","false"),default="true"); parser.add_argument("--cleanup-untracked-python-bytecode",choices=("true","false"),default="false"); parser.add_argument("--max-iterations",type=int,default=8); parser.add_argument("--model-path",default=None); parser.add_argument("--model-bytes",type=int,default=MODEL_BYTES); parser.add_argument("--model-sha256",default=MODEL_SHA256); parser.add_argument("--model-alias",default=MODEL_ALIAS); args=parser.parse_args(argv) evidence=Path(args.evidence_dir).expanduser().resolve() if evidence.exists() and any(evidence.iterdir()): raise SystemExit(f"refusing to overwrite non-empty evidence directory: {evidence}") - evidence.mkdir(parents=True,exist_ok=True); summary=summary_base(); summary["study_id"]=args.study_id + evidence.mkdir(parents=True,exist_ok=True); summary=summary_base(model_alias=args.model_alias); summary["study_id"]=args.study_id llama_proc=None; canvas_proc=None; isolated=None try: if not ensure_upstream(evidence,summary): return block(evidence,summary,"BLOCKED_UPSTREAM_IDENTITY","upstream",summary.get("blocker")) @@ -242,9 +249,9 @@ def main(argv:list[str]|None=None)->int: if args.max_iterations < 1: raise ValueError("--max-iterations must be >= 1") summary["timeouts"]={"conversation_poll_seconds":args.conversation_timeout_seconds,"executor_process_seconds":args.executor_timeout_seconds,"vertical_wall_seconds":args.vertical_wall_time_seconds} summary["max_iterations_requested"]=args.max_iterations - assert_ports(); model=find_model(); server=find_server() + assert_ports(); model=find_model(model_path=args.model_path,model_bytes=args.model_bytes,model_sha256=args.model_sha256); server=find_server() if args.llama_context < 1: raise ValueError("--llama-context must be >= 1") - llama_argv=[str(server),"-m",str(model),"--device","CUDA0","-ngl","99","-c",str(args.llama_context),"--host","127.0.0.1","--port",str(LLAMA_PORT),"--alias",MODEL_ALIAS] + llama_argv=[str(server),"-m",str(model),"--device","CUDA0","-ngl","99","-c",str(args.llama_context),"--host","127.0.0.1","--port",str(LLAMA_PORT),"--alias",args.model_alias] if args.llama_parallel is not None: if args.llama_parallel < 1: raise ValueError("--llama-parallel must be >= 1") llama_argv += ["--parallel",str(args.llama_parallel)] @@ -257,10 +264,10 @@ def main(argv:list[str]|None=None)->int: if effective_context != args.llama_context: raise RuntimeError(f"llama context binding mismatch: declared={args.llama_context} effective={effective_context}") summary["local_runtime"]["context_size"]=effective_context - write_json(evidence/"runtime-binding.json",{"llama_cpp_build":LLAMA_BUILD,"llama_cpp_commit":LLAMA_COMMIT,"server_argv":llama_argv,"base_url":LLAMA_BASE,"model_alias":MODEL_ALIAS,"model_artifact":str(model),"model_bytes":model.stat().st_size,"model_sha256":sha256(model),"context_size":effective_context,"parallel":args.llama_parallel,"fallback":"DISABLED"}) + write_json(evidence/"runtime-binding.json",{"llama_cpp_build":LLAMA_BUILD,"llama_cpp_commit":LLAMA_COMMIT,"server_argv":llama_argv,"base_url":LLAMA_BASE,"model_alias":args.model_alias,"model_artifact":str(model),"model_bytes":model.stat().st_size,"model_sha256":sha256(model),"context_size":effective_context,"parallel":args.llama_parallel,"fallback":"DISABLED"}) llama_out=(evidence/"llama-server-stdout.txt").open("w",encoding="utf-8",newline="\n"); llama_err=(evidence/"llama-server-stderr.txt").open("w",encoding="utf-8",newline="\n") llama_proc=subprocess.Popen(llama_argv,cwd=server.parent,stdout=llama_out,stderr=llama_err,text=True,shell=False) - write_json(evidence/"llama-preflight.json",wait_llama(llama_proc)); summary["gates"]["local_runtime_ready"]=True + write_json(evidence/"llama-preflight.json",wait_llama(llama_proc,args.model_alias)); summary["gates"]["local_runtime_ready"]=True with tempfile.TemporaryDirectory(prefix="codepro-phase7-s4-",dir=S4_ROOT,ignore_cleanup_errors=True) as tmp: base=Path(tmp); source=base/"source"; workspaces=base/"workspaces"; state=base/"canvas-state"; profile=base/"profile"; source.mkdir(); state.mkdir(); profile.mkdir() (profile/"AppData"/"Local").mkdir(parents=True); (profile/"AppData"/"Roaming").mkdir(parents=True); (profile/".cache").mkdir(parents=True) @@ -297,7 +304,7 @@ def main(argv:list[str]|None=None)->int: write_json(evidence/"canvas-preflight.json",wait_canvas(canvas_proc)); summary["gates"]["agent_canvas_ready"]=True result_path=evidence/"openhands-result.json" issue="Work only in the current repository. Inspect value.py and confirm it contains VALUE = \"before\". Change it to VALUE = \"after\". Then run a Python command that imports value and asserts value.VALUE == \"after\". Finish after the check succeeds." - executor=[sys.executable,str(ROOT/"tools"/"phase7_openhands_executor.py"),"--upstream",str(UPSTREAM),"--backend-url",CANVAS_BASE,"--api-key",SESSION_KEY,"--working-dir",str(isolated.workspace),"--llm-base-url",LLAMA_BASE,"--model-alias",MODEL_ALIAS,"--result",str(result_path),"--issue",issue,"--poll-timeout-seconds",str(args.conversation_timeout_seconds),"--process-timeout-seconds",str(args.executor_timeout_seconds),"--conversation-worktree",args.conversation_worktree,"--cleanup-untracked-python-bytecode",args.cleanup_untracked_python_bytecode,"--max-iterations",str(args.max_iterations)] + executor=[sys.executable,str(ROOT/"tools"/"phase7_openhands_executor.py"),"--upstream",str(UPSTREAM),"--backend-url",CANVAS_BASE,"--api-key",SESSION_KEY,"--working-dir",str(isolated.workspace),"--llm-base-url",LLAMA_BASE,"--model-alias",args.model_alias,"--result",str(result_path),"--issue",issue,"--poll-timeout-seconds",str(args.conversation_timeout_seconds),"--process-timeout-seconds",str(args.executor_timeout_seconds),"--conversation-worktree",args.conversation_worktree,"--cleanup-untracked-python-bytecode",args.cleanup_untracked_python_bytecode,"--max-iterations",str(args.max_iterations)] summary["conversation_worktree"]={"openhands_nested":args.conversation_worktree=="true","codepro_isolated_workspace":True} summary["workspace_ephemeral_cleanup_policy"]="untracked-python-bytecode-only-v1" if args.cleanup_untracked_python_bytecode=="true" else None if args.platform_contract is not None: @@ -341,7 +348,7 @@ def main(argv:list[str]|None=None)->int: else: system_prompt_profile_observed=False max_iterations_observed=(adapter.get("conversation_info") or {}).get("max_iterations")==args.max_iterations if isinstance(adapter.get("conversation_info"),dict) else False - summary["gates"].update({"max_iterations_observed":max_iterations_observed,"explicit_local_binding":(adapter.get("llm_binding") or {}).get("base_url")==LLAMA_BASE and (adapter.get("llm_binding") or {}).get("model")==f"openai/{MODEL_ALIAS}","platform_contract_observed":platform_contract_observed,"system_prompt_profile_observed":system_prompt_profile_observed,"model_calls_observed":model_call_observed(adapter),"inspect_observed":obs.get("inspectObserved") is True,"command_observed":obs.get("commandObserved") is True,"event_edit_signal":obs.get("editObserved") is True,"edit_observed":len(vertical.get("changed_files") or [])>0,"termination_observed":str(adapter.get("execution_status") or "").lower() in {"finished","error","stuck","stopped"},"patch_captured":patch_ok,"independent_verifier":verifier_ok,"source_repository_unchanged":source_state.get("clean") is True,"workspace_cleanup":cleanup.removed}) + summary["gates"].update({"max_iterations_observed":max_iterations_observed,"explicit_local_binding":(adapter.get("llm_binding") or {}).get("base_url")==LLAMA_BASE and (adapter.get("llm_binding") or {}).get("model")==f"openai/{args.model_alias}","platform_contract_observed":platform_contract_observed,"system_prompt_profile_observed":system_prompt_profile_observed,"model_calls_observed":model_call_observed(adapter),"inspect_observed":obs.get("inspectObserved") is True,"command_observed":obs.get("commandObserved") is True,"event_edit_signal":obs.get("editObserved") is True,"edit_observed":len(vertical.get("changed_files") or [])>0,"termination_observed":str(adapter.get("execution_status") or "").lower() in {"finished","error","stuck","stopped"},"patch_captured":patch_ok,"independent_verifier":verifier_ok,"source_repository_unchanged":source_state.get("clean") is True,"workspace_cleanup":cleanup.removed}) summary["repository_state"]={"initial_revision":revision,"final":final_state,"source":source_state}; summary["classification"]=classify(vertical,adapter,summary["gates"]) write_json(evidence/"s4-summary.json",summary); print(json.dumps(summary,ensure_ascii=False,indent=2,sort_keys=True)); return 0 except Exception as exc: diff --git a/tools/run_phase7q_authenticode_signing_capability_preflight.py b/tools/run_phase7q_authenticode_signing_capability_preflight.py new file mode 100644 index 00000000..135a7159 --- /dev/null +++ b/tools/run_phase7q_authenticode_signing_capability_preflight.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Read-only preflight for local Authenticode signing capability.""" + +from __future__ import annotations + +import json +import subprocess + + +PS = r""" +$ErrorActionPreference = 'SilentlyContinue' + +function Test-Chain($cert) { + $chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain + $ok = $chain.Build($cert) + [pscustomobject]@{ + ok = [bool]$ok + status = @($chain.ChainStatus | ForEach-Object { $_.Status.ToString() }) + } +} + +$stores = @('Cert:\CurrentUser\My', 'Cert:\LocalMachine\My') +$certs = foreach($store in $stores) { + if(Test-Path $store) { + Get-ChildItem $store | ForEach-Object { + $c = $_ + $ekuOids = @($c.EnhancedKeyUsageList | ForEach-Object { $_.ObjectId.Value }) + if($ekuOids -contains '1.3.6.1.5.5.7.3.3') { + $chain = Test-Chain $c + [pscustomobject]@{ + store = $store + thumbprint = $c.Thumbprint + has_private_key = [bool]$c.HasPrivateKey + not_before = $c.NotBefore.ToUniversalTime().ToString('o') + not_after = $c.NotAfter.ToUniversalTime().ToString('o') + chain_ok = [bool]$chain.ok + chain_status = @($chain.status) + } + } + } + } +} + +$signtool = Get-Command signtool.exe -ErrorAction SilentlyContinue +if(-not $signtool) { + $candidates = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Filter signtool.exe -Recurse -File -ErrorAction SilentlyContinue | + Sort-Object FullName -Descending + $signtoolPaths = @($candidates | ForEach-Object { $_.FullName }) +} else { + $signtoolPaths = @($signtool.Source) +} + +[pscustomobject]@{ + code_signing_certificates = @($certs) + usable_code_signing_certificate_count = @($certs | Where-Object { $_.has_private_key -and $_.chain_ok -and ([datetime]$_.not_after -gt [datetime]::UtcNow) }).Count + signtool_paths = @($signtoolPaths) +} | ConvertTo-Json -Depth 6 -Compress +""" + +def main() -> int: + p = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", PS], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + payload: dict[str, object] = { + "schema_version": 1, + "operation": "phase7q-authenticode-signing-capability-preflight", + "modifies_files": False, + "modifies_policy": False, + "returncode": p.returncode, + "stderr": p.stderr, + } + if p.stdout.strip(): + try: + payload["result"] = json.loads(p.stdout) + except json.JSONDecodeError: + payload["raw_stdout"] = p.stdout + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if p.returncode == 0 else p.returncode + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_b11295_broad_code_integrity_capture.py b/tools/run_phase7q_b11295_broad_code_integrity_capture.py new file mode 100644 index 00000000..10838e7b --- /dev/null +++ b/tools/run_phase7q_b11295_broad_code_integrity_capture.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Read-only broad Code Integrity capture for the Phase7Q b11295 R1 window.""" + +from __future__ import annotations + +import json +import subprocess + + +START_UTC = "2026-09-30T19:46:40Z" +END_UTC = "2026-09-30T19:47:30Z" +CANDIDATE_DIR = r"D:\projetos\codepro-mini-runtime\downloads\phase7q-remediation\b11295\extracted" +NEEDLES = [ + "llama-server.exe", + "llama-server-impl.dll", + "b11295", + CANDIDATE_DIR, +] + + +def main() -> int: + needles_ps = ",".join(json.dumps(x) for x in NEEDLES) + ps = rf""" +$start=[datetime]::Parse({json.dumps(START_UTC)}) +$end=[datetime]::Parse({json.dumps(END_UTC)}) +$needles=@({needles_ps}) +$events = Get-WinEvent -FilterHashtable @{{ + LogName='Microsoft-Windows-CodeIntegrity/Operational' + StartTime=$start + EndTime=$end +}} -ErrorAction SilentlyContinue | + Where-Object {{ + $_.Id -in 3033,3076,3077 -and ( + $m=$_.Message + ($needles | Where-Object {{ $m -like ('*' + $_ + '*') }}).Count -gt 0 + ) + }} | + Select-Object TimeCreated,Id,LevelDisplayName,Message +@($events) | ConvertTo-Json -Depth 6 -Compress +""" + p = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + payload: dict[str, object] = { + "schema_version": 1, + "operation": "phase7q-b11295-broad-code-integrity-capture", + "policy_changes": False, + "file_changes": False, + "candidate_execution": False, + "start_utc": START_UTC, + "end_utc": END_UTC, + "needles": NEEDLES, + "returncode": p.returncode, + "stderr": p.stderr, + } + if p.stdout.strip(): + try: + payload["events"] = json.loads(p.stdout) + except json.JSONDecodeError: + payload["raw_stdout"] = p.stdout + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if p.returncode == 0 else p.returncode + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_b11295_isolated_execution_preflight.py b/tools/run_phase7q_b11295_isolated_execution_preflight.py new file mode 100644 index 00000000..4614a963 --- /dev/null +++ b/tools/run_phase7q_b11295_isolated_execution_preflight.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Execute the isolated b11295 candidate minimally and capture Code Integrity evidence. + +This does not replace the frozen runtime, invoke a model, or modify policy. +""" + +from __future__ import annotations + +import datetime as dt +import json +import subprocess +from pathlib import Path + + +RUNTIME_ROOT = Path(r"D:\projetos\codepro-mini-runtime") +CANDIDATE_DIR = RUNTIME_ROOT / "downloads" / "phase7q-remediation" / "b11295" / "extracted" +SERVER = CANDIDATE_DIR / "llama-server.exe" + + +def ps_json(command: str) -> dict[str, object]: + p = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", command], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + out: dict[str, object] = { + "returncode": p.returncode, + "stderr": p.stderr, + } + if p.stdout.strip(): + try: + out["result"] = json.loads(p.stdout) + except json.JSONDecodeError: + out["raw_stdout"] = p.stdout + return out + + +def main() -> int: + started = dt.datetime.now(dt.timezone.utc) + zone = ps_json( + "$p=" + json.dumps(str(SERVER)) + "; " + "$z=Get-Item -LiteralPath $p -Stream Zone.Identifier -ErrorAction SilentlyContinue; " + "[pscustomobject]@{Present=[bool]$z;Length=if($z){$z.Length}else{$null}} | ConvertTo-Json -Compress" + ) + + p = subprocess.run( + [str(SERVER), "--version"], + cwd=str(CANDIDATE_DIR), + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + finished = dt.datetime.now(dt.timezone.utc) + + start_iso = started.astimezone().isoformat() + ps_events = rf""" +$start = [datetime]::Parse({json.dumps(start_iso)}) +$exe = {json.dumps(str(SERVER))} +$dir = {json.dumps(str(CANDIDATE_DIR))} +$events = Get-WinEvent -FilterHashtable @{{ + LogName='Microsoft-Windows-CodeIntegrity/Operational' + StartTime=$start.AddSeconds(-5) +}} -ErrorAction SilentlyContinue | + Where-Object {{ + $_.Id -in 3033,3076,3077 -and + ($_.Message -like ('*' + $exe + '*') -or $_.Message -like ('*' + $dir + '*')) + }} | + Select-Object TimeCreated,Id,LevelDisplayName,Message +@($events) | ConvertTo-Json -Depth 5 -Compress +""" + events = ps_json(ps_events) + + payload = { + "schema_version": 1, + "operation": "phase7q-b11295-isolated-execution-preflight", + "candidate_cell": "PHASE7Q-ENV-R1", + "candidate_execution": True, + "model_invocation": False, + "runtime_replacement": False, + "policy_changes": False, + "server": str(SERVER), + "zone_identifier": zone, + "started_utc": started.isoformat(), + "finished_utc": finished.isoformat(), + "process": { + "argv": [str(SERVER), "--version"], + "returncode": p.returncode, + "returncode_hex": f"0x{p.returncode & 0xFFFFFFFF:08X}", + "stdout": p.stdout, + "stderr": p.stderr, + }, + "code_integrity_events": events, + } + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_b11295_r1_closure_collector.py b/tools/run_phase7q_b11295_r1_closure_collector.py new file mode 100644 index 00000000..6c67ae8e --- /dev/null +++ b/tools/run_phase7q_b11295_r1_closure_collector.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +"""Consolidated read-only closure collector for Phase7Q ENV-R1. + +This script does not execute candidate binaries, invoke a model, replace a runtime, +or modify Windows policy. It independently reads the already-recorded Code Integrity +window through Get-WinEvent and wevtutil, then re-verifies the critical candidate +member identity and Authenticode state. +""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +import xml.etree.ElementTree as ET +from pathlib import Path + + +START_UTC = "2026-09-30T19:46:40.000Z" +END_UTC = "2026-09-30T19:47:30.000Z" +POLICY_ID = "0283ac0f-fff1-49ae-ada1-8a933130cad6" +RUNTIME_ROOT = Path(r"D:\projetos\codepro-mini-runtime") +CANDIDATE_DIR = RUNTIME_ROOT / "downloads" / "phase7q-remediation" / "b11295" / "extracted" +SERVER = CANDIDATE_DIR / "llama-server.exe" +SERVER_IMPL = CANDIDATE_DIR / "llama-server-impl.dll" +NEEDLES = [ + str(CANDIDATE_DIR).lower(), + "llama-server.exe", + "llama-server-impl.dll", + "b11295", +] + + +def run(cmd: list[str]) -> dict[str, object]: + p = subprocess.run( + cmd, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + return { + "cmd": cmd, + "returncode": p.returncode, + "returncode_hex": f"0x{p.returncode & 0xFFFFFFFF:08X}", + "stdout": p.stdout, + "stderr": p.stderr, + } + + +def sha256_file(path: Path) -> str | None: + if not path.is_file(): + return None + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + h.update(chunk) + return h.hexdigest().upper() + + +def authenticode(path: Path) -> dict[str, object]: + ps = ( + "$s=Get-AuthenticodeSignature -LiteralPath " + + json.dumps(str(path)) + + "; [pscustomobject]@{Status=$s.Status.ToString();" + "SignerSubject=if($s.SignerCertificate){$s.SignerCertificate.Subject}else{$null};" + "SignerThumbprint=if($s.SignerCertificate){$s.SignerCertificate.Thumbprint}else{$null}}" + " | ConvertTo-Json -Compress" + ) + out = run(["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps]) + result: dict[str, object] = { + "returncode": out["returncode"], + "stderr": out["stderr"], + } + stdout = str(out["stdout"]).strip() + if stdout: + try: + result["result"] = json.loads(stdout) + except json.JSONDecodeError: + result["raw_stdout"] = stdout + return result + + +def get_winevent_capture() -> dict[str, object]: + needles_ps = ",".join(json.dumps(x) for x in NEEDLES) + ps = rf""" +$start=[datetime]::Parse({json.dumps(START_UTC)}) +$end=[datetime]::Parse({json.dumps(END_UTC)}) +$needles=@({needles_ps}) +$events = Get-WinEvent -FilterHashtable @{{ + LogName='Microsoft-Windows-CodeIntegrity/Operational' + StartTime=$start + EndTime=$end +}} -ErrorAction Stop | + Where-Object {{ + if ($_.Id -notin 3033,3076,3077) {{ return $false }} + $m = $_.Message + $xml = $_.ToXml() + foreach ($needle in $needles) {{ + if (($m -and $m.ToLowerInvariant().Contains($needle.ToLowerInvariant())) -or + ($xml -and $xml.ToLowerInvariant().Contains($needle.ToLowerInvariant()))) {{ + return $true + }} + }} + return $false + }} | + ForEach-Object {{ + [pscustomobject]@{{ + TimeCreated = $_.TimeCreated.ToUniversalTime().ToString('o') + Id = $_.Id + Message = $_.Message + Xml = $_.ToXml() + }} + }} +@($events) | ConvertTo-Json -Depth 6 -Compress +""" + out = run(["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps]) + result: dict[str, object] = { + "returncode": out["returncode"], + "stderr": out["stderr"], + } + stdout = str(out["stdout"]).strip() + if stdout: + try: + result["events"] = json.loads(stdout) + except json.JSONDecodeError: + result["raw_stdout"] = stdout + return result + + +def wevtutil_capture() -> dict[str, object]: + query = ( + "*[System[" + "(EventID=3033 or EventID=3076 or EventID=3077) and " + f"TimeCreated[@SystemTime>='{START_UTC}' and @SystemTime<='{END_UTC}']" + "]]" + ) + out = run([ + "wevtutil.exe", + "qe", + "Microsoft-Windows-CodeIntegrity/Operational", + f"/q:{query}", + "/f:xml", + "/e:Events", + ]) + result: dict[str, object] = { + "returncode": out["returncode"], + "stderr": out["stderr"], + } + raw = str(out["stdout"]) + matched: list[dict[str, object]] = [] + if raw.strip(): + try: + root = ET.fromstring(raw) + ns = {"e": "http://schemas.microsoft.com/win/2004/08/events/event"} + for event in root.findall("e:Event", ns): + xml_text = ET.tostring(event, encoding="unicode") + low = xml_text.lower() + if not any(n in low for n in NEEDLES): + continue + event_id = event.findtext("e:System/e:EventID", default="", namespaces=ns) + time_node = event.find("e:System/e:TimeCreated", ns) + system_time = time_node.attrib.get("SystemTime") if time_node is not None else None + data: dict[str, list[str]] = {} + for node in event.findall("e:EventData/e:Data", ns): + key = node.attrib.get("Name", "") + data.setdefault(key, []).append(node.text or "") + matched.append({ + "event_id": int(event_id) if event_id.isdigit() else event_id, + "system_time": system_time, + "event_data": data, + "xml": xml_text, + }) + result["events"] = matched + except ET.ParseError as exc: + result["parse_error"] = str(exc) + result["raw_stdout"] = raw + return result + + +def extract_attribution(events: list[dict[str, object]]) -> dict[str, object]: + blob = json.dumps(events, ensure_ascii=False).lower() + return { + "event_count": len(events), + "candidate_path_observed": str(CANDIDATE_DIR).lower() in blob or "b11295" in blob, + "server_exe_observed": "llama-server.exe" in blob, + "server_impl_observed": "llama-server-impl.dll" in blob, + "target_policy_id_observed": POLICY_ID.lower() in blob, + "event_3033_observed": any(str(e.get("event_id", e.get("Id", ""))) == "3033" for e in events), + "event_3076_observed": any(str(e.get("event_id", e.get("Id", ""))) == "3076" for e in events), + "event_3077_observed": any(str(e.get("event_id", e.get("Id", ""))) == "3077" for e in events), + } + + +def normalize_events(capture: dict[str, object]) -> list[dict[str, object]]: + events = capture.get("events", []) + if isinstance(events, dict): + return [events] + if isinstance(events, list): + return [e for e in events if isinstance(e, dict)] + return [] + + +def main() -> int: + primary = get_winevent_capture() + independent = wevtutil_capture() + + primary_events = normalize_events(primary) + independent_events = normalize_events(independent) + + payload = { + "schema_version": 1, + "operation": "phase7q-b11295-r1-closure-collector", + "candidate_cell": "PHASE7Q-ENV-R1", + "candidate_execution": False, + "model_invocation": False, + "runtime_replacement": False, + "policy_changes": False, + "file_changes": False, + "window": {"start_utc": START_UTC, "end_utc": END_UTC}, + "critical_members": { + "llama-server.exe": { + "path": str(SERVER), + "exists": SERVER.is_file(), + "bytes": SERVER.stat().st_size if SERVER.is_file() else None, + "sha256": sha256_file(SERVER), + "authenticode": authenticode(SERVER), + }, + "llama-server-impl.dll": { + "path": str(SERVER_IMPL), + "exists": SERVER_IMPL.is_file(), + "bytes": SERVER_IMPL.stat().st_size if SERVER_IMPL.is_file() else None, + "sha256": sha256_file(SERVER_IMPL), + "authenticode": authenticode(SERVER_IMPL), + }, + }, + "get_winevent": primary, + "get_winevent_attribution": extract_attribution(primary_events), + "wevtutil": independent, + "wevtutil_attribution": extract_attribution(independent_events), + } + + p_attr = payload["get_winevent_attribution"] + i_attr = payload["wevtutil_attribution"] + assert isinstance(p_attr, dict) + assert isinstance(i_attr, dict) + payload["independent_agreement"] = { + "candidate_path": bool(p_attr["candidate_path_observed"] and i_attr["candidate_path_observed"]), + "server_impl": bool(p_attr["server_impl_observed"] and i_attr["server_impl_observed"]), + "target_policy_id": bool(p_attr["target_policy_id_observed"] and i_attr["target_policy_id_observed"]), + "event_3077": bool(p_attr["event_3077_observed"] and i_attr["event_3077_observed"]), + } + + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_b11295_remediation_candidate_preflight.py b/tools/run_phase7q_b11295_remediation_candidate_preflight.py new file mode 100644 index 00000000..f8c58de7 --- /dev/null +++ b/tools/run_phase7q_b11295_remediation_candidate_preflight.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""Stage and inspect the exact Phase7Q b11295 remediation candidate. + +This script downloads and extracts the candidate into an isolated directory. +It does not execute candidate binaries, replace the frozen runtime, or modify policy. +""" + +from __future__ import annotations + +import hashlib +import json +import shutil +import subprocess +import zipfile +from pathlib import Path + + +RUNTIME_ROOT = Path(r"D:\projetos\codepro-mini-runtime") +STAGING_ROOT = RUNTIME_ROOT / "downloads" / "phase7q-remediation" / "b11295" +ARCHIVE = STAGING_ROOT / "llama-b11295-bin-win-cuda-13.4-x64.zip" +EXTRACT_DIR = STAGING_ROOT / "extracted" +URL = "https://github.com/ggml-org/llama.cpp/releases/download/b11295/llama-b11295-bin-win-cuda-13.4-x64.zip" +EXPECTED_SHA256 = "5F2EC28C4DED2986499D6B7B9DBD1FECB6EE7182208F3167C40E8EE3D585D1DC" + + +def sha256_file(path: Path) -> str: + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + h.update(chunk) + return h.hexdigest().upper() + + +def authenticode(path: Path) -> dict[str, object]: + ps = ( + "$s=Get-AuthenticodeSignature -LiteralPath " + + json.dumps(str(path)) + + "; [pscustomobject]@{Status=$s.Status.ToString();" + "StatusMessage=$s.StatusMessage;" + "SignerSubject=if($s.SignerCertificate){$s.SignerCertificate.Subject}else{$null};" + "SignerThumbprint=if($s.SignerCertificate){$s.SignerCertificate.Thumbprint}else{$null}}" + " | ConvertTo-Json -Compress" + ) + p = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + result: dict[str, object] = { + "returncode": p.returncode, + "stderr": p.stderr, + } + if p.stdout.strip(): + try: + result["result"] = json.loads(p.stdout) + except json.JSONDecodeError: + result["raw_stdout"] = p.stdout + return result + + +def main() -> int: + STAGING_ROOT.mkdir(parents=True, exist_ok=True) + + if not ARCHIVE.exists(): + p = subprocess.run( + ["curl.exe", "-L", "--fail", "--output", str(ARCHIVE), URL], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + if p.returncode != 0: + print(json.dumps({ + "schema_version": 1, + "operation": "phase7q-b11295-remediation-candidate-preflight", + "download_returncode": p.returncode, + "download_stderr": p.stderr, + "policy_changes": False, + "runtime_replacement": False, + "candidate_execution": False, + }, ensure_ascii=False, indent=2, sort_keys=True)) + return p.returncode + + archive_sha = sha256_file(ARCHIVE) + digest_match = archive_sha == EXPECTED_SHA256 + + if EXTRACT_DIR.exists(): + shutil.rmtree(EXTRACT_DIR) + EXTRACT_DIR.mkdir(parents=True, exist_ok=True) + + members: list[dict[str, object]] = [] + if digest_match: + with zipfile.ZipFile(ARCHIVE) as zf: + zf.extractall(EXTRACT_DIR) + + for path in sorted(EXTRACT_DIR.rglob("*")): + if not path.is_file(): + continue + row: dict[str, object] = { + "relative_path": str(path.relative_to(EXTRACT_DIR)), + "bytes": path.stat().st_size, + "sha256": sha256_file(path), + } + if path.suffix.lower() in {".exe", ".dll"}: + row["authenticode"] = authenticode(path) + members.append(row) + + payload = { + "schema_version": 1, + "operation": "phase7q-b11295-remediation-candidate-preflight", + "candidate_cell": "PHASE7Q-ENV-R1", + "candidate_tag": "b11295", + "candidate_commit": "3b3d022b823abaa62a467b26a44e10659e080ee7", + "archive": str(ARCHIVE), + "archive_bytes": ARCHIVE.stat().st_size if ARCHIVE.exists() else None, + "archive_sha256": archive_sha if ARCHIVE.exists() else None, + "expected_archive_sha256": EXPECTED_SHA256, + "archive_digest_match": digest_match, + "extract_dir": str(EXTRACT_DIR), + "members": members, + "policy_changes": False, + "runtime_replacement": False, + "candidate_execution": False, + "modifies_files": True, + "modification_scope": "isolated remediation staging directory only", + } + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if digest_match else 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_citool_policy_inventory.py b/tools/run_phase7q_citool_policy_inventory.py new file mode 100644 index 00000000..de12fc9f --- /dev/null +++ b/tools/run_phase7q_citool_policy_inventory.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Inventory active Code Integrity policies with CiTool without modifying policy state.""" + +from __future__ import annotations + +import json +import subprocess + + +TARGET_POLICY_ID = "{0283AC0F-FFF1-49AE-ADA1-8A933130CAD6}" + + +def main() -> int: + p = subprocess.run( + ["CiTool.exe", "-lp", "-json"], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + payload: dict[str, object] = { + "schema_version": 1, + "operation": "phase7q-citool-policy-inventory", + "policy_changes": False, + "returncode": p.returncode, + "returncode_hex": f"0x{p.returncode & 0xFFFFFFFF:08X}", + "stderr": p.stderr, + "target_policy_id": TARGET_POLICY_ID, + } + if (p.returncode & 0xFFFFFFFF) == 0x80070005: + payload["failure_class"] = "BLOCKED_BY_PRIVILEGE" + payload["failure_detail"] = "ACCESS_DENIED" + payload["inventory_established"] = False + if p.stdout.strip(): + try: + raw = json.loads(p.stdout) + policies = raw.get("Policies", []) if isinstance(raw, dict) else [] + target = [] + for row in policies: + if not isinstance(row, dict): + continue + policy_id = str(row.get("PolicyID", "")).upper() + if policy_id == TARGET_POLICY_ID: + target.append(row) + payload["target_policies"] = target + payload["target_match_count"] = len(target) + payload["active_enforced_policies"] = [ + { + key: row.get(key) + for key in ( + "PolicyID", + "BasePolicyID", + "FriendlyName", + "Version", + "IsSystemPolicy", + "IsSignedPolicy", + "IsOnDisk", + "IsEnforced", + "IsAuthorized", + ) + if key in row + } + for row in policies + if isinstance(row, dict) and str(row.get("IsEnforced", "")).lower() == "true" + ] + except json.JSONDecodeError: + payload["raw_stdout"] = p.stdout + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if p.returncode == 0 else p.returncode + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_elevation_and_direct_citool_diagnostic.py b/tools/run_phase7q_elevation_and_direct_citool_diagnostic.py new file mode 100644 index 00000000..57ce85f7 --- /dev/null +++ b/tools/run_phase7q_elevation_and_direct_citool_diagnostic.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Read-only diagnostics for elevation state and direct CiTool policy listing.""" + +from __future__ import annotations + +import json +import subprocess + + +def run(cmd: list[str]) -> dict[str, object]: + p = subprocess.run( + cmd, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + return { + "cmd": cmd, + "returncode": p.returncode, + "returncode_hex": f"0x{p.returncode & 0xFFFFFFFF:08X}", + "stdout": p.stdout, + "stderr": p.stderr, + } + + +def main() -> int: + whoami = run(["whoami", "/groups"]) + citool_help = run(["CiTool.exe", "-h"]) + citool_list = run(["CiTool.exe", "-lp", "-json"]) + groups_text = str(whoami.get("stdout", "")) + high_integrity = ("S-1-16-12288" in groups_text) or ("High Mandatory Level" in groups_text) or ("Nível Obrigatório Alto" in groups_text) + admin_deny_only = ("S-1-5-32-544" in groups_text) and ( + "deny only" in groups_text.lower() or "usado apenas para negar" in groups_text.lower() + ) + payload = { + "schema_version": 1, + "operation": "phase7q-elevation-and-direct-citool-diagnostic", + "policy_changes": False, + "file_changes": False, + "integrity_level_high": high_integrity, + "administrators_group_deny_only": admin_deny_only, + "effective_elevation": bool(high_integrity and not admin_deny_only), + "whoami_groups": whoami, + "citool_help": citool_help, + "citool_list_policies": citool_list, + } + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_frozen_runtime_provenance_preflight.py b/tools/run_phase7q_frozen_runtime_provenance_preflight.py new file mode 100644 index 00000000..97e1c073 --- /dev/null +++ b/tools/run_phase7q_frozen_runtime_provenance_preflight.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Inspect retained provenance for the frozen llama.cpp b11205 package without modifying files.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path + +ROOT = Path(r"D:\projetos\codepro-mini-runtime") +DOWNLOADS = ROOT / "downloads" +TARGET_DIR = DOWNLOADS / "llama-b11205-bin-win-cuda-13.4-x64" + + +def sha256(path: Path) -> str: + d = hashlib.sha256() + with path.open("rb") as h: + for chunk in iter(lambda: h.read(1024 * 1024), b""): + d.update(chunk) + return d.hexdigest().upper() + + +def row(path: Path) -> dict[str, object]: + item: dict[str, object] = { + "path": str(path.resolve()), + "name": path.name, + "is_file": path.is_file(), + "is_dir": path.is_dir(), + } + if path.is_file(): + item["bytes"] = path.stat().st_size + if path.stat().st_size <= 8 * 1024 * 1024 * 1024: + item["sha256"] = sha256(path) + item["zone_identifier_exists"] = Path(str(path) + ":Zone.Identifier").exists() + return item + + +def main() -> int: + candidates = [] + if DOWNLOADS.exists(): + for path in sorted(DOWNLOADS.iterdir()): + name = path.name.lower() + if "b11205" in name or "llama" in name or path.resolve() == TARGET_DIR.resolve(): + candidates.append(row(path)) + target_files = [] + if TARGET_DIR.exists(): + for path in sorted(TARGET_DIR.iterdir()): + target_files.append(row(path)) + print(json.dumps({ + "schema_version": 1, + "operation": "phase7q-frozen-runtime-provenance-preflight", + "modifies_files": False, + "downloads_root": str(DOWNLOADS), + "target_dir": str(TARGET_DIR), + "download_candidates": candidates, + "target_directory_entries": target_files, + }, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_frozen_runtime_signature_preflight.py b/tools/run_phase7q_frozen_runtime_signature_preflight.py new file mode 100644 index 00000000..71e57639 --- /dev/null +++ b/tools/run_phase7q_frozen_runtime_signature_preflight.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Inspect identity and Authenticode state of frozen llama.cpp runtime files without modifying them.""" + +from __future__ import annotations + +import hashlib +import json +import subprocess +from pathlib import Path + +RUNTIME_DIR = Path(r"D:\projetos\codepro-mini-runtime\downloads\llama-b11205-bin-win-cuda-13.4-x64") +FILES = ("llama-server.exe", "llama-server-impl.dll") + + +def sha256(path: Path) -> str: + d = hashlib.sha256() + with path.open("rb") as h: + for chunk in iter(lambda: h.read(1024 * 1024), b""): + d.update(chunk) + return d.hexdigest().upper() + + +def signature(path: Path) -> dict[str, object]: + ps = ( + "$s=Get-AuthenticodeSignature -LiteralPath " + + repr(str(path)) + + "; " + + "[pscustomobject]@{Status=$s.Status.ToString();StatusMessage=$s.StatusMessage;" + + "SignerSubject=if($s.SignerCertificate){$s.SignerCertificate.Subject}else{$null};" + + "SignerThumbprint=if($s.SignerCertificate){$s.SignerCertificate.Thumbprint}else{$null};" + + "TimeStamperSubject=if($s.TimeStamperCertificate){$s.TimeStamperCertificate.Subject}else{$null}}" + + " | ConvertTo-Json -Compress" + ) + p = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + out: dict[str, object] = {"returncode": p.returncode, "stderr": p.stderr} + if p.stdout.strip(): + try: + out["result"] = json.loads(p.stdout) + except json.JSONDecodeError: + out["raw_stdout"] = p.stdout + return out + + +def main() -> int: + rows = [] + for name in FILES: + path = (RUNTIME_DIR / name).resolve() + row: dict[str, object] = {"path": str(path), "exists": path.is_file()} + if path.is_file(): + row.update( + { + "bytes": path.stat().st_size, + "sha256": sha256(path), + "authenticode": signature(path), + "zone_identifier_exists": Path(str(path) + ":Zone.Identifier").exists(), + } + ) + rows.append(row) + print( + json.dumps( + { + "schema_version": 1, + "operation": "phase7q-frozen-runtime-signature-preflight", + "modifies_files": False, + "runtime_dir": str(RUNTIME_DIR), + "files": rows, + }, + ensure_ascii=False, + indent=2, + sort_keys=True, + ) + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_model_artifact_preflight.py b/tools/run_phase7q_model_artifact_preflight.py new file mode 100644 index 00000000..1dac23f9 --- /dev/null +++ b/tools/run_phase7q_model_artifact_preflight.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Inventory Phase 4 model artifacts and retained local evidence without executing a model.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path + +ROOT = Path(__file__).parents[1].resolve() +RUNTIME_ROOT = Path(r"D:\projetos\codepro-mini-runtime") +MODEL_ROOT = RUNTIME_ROOT / "models" / "phase4" +EVIDENCE_ROOT = ROOT / "evidence" / "phase4-model-compatibility" +TERMS = ("nanbeige", "qwen", "granite", "swe-dev", "swedev") + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest().upper() + + +def artifact_rows() -> list[dict[str, object]]: + rows: list[dict[str, object]] = [] + if not MODEL_ROOT.exists(): + return rows + for path in sorted(MODEL_ROOT.rglob("*.gguf")): + name_lower = path.name.lower() + hints = sorted({term for term in TERMS if term in name_lower}) + rows.append( + { + "path": str(path.resolve()), + "name": path.name, + "bytes": path.stat().st_size, + "sha256": sha256(path), + "candidate_hints": hints, + } + ) + return rows + + +def evidence_references() -> list[dict[str, object]]: + rows: list[dict[str, object]] = [] + if not EVIDENCE_ROOT.exists(): + return rows + for path in sorted(EVIDENCE_ROOT.rglob("*")): + if not path.is_file() or path.stat().st_size > 8 * 1024 * 1024: + continue + if path.suffix.lower() not in {".json", ".jsonl", ".txt", ".md", ".log", ".csv"}: + continue + try: + text = path.read_text(encoding="utf-8", errors="replace") + except OSError: + continue + lower = text.lower() + matched = sorted({term for term in TERMS if term in lower}) + if not matched: + continue + lines = text.splitlines() + excerpts: list[dict[str, object]] = [] + for index, line in enumerate(lines, start=1): + line_lower = line.lower() + if any(term in line_lower for term in matched): + excerpts.append({"line": index, "text": line[:1200]}) + if len(excerpts) >= 20: + break + rows.append( + { + "path": str(path.resolve()), + "matched_terms": matched, + "excerpts": excerpts, + } + ) + return rows + + +def main() -> int: + payload = { + "schema_version": 1, + "operation": "phase7q-model-artifact-preflight", + "execution": "NONE", + "model_invocation": False, + "runtime_root": str(RUNTIME_ROOT), + "model_root": str(MODEL_ROOT), + "evidence_root": str(EVIDENCE_ROOT), + "artifacts": artifact_rows(), + "evidence_references": evidence_references(), + } + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_runtime_archive_member_linkage.py b/tools/run_phase7q_runtime_archive_member_linkage.py new file mode 100644 index 00000000..983387f4 --- /dev/null +++ b/tools/run_phase7q_runtime_archive_member_linkage.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Compare retained b11205 ZIP members against current extracted runtime files without modifying anything.""" + +from __future__ import annotations + +import hashlib +import io +import json +from pathlib import Path +from zipfile import ZipFile + +ROOT = Path(r"D:\projetos\codepro-mini-runtime\downloads") +ARCHIVE = ROOT / "llama-b11205-bin-win-cuda-13.4-x64.zip" +EXTRACTED = ROOT / "llama-b11205-bin-win-cuda-13.4-x64" +TARGETS = ("llama-server.exe", "llama-server-impl.dll") + + +def sha256_file(path: Path) -> str: + d = hashlib.sha256() + with path.open("rb") as h: + for chunk in iter(lambda: h.read(1024 * 1024), b""): + d.update(chunk) + return d.hexdigest().upper() + + +def sha256_bytes(data: bytes) -> str: + return hashlib.sha256(data).hexdigest().upper() + + +def main() -> int: + payload: dict[str, object] = { + "schema_version": 1, + "operation": "phase7q-runtime-archive-member-linkage", + "modifies_files": False, + "archive": str(ARCHIVE), + "archive_exists": ARCHIVE.is_file(), + "extracted_dir": str(EXTRACTED), + "targets": [], + } + if not ARCHIVE.is_file(): + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 2 + payload["archive_sha256"] = sha256_file(ARCHIVE) + with ZipFile(ARCHIVE, "r") as zf: + names = zf.namelist() + rows = [] + for target in TARGETS: + matches = [name for name in names if Path(name).name.lower() == target.lower()] + extracted = EXTRACTED / target + row: dict[str, object] = { + "target": target, + "zip_matches": matches, + "extracted_exists": extracted.is_file(), + } + if len(matches) == 1: + data = zf.read(matches[0]) + row["zip_member"] = matches[0] + row["zip_member_bytes"] = len(data) + row["zip_member_sha256"] = sha256_bytes(data) + if extracted.is_file(): + row["extracted_bytes"] = extracted.stat().st_size + row["extracted_sha256"] = sha256_file(extracted) + row["byte_identity_match"] = ( + row.get("zip_member_sha256") is not None + and row.get("zip_member_sha256") == row.get("extracted_sha256") + ) + rows.append(row) + payload["targets"] = rows + payload["all_targets_match"] = all(bool(row.get("byte_identity_match")) for row in rows) + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if payload["all_targets_match"] else 3 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_s4_l1_q1_nanbeige42.py b/tools/run_phase7q_s4_l1_q1_nanbeige42.py new file mode 100644 index 00000000..27d539ff --- /dev/null +++ b/tools/run_phase7q_s4_l1_q1_nanbeige42.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Run the controlled Phase 7R Phase 7Q S4-L1-Q1 scaffold/model pair cell.""" + +from __future__ import annotations + +from pathlib import Path +import sys + +ROOT = Path(__file__).parents[1].resolve() +if str(ROOT / "tools") not in sys.path: + sys.path.insert(0, str(ROOT / "tools")) + +import run_phase7_s4_openhands as s4 # noqa: E402 + +BASE_EVIDENCE = ROOT / "evidence" / "phase7r-s4-runtime-requalification" / "S4-L1-Nanbeige42-parallel1-litellm1943-ctx16384-timeout600-winps2-inlineprompt2-maxiter10-single-workspace" + + +def next_evidence_dir() -> Path: + if not BASE_EVIDENCE.exists() or not any(BASE_EVIDENCE.iterdir()): + return BASE_EVIDENCE + attempt = 2 + while True: + candidate = BASE_EVIDENCE.with_name(f"{BASE_EVIDENCE.name}-attempt-{attempt}") + if not candidate.exists() or not any(candidate.iterdir()): + return candidate + attempt += 1 + + +if __name__ == "__main__": + evidence = next_evidence_dir() + print(f"S4_L1_Q1_EVIDENCE_DIR = {evidence}") + raise SystemExit( + s4.main( + [ + "--evidence-dir", + str(evidence), + "--study-id", + "phase7q-s4-l1-q1-nanbeige42-pair", + "--llama-parallel", + "1", + "--llama-context", + "16384", + "--litellm-version", + "1.94.3", + "--conversation-timeout-seconds", + "600", + "--executor-timeout-seconds", + "660", + "--vertical-wall-time-seconds", + "720", + "--platform-contract", + "windows-powershell-v2", + "--system-prompt-profile", + "windows-embedded-v1", + "--conversation-worktree", + "false", + "--cleanup-untracked-python-bytecode", + "true", + "--max-iterations", + "10", + "--model-path", + r"D:\\projetos\\codepro-mini-runtime\\models\\phase4\\L1-Nanbeige4.2-3B\\Nanbeige4.2-3B-Q4_K_M-mainline-e31e82d.gguf", + "--model-bytes", + "2574807872", + "--model-sha256", + "FBCE43B8977DD73A86126E85AE0BFE219EF3131498AE7C37571DEA58F3C6AA34", + "--model-alias", + "codepro-phase7q-nanbeige42-3b", + ] + ) + ) diff --git a/tools/run_phase7q_windows_application_control_diagnostic.py b/tools/run_phase7q_windows_application_control_diagnostic.py new file mode 100644 index 00000000..6c8cb113 --- /dev/null +++ b/tools/run_phase7q_windows_application_control_diagnostic.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Diagnose Windows Application Control blocks relevant to Phase 7Q without changing policy.""" + +from __future__ import annotations + +import json +import subprocess +from datetime import datetime, timedelta, timezone + +CHANNEL = "Microsoft-Windows-CodeIntegrity/Operational" +EVENT_IDS = (3033, 3076, 3077) + + +def main() -> int: + since = (datetime.now(timezone.utc) - timedelta(hours=6)).isoformat() + ps = rf""" +$start = [DateTime]::Parse('{since}').ToLocalTime() +$events = Get-WinEvent -FilterHashtable @{{LogName='{CHANNEL}'; StartTime=$start}} -ErrorAction SilentlyContinue | + Where-Object {{ $_.Id -in {','.join(str(x) for x in EVENT_IDS)} }} | + Select-Object -First 100 TimeCreated, Id, LevelDisplayName, Message, RecordId +$events | ConvertTo-Json -Depth 4 -Compress +""" + proc = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", ps], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + payload = { + "schema_version": 1, + "operation": "phase7q-windows-application-control-diagnostic", + "policy_changes": False, + "channel": CHANNEL, + "event_ids": list(EVENT_IDS), + "powershell_returncode": proc.returncode, + "stderr": proc.stderr, + "events": [], + } + if proc.stdout.strip(): + try: + value = json.loads(proc.stdout) + payload["events"] = value if isinstance(value, list) else [value] + except json.JSONDecodeError: + payload["raw_stdout"] = proc.stdout + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if proc.returncode == 0 else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_phase7q_windows_application_control_posture.py b/tools/run_phase7q_windows_application_control_posture.py new file mode 100644 index 00000000..f8da121c --- /dev/null +++ b/tools/run_phase7q_windows_application_control_posture.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Inspect Windows application-control posture without changing policy or files.""" + +from __future__ import annotations + +import json +import subprocess + + +def run_ps(script: str) -> dict[str, object]: + p = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", script], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + out: dict[str, object] = {"returncode": p.returncode, "stderr": p.stderr} + if p.stdout.strip(): + try: + out["result"] = json.loads(p.stdout) + except json.JSONDecodeError: + out["raw_stdout"] = p.stdout + return out + + +def main() -> int: + device_guard = run_ps(r""" +$dg = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction SilentlyContinue +$dg | Select-Object InstanceIdentifier,Version,VirtualizationBasedSecurityStatus,CodeIntegrityPolicyEnforcementStatus,UsermodeCodeIntegrityPolicyEnforcementStatus,SecurityServicesConfigured,SecurityServicesRunning,SecurityFeaturesEnabled,AvailableSecurityProperties,RequiredSecurityProperties | ConvertTo-Json -Depth 3 -Compress +""") + ci_policies = run_ps(r""" +$paths = @( + "$env:windir\System32\CodeIntegrity\CiPolicies\Active", + "$env:windir\System32\CodeIntegrity" +) +$items = foreach($p in $paths) { + if(Test-Path -LiteralPath $p) { + Get-ChildItem -LiteralPath $p -Force -File -ErrorAction SilentlyContinue | + Select-Object FullName,Name,Length,LastWriteTimeUtc + } +} +$items | ConvertTo-Json -Depth 4 -Compress +""") + registry = run_ps(r""" +$paths = @( + 'HKLM:\SYSTEM\CurrentControlSet\Control\CI\Policy', + 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard', + 'HKLM:\SOFTWARE\Microsoft\Windows Defender\SmartScreen' +) +$out = foreach($p in $paths) { + if(Test-Path $p) { + $item = Get-ItemProperty -Path $p + $values = [ordered]@{} + foreach($prop in $item.PSObject.Properties) { + if($prop.Name -notlike 'PS*') { + $values[$prop.Name] = $prop.Value + } + } + [pscustomobject]@{Path=$p; Values=$values} + } +} +$out | ConvertTo-Json -Depth 4 -Compress +""") + payload = { + "schema_version": 1, + "operation": "phase7q-windows-application-control-posture", + "policy_changes": False, + "file_changes": False, + "device_guard": device_guard, + "code_integrity_policy_files": ci_policies, + "registry": registry, + } + print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())