From c114b3e76a36bbea8b0a358a7aeb47695aecf374 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:21:19 +0330 Subject: [PATCH 001/188] ci(linux): validate unreleased v1.5.5-rc1 against pinned v1.5.4 stable Stage 23 reviewed text-source changes on a candidate-only branch. Require deterministic rc1 ZIP SHA, negative-first Linux upgrade tests, rootless encrypted DNS safety tests, separate historic v1.5.4 regression, and unchanged Windows/public-release contracts. No release or install. --- .github/workflows/ci.yml | 153 ++--- build_linux_package.py | 20 +- docs/ENCRYPTED_DNS_R516.md | 95 +++ docs/README_PACKAGE_155_RC1.md | 41 ++ linux/app/direct_internet_method.py | 83 ++- linux/app/direct_method_helper.sh | 68 +- linux/app/user_uninstall_preserve.py | 2 +- linux/app/user_update_transaction.py | 19 +- linux/install.sh | 50 +- linux/system/encrypted_selective_dns.py | 568 +++++++++++++++++ linux/system/encrypted_selective_policy.py | 125 ++++ linux/system/install_system.sh | 23 +- linux/system/protected_upgrade_transaction.py | 363 +++++++++++ linux/system/uninstall_system.sh | 11 + linux/system/upgrade_system.sh | 45 ++ ...ypted_selective_dns_controller_contract.py | 339 ++++++++++ .../encrypted_selective_dns_fake_lifecycle.py | 238 +++++++ ...pted_selective_dns_gui_consent_contract.py | 113 ++++ ...pted_selective_dns_integration_contract.py | 128 ++++ ...encrypted_selective_dns_policy_contract.py | 89 +++ .../protected_upgrade_transaction_contract.py | 241 +++++++ .../r519_user_update_transaction_contract.py | 587 ++++++++++++++++++ tests/r519_versioned_package_contract.py | 143 +++++ 23 files changed, 3409 insertions(+), 135 deletions(-) create mode 100644 docs/ENCRYPTED_DNS_R516.md create mode 100644 docs/README_PACKAGE_155_RC1.md create mode 100644 linux/system/encrypted_selective_dns.py create mode 100644 linux/system/encrypted_selective_policy.py create mode 100644 linux/system/protected_upgrade_transaction.py create mode 100644 linux/system/upgrade_system.sh create mode 100644 tests/encrypted_selective_dns_controller_contract.py create mode 100644 tests/encrypted_selective_dns_fake_lifecycle.py create mode 100644 tests/encrypted_selective_dns_gui_consent_contract.py create mode 100644 tests/encrypted_selective_dns_integration_contract.py create mode 100644 tests/encrypted_selective_dns_policy_contract.py create mode 100644 tests/protected_upgrade_transaction_contract.py create mode 100644 tests/r519_user_update_transaction_contract.py create mode 100644 tests/r519_versioned_package_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c6670ff..77c0ede4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,113 +18,96 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Python syntax - run: python3 -m py_compile build_linux_package.py linux/app/direct_internet_method.py linux/system/selective_dns.py linux/app/router_gateway.py tests/selective_dns_integration_safety_contract.py tests/selective_dns_monitor_lock_contract.py tests/selective_dns_full_lifecycle_mock.py tests/selective_dns_bindings_contract.py tests/selective_dns_backstop_contract.py tests/selective_dns_backstop_runtime_mock.py tests/linux_contract_audit.py tests/linux_user_update_transaction_contract.py tests/linux_real_extracted_archive_contract.py linux/app/user_update_transaction.py linux/app/user_uninstall_preserve.py tests/adult_coverage_data_audit.py tests/router_gateway_contract_audit.py tests/router_gateway_runtime_smoke.py tests/rejected_component_audit.py tests/linux_installed_152_lifecycle.py tests/tailscale_coexistence_contract.py tests/tailscale_ui_state_contract.py tests/tailscale_gui_scope_guard_contract.py tests/tailscale_rustdesk_transport_audit.py tests/tailscale_nft_ownership_contract.py tests/tailscale_cleanup_ownership_contract.py tests/tailscale_recovery_missing_state_contract.py tests/nft_presence_fail_closed_contract.py tests/tailscale_transient_rollback_ownership_contract.py tests/linux_nft_kernel_namespace_contract.py tests/linux_mutation_action_lock_contract.py tests/linux_rollback_exit_integrity_contract.py - - name: Shell syntax and LF guard + - name: Candidate source syntax and shell safety shell: bash run: | set -euo pipefail - while IFS= read -r f; do - if grep -q $'\r' "$f"; then - echo "CRLF detected in $f" >&2 + python3 -m compileall -q build_linux_package.py linux/app linux/system tests + while IFS= read -r path; do + if grep -q $'\r' "$path"; then + echo "CRLF in candidate shell: $path" >&2 exit 1 fi - bash -n "$f" - done < <(find linux -type f -name '*.sh' -print | sort) - - name: Linux and Router Gateway contract audits - run: | - python3 tests/linux_contract_audit.py - python3 tests/linux_user_update_transaction_contract.py - python3 tests/tailscale_coexistence_contract.py - python3 tests/selective_dns_optin_contract.py - python3 tests/selective_dns_isolated_runtime.py - python3 tests/selective_dns_restore_mock_contract.py - python3 tests/selective_dns_gui_optin_contract.py - python3 tests/selective_dns_integration_safety_contract.py - python3 tests/selective_dns_monitor_lock_contract.py - python3 tests/selective_dns_full_lifecycle_mock.py - python3 tests/selective_dns_bindings_contract.py - python3 tests/selective_dns_backstop_contract.py - python3 tests/selective_dns_backstop_runtime_mock.py - python3 tests/tailscale_ui_state_contract.py - python3 tests/tailscale_gui_scope_guard_contract.py - python3 tests/tailscale_rustdesk_transport_audit.py --selftest - python3 tests/tailscale_nft_ownership_contract.py - python3 tests/tailscale_cleanup_ownership_contract.py - python3 tests/tailscale_recovery_missing_state_contract.py - python3 tests/nft_presence_fail_closed_contract.py - python3 tests/tailscale_transient_rollback_ownership_contract.py - python3 tests/linux_nft_kernel_namespace_contract.py --source-only - python3 tests/linux_mutation_action_lock_contract.py - python3 tests/linux_rollback_exit_integrity_contract.py - python3 tests/adult_coverage_data_audit.py - python3 tests/router_gateway_contract_audit.py - python3 tests/release_metadata_audit.py - python3 tests/release_publication_negative_regression.py - python3 tests/unsigned_public_release_contract.py --fixtures - python3 tests/rejected_component_audit.py - - name: Isolated real-kernel nftables contract - shell: bash - run: | - set -euo pipefail - # Reject direct execution against the host network namespace. - # The contract checks an empty isolated loopback-only namespace. - command -v unshare - command -v ip - if ! command -v nft >/dev/null 2>&1; then - sudo apt-get update -qq - sudo apt-get install -y nftables - fi - sudo -n python3 tests/linux_nft_kernel_namespace_contract.py --kernel - - name: Build Linux archive + bash -n "$path" + done < <(find linux -type f -name '*.sh' | sort) + - name: Build deterministic Linux 1.5.5-rc1 package run: python3 build_linux_package.py - - name: Validate real extracted ZIP transaction and user-data rollback - run: python3 -B tests/linux_real_extracted_archive_contract.py - - name: Verify exact Linux candidate artifact digest + - name: Exact candidate SHA and non-release authority gate shell: bash run: | set -euo pipefail python3 - <<'PY' import hashlib,json,pathlib,zipfile - recorded=json.loads(pathlib.Path("RELEASE.json").read_text(encoding="utf-8")) - old=recorded["platforms"]["linux"] - # RELEASE.json pins the R467 unpublished new candidate artifact; - # a local build alone is NOT a public installed acceptance or permission to publish. - assert old["sha256"].upper()=="4784FCD44ED3D1B40103BCA140B2C0066784F794D07C3AE1F525334766A553C7" - assert recorded["version"]=="1.5.4" and recorded["status"]=="CANDIDATE_V154_UNPUBLISHED_UNPROVEN" - p=pathlib.Path(old["artifact"]) - assert p.is_file(), "NEW_CANDIDATE_ARCHIVE_MISSING" - actual=hashlib.sha256(p.read_bytes()).hexdigest().upper() - expected="4784FCD44ED3D1B40103BCA140B2C0066784F794D07C3AE1F525334766A553C7" - assert actual==expected, f"NEW_LINUX_CANDIDATE_SHA_MISMATCH {actual} != {expected}" - assert p.stat().st_size==8270220, "NEW_LINUX_CANDIDATE_BYTES_WRONG" - assert actual==old["sha256"].upper(), "R467_CANDIDATE_SOURCE_DIGEST_MISMATCH" + p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") + expected="45A29A65B3E34302AD8E0745324EC2A1C418BB43669F9B95B15963B02CDFCC49" + assert p.is_file() and p.stat().st_size==8334126 + assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected + meta=json.loads(pathlib.Path("RELEASE.json").read_text()) + assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" with zipfile.ZipFile(p) as z: - assert z.testzip() is None and len(z.namelist())==24 - meta_name=next(x for x in z.namelist() if x.endswith("PACKAGE_MANIFEST.json")) - manifest=json.loads(z.read(meta_name)) - assert len(manifest["files"])==22 - prefix=meta_name[:-len("PACKAGE_MANIFEST.json")] - for row in manifest["files"]: - data=z.read(prefix+row["file"]) - assert len(data)==row["bytes"] - assert hashlib.sha256(data).hexdigest().upper()==row["sha256"] - print("PASS R486 VERSIONED DOTNET10 LINUX CANDIDATE, V152 PUBLIC HISTORICAL UNCHANGED", actual) + root="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" + assert len(z.namelist())==28 and len(z.namelist())==len(set(z.namelist())) + j=json.loads(z.read(root+"PACKAGE_MANIFEST.json")) + assert j["version"]=="1.5.5-rc1" and len(j["files"])==26 + for x in j["files"]: + data=z.read(root+x["file"]) + assert len(data)==x["bytes"] + assert hashlib.sha256(data).hexdigest().upper()==x["sha256"] + assert z.testzip() is None + print("R520_EXACT_155RC1_PACKAGE_AND_PUBLIC_V154_AUTHORITY_PASS",expected) PY - - name: Validate shell syntax inside built archive + - name: Negative-first R519 candidate package and protected upgrade + run: | + set -euo pipefail + python3 -B tests/r519_versioned_package_contract.py + python3 -B tests/protected_upgrade_transaction_contract.py + python3 -B tests/r519_user_update_transaction_contract.py + - name: R517 encrypted DNS and Tailscale negative-first contracts + run: | + set -euo pipefail + python3 -B tests/encrypted_selective_dns_policy_contract.py + python3 -B tests/encrypted_selective_dns_controller_contract.py + python3 -B tests/encrypted_selective_dns_fake_lifecycle.py + python3 -B tests/encrypted_selective_dns_gui_consent_contract.py + python3 -B tests/encrypted_selective_dns_integration_contract.py + python3 -B tests/tailscale_coexistence_contract.py + python3 -B tests/selective_dns_optin_contract.py + - name: Package embedded shell and source-mode integrity shell: bash run: | set -euo pipefail d="$(mktemp -d)" trap 'rm -rf "$d"' EXIT - unzip -q delivery/DirectInternetMethod_1.5.4_Linux_x86_64.zip -d "$d" + unzip -q delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip -d "$d" while IFS= read -r f; do + bash -n "$f" if grep -q $'\r' "$f"; then - echo "CRLF detected in packaged shell: $f" >&2 + echo "UNSAFE_PACKAGE_CRLF:$f" >&2 exit 1 fi - bash -n "$f" - done < <(find "$d" -type f -name '*.sh' -print | sort) + done < <(find "$d" -type f -name '*.sh' | sort) + + linux-public-v154-regression: + name: linux-public-v154-regression + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: 51009372e3c2d069d7016b97773f35ee5e1e0631 + - name: Preserve original stable Linux contracts at exact tag + shell: bash + run: | + set -euo pipefail + test "$(git rev-parse HEAD)" = "51009372e3c2d069d7016b97773f35ee5e1e0631" + python3 -B tests/linux_contract_audit.py + python3 -B tests/linux_user_update_transaction_contract.py + python3 -B tests/selective_dns_optin_contract.py + python3 -B tests/selective_dns_gui_optin_contract.py + python3 -B tests/selective_dns_integration_safety_contract.py + python3 -B tests/tailscale_coexistence_contract.py + python3 -B tests/release_metadata_audit.py + python3 build_linux_package.py + python3 -B tests/linux_real_extracted_archive_contract.py windows-source: name: windows-source diff --git a/build_linux_package.py b/build_linux_package.py index c6812adc..c1d8eba4 100644 --- a/build_linux_package.py +++ b/build_linux_package.py @@ -2,15 +2,19 @@ from __future__ import annotations import hashlib,json,pathlib,zipfile,tempfile,os R=pathlib.Path(__file__).resolve().parent -OUT=R/"delivery"/"DirectInternetMethod_1.5.4_Linux_x86_64.zip" -ROOT="DirectInternetMethod_1.5.4_Linux_x86_64" +OUT=R/"delivery"/"DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" +ROOT="DirectInternetMethod_1.5.5-rc1_Linux_x86_64" FILES=[ - ("docs/README_PACKAGE_154.md","README.md"), + ("docs/README_PACKAGE_155_RC1.md","README.md"), ("linux/install.sh","install.sh"), ("linux/uninstall.sh","uninstall.sh"), ("linux/system/install_system.sh","system/install_system.sh"), ("linux/system/control.sh","system/control.sh"), ("linux/system/selective_dns.py","system/selective_dns.py"), + ("linux/system/encrypted_selective_dns.py","system/encrypted_selective_dns.py"), + ("linux/system/encrypted_selective_policy.py","system/encrypted_selective_policy.py"), + ("linux/system/protected_upgrade_transaction.py","system/protected_upgrade_transaction.py"), + ("linux/system/upgrade_system.sh","system/upgrade_system.sh"), ("linux/system/uninstall_system.sh","system/uninstall_system.sh"), ("linux/app/direct_internet_method.py","app/direct_internet_method.py"), ("linux/app/router_gateway.py","app/router_gateway.py"), @@ -36,15 +40,15 @@ def payload_bytes(src:str,dst:str)->bytes: data=data.replace(b"\r\n",b"\n").replace(b"\r",b"\n") return data # Frozen originally shipped documentation; root README may describe current publication. -if sha_bytes(payload_bytes("docs/README_PACKAGE_154.md","README.md"))!="4E17DB910024D0D5841857928C45AD0EC2C5D65D30B3311FBE1864F1885AC84F": - raise SystemExit("V154_PACKAGE_README_SHA_CHANGED") +if sha_bytes(payload_bytes("docs/README_PACKAGE_155_RC1.md","README.md"))!="2E2D817FD4728903E7085C4D7D02786107206881DA47FE4DFA6AA3C6F4E4AF76": + raise SystemExit("R519_PACKAGE_README_SHA_CHANGED") rows=[] for src,dst in FILES: p=R/src if not p.is_file(): raise SystemExit("MISSING:"+src) data=payload_bytes(src,dst) rows.append({"file":dst,"bytes":len(data),"sha256":sha_bytes(data)}) -manifest={"schema":1,"product":"Direct Internet Method","version":"1.5.4","platform":"linux-x86_64","files":rows} +manifest={"schema":1,"product":"Direct Internet Method","version":"1.5.5-rc1","platform":"linux-x86_64","files":rows} OUT.parent.mkdir(parents=True,exist_ok=True) fd,tmp_name=tempfile.mkstemp(prefix="dim-build-",suffix=".zip",dir=OUT.parent) os.close(fd) @@ -53,7 +57,7 @@ def payload_bytes(src:str,dst:str)->bytes: with zipfile.ZipFile(tmp,"w",compression=zipfile.ZIP_STORED) as z: for src,dst in sorted(FILES,key=lambda x:x[1]): data=payload_bytes(src,dst) - zi=zipfile.ZipInfo(f"{ROOT}/{dst}",date_time=(2026,10,1,0,0,0)) + zi=zipfile.ZipInfo(f"{ROOT}/{dst}",date_time=(2026,10,14,0,0,0)) zi.create_system=3 zi.external_attr=((0o755 if dst.endswith((".sh",".py")) or dst in ("runtime/ctrld","runtime/nfqws") else 0o644)&0xFFFF)<<16 zi.compress_type=zipfile.ZIP_STORED @@ -63,7 +67,7 @@ def payload_bytes(src:str,dst:str)->bytes: ("SHA256SUMS.txt","".join(f'{x["sha256"]} {x["file"]}\n' for x in rows)) ]: data=(json.dumps(obj,ensure_ascii=False,indent=2)+"\n").encode() if isinstance(obj,dict) else obj.encode() - zi=zipfile.ZipInfo(f"{ROOT}/{name}",date_time=(2026,10,1,0,0,0)) + zi=zipfile.ZipInfo(f"{ROOT}/{name}",date_time=(2026,10,14,0,0,0)) zi.create_system=3 zi.external_attr=(0o644&0xFFFF)<<16;zi.compress_type=zipfile.ZIP_STORED z.writestr(zi,data) diff --git a/docs/ENCRYPTED_DNS_R516.md b/docs/ENCRYPTED_DNS_R516.md new file mode 100644 index 00000000..0faa4f1c --- /dev/null +++ b/docs/ENCRYPTED_DNS_R516.md @@ -0,0 +1,95 @@ +# R516 — encrypted selective DNS candidate (NOT RELEASED) + +**Project:** Direct Internet Method; source authority is exact GitHub main commit +`26ef644117ea474b487186ce5e6e61e92cd02dd4`. Existing public +Stable remains **v1.5.4**. The last cumulative Project Brain accepted is R514 +SHA-256 `DF4D5EFD2F412D344160745491BC5F812DCD03AF8D89B3B21FC5644D9C229300`. + +## Reason and evidence + +The installed Linux `linux/system/selective_dns.py` has an explicitly +opted-in but **unencrypted** UDP upstream `194.225.152.10` routed via +`dnsmasq`. It does not meet the project's encrypted-DNS requirement +under observed DNS interception. + +R511 and R513 used the already bundled `ctrld v1.5.7` with verified TLS +DoH `https://76.76.10.11/p0` on bounded per-link DNS pilots. R516 added +a narrower localhost-only source-policy test (non-root, MMZ Linux on +2026-10-14) on `127.0.0.1:53534` with `systemd-run --user` and a 90-second +`RuntimeMaxSec`; system DNS, Tailscale and the Direct Method app were not +changed. Exact runtime TOML SHA-256: +`46F63805FE53B5CEDC6B7D4DF242BBF13A6CA8229BB9072058BE8DE304697954`. + +Observed R516 localhost DNS: +- `www.youtube.com`: public IP addresses `142.251.150.4` through + `142.251.157.4`, no RFC1918 addresses; +- `www.googlevideo.com`: public `142.251.141.36`; +- `github.com`: public `140.82.121.3`, same as router; +- `example.com`: public Cloudflare IPs; +- direct query against original `192.168.8.1` returned forged/private + `10.10.34.36` for YouTube. +The temporary user unit exited or was stopped and the listener was absent, +with DNS and default route/Tailscale unchanged. + +Independent repeat on Emad Linux (router `192.168.20.1`, `127.0.0.1:53535`) +generated **byte-exact** source TOML SHA-256 +`3CC90962571AC80220B1A947C901709C64CD0DA0BD63EE5BEB6D1EC8781615DC`. +The DoH path gave legitimate public YouTube and Googlevideo addresses, +and `github.com` matched the physical router. **`example.com` timed out +because this non-target request intentionally fell back to Emad's unreliable +router DNS.** Thus the targeted model demonstrably protects listed domains; +it does NOT repair arbitrary domains, which require an explicit broader opt-in. +The user-bounded test service stopped, with physical DNS, Tailscale and +Direct Method unchanged. On Emad, the separate Seman L2TP connection remained +user-controlled and was not used to infer safe combined product mode. + +Both routers' rendered Policy bytes are pinned in the **46/46 offline +negative-first tests**, and all invalid resolver addresses and unsafe ports +fail closed. MMZ direct router YouTube answers were private during the test; +Emad's physical router responses vary over time, so one public answer from +that router does not establish reliable default DNS. + +The pure source policy generator in +`linux/system/encrypted_selective_policy.py` emits **byte-exact** TOML +for the validated MMZ router and temporary listener port. It has negative-first +rootless contracts in `tests/encrypted_selective_dns_policy_contract.py`. +No installer, root helper, UI, upgrade flow or published binary is wired +to this policy in R516. + +## Acceptance and open gates + +**PASS:** rootless isolated policy, exact runtime sample, encrypted target +DoH with real public A, original router fallback for non-target names and +bounded cleanup without product/system DNS/Tailscale changes. + +**OPEN:** supervised integration into one protected updater and GUI; +opt-in token migration from *unencrypted* to *encrypted* (never silently +reinterpret consent); native per-link route-only recovery/backstop with +systemd-resolved; tests on Ubuntu 26 under actual installed user/service; +one-install update/rollback and hosted exact-SHA CI; selected-site HTTPS +access (MMZ YouTube still curl35); Seman concurrent VPN policy and +service/region HTTP403. + +The generator must not be silently substituted into v1.5.4's old +`dnsmasq` controller, because the controller's assumptions (runtime +binary, state schema, consent, rollback) differ. **SUPERSEDED—DO NOT RUN** +includes older temporary R510/R512/R513/R514 and independently completed +R515 scripts; preserve previous Brain and historical raw files. + +## Next exact action + +In a separate transaction, design and negative-test an opt-in encrypted +controller that owns a disposable per-link resolver, enforces route-only +domains and exposes an independent bounded rollback before touching DNS. +Then modify the protected installer and GUI together in a versioned release +candidate; require a clean in-place upgrade path, native V&V, CI and +explicit promotion gates. Do not publish merely because the pure policy +and DNS lookups PASS. + +Sources: +- Control D ctrld v1.5.7 configuration reference, policy precedence + (rules before networks): https://github.com/Control-D-Inc/ctrld/blob/v1.5.7/docs/config.md +- Ubuntu systemd-resolved D-Bus SetLinkDNSEx/SetLinkDomains/RevertLink: + https://manpages.ubuntu.com/manpages/resolute/man5/org.freedesktop.resolve1.5.html +- Public v1.5.4 release: + https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 diff --git a/docs/README_PACKAGE_155_RC1.md b/docs/README_PACKAGE_155_RC1.md new file mode 100644 index 00000000..724bad53 --- /dev/null +++ b/docs/README_PACKAGE_155_RC1.md @@ -0,0 +1,41 @@ +# Direct Internet Method 1.5.5-rc1 — Linux candidate (NOT RELEASED) + +This archive is a versioned development candidate for upgrading the existing +single-install Linux app from public **v1.5.4**. It is not a public Stable +release and must not be offered as an automatic update. + +## Scope +- Existing v1.5.4 settings, custom hosts, Router Gateway provider and + Tailscale split mode must remain intact. +- Explicit NEW consent is required for encrypted selective DNS. Previous + unencrypted third-party UDP opt-in does **not** count as consent. +- Pinned ctrld v1.5.7 DoH upstream routes only documented target domains + through an owned per-link resolver. Non-target DNS stays with the user's + physical DNS server, which may still time out or be intercepted. +- The root backend upgrade from installed protected v1.5.2 is a single-slot + accepted-SHA transaction with an independent Rollback; it is **not** + authorized for promotion until installed-native QA and hosted CI. +- Running a new root backend without an accepted user/backend pair is + forbidden. The candidate's installer must fail closed if either side is + foreign, active, or has an uncommitted conflicting transaction. + +## Known limitations / open acceptance gates +The R516 targeted DNS pilot succeeded on two different LANs, and the R517 +controller passed source-only and simulated rollback tests; these do not +demonstrate a successful upgrade, protection after reboot, a working +per-link resolver under live root, or access to every website. +YouTube HTTPS on the MMZ mobile ISP reset TLS even after native balanced +DPI, with a separate Strong test's root-only receipts unavailable. Seman +L2TP combined with Tailscale is not an accepted mode. Some services may +still return a region/account-specific HTTP 403. + +Do not delete backups, auto-promote, disable Tailscale/Seman, reset system +DNS, reboot or shutdown as part of installation. +A native acceptance record must prove exact SHA, data preservation, service +Start/Stop/Recovery, rollback, Tailscale and physical DNS invariants before +release. + +Source authority: GOD13emad/DirectInternetMethod, main commit +26ef644117ea474b487186ce5e6e61e92cd02dd4, versioned R519 branch. +Public release authority remains v1.5.4 (tag +51009372e3c2d069d7016b97773f35ee5e1e0631). diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index f7d797e4..22911880 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -22,7 +22,7 @@ from router_gateway import RouterGatewayWindow APP_ID="io.github.god13emad.DirectInternetMethod" -VERSION="1.5.4" +VERSION="1.5.5-rc1" GLib.set_prgname("DirectInternetMethod") GLib.set_application_name("Direct Internet Method") try: @@ -46,7 +46,9 @@ SCOPE_FILE=APP_HOME/"scope.txt" SELECTIVE_DNS_OPT_IN=APP_HOME/"selective-dns-optin.txt" SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="e52fcaeb9c60d9e43d4a5b75462455ad8c5ee4d82fee679a0cc4a3520d8abea5" +ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" +ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" +COEXIST_HELPER_SHA256="b6641270d1ed8687a2fb1331016f212533bfd4e490331213ff24684d2999a397" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): @@ -82,6 +84,47 @@ def selective_dns_optin_save(enabled): SELECTIVE_DNS_OPT_IN.unlink() +def encrypted_dns_optin_state(): + """Distinct explicit consent; NEVER convert old unencrypted consent.""" + try: + if ENCRYPTED_DNS_OPT_IN.is_symlink(): + return "INVALID" + if not ENCRYPTED_DNS_OPT_IN.exists(): + return "OFF" + st=ENCRYPTED_DNS_OPT_IN.stat() + if not ENCRYPTED_DNS_OPT_IN.is_file() or st.st_uid!=os.getuid() or st.st_mode & 0o077: + return "INVALID" + if selective_dns_optin_state()!="OFF": + return "INVALID" + return ("ON" if ENCRYPTED_DNS_OPT_IN.read_text(encoding="utf-8").strip()== + ENCRYPTED_DNS_TOKEN else "INVALID") + except Exception: + return "INVALID" + + +def encrypted_dns_optin_save(enabled): + if ENCRYPTED_DNS_OPT_IN.is_symlink(): + raise ValueError("Encrypted DNS consent symlink is unsafe") + if enabled: + if selective_dns_optin_state()!="OFF": + raise ValueError("Unencrypted DNS consent must first be disabled") + ENCRYPTED_DNS_OPT_IN.parent.mkdir(parents=True,exist_ok=True) + fd,temp=tempfile.mkstemp(prefix=".dim-encrypted-consent-",dir=str(ENCRYPTED_DNS_OPT_IN.parent)) + try: + with os.fdopen(fd,"w",encoding="utf-8") as f: + f.write(ENCRYPTED_DNS_TOKEN+"\n") + f.flush();os.fsync(f.fileno()) + os.chmod(temp,0o600) + os.replace(temp,ENCRYPTED_DNS_OPT_IN) + finally: + if os.path.lexists(temp):os.unlink(temp) + elif ENCRYPTED_DNS_OPT_IN.exists(): + st=ENCRYPTED_DNS_OPT_IN.stat() + if st.st_uid!=os.getuid() or not ENCRYPTED_DNS_OPT_IN.is_file(): + raise ValueError("Foreign encrypted DNS consent cannot be deleted") + ENCRYPTED_DNS_OPT_IN.unlink() + + def ui_test_log(message): p=os.environ.get("DIM_UI_TEST_LOG") if p: @@ -138,7 +181,7 @@ def sync_adult_catalog(force=False): last_error=None for source_url in ADULT_CATALOG_URLS: try: - req=urllib.request.Request(source_url,headers={"User-Agent":"DirectInternetMethod/1.5.4"}) + req=urllib.request.Request(source_url,headers={"User-Agent":"DirectInternetMethod/1.5.5-rc1"}) with urllib.request.urlopen(req,timeout=20) as resp: raw=resp.read(4*1024*1024+1) if len(raw)<100000 or len(raw)>4*1024*1024: @@ -292,7 +335,7 @@ def _coexistence_backend_ready(): not helper.is_file() or not meta.is_file()): return False installed=json.loads(meta.read_text(encoding="utf-8")) - return (installed.get("version") in ("1.5.2","1.5.3","1.5.4") and + return (installed.get("version") == "1.5.5-rc1" and installed.get("uid")==os.getuid() and hashlib.sha256(helper.read_bytes()).hexdigest()==COEXIST_HELPER_SHA256) except Exception: @@ -427,11 +470,13 @@ def curl(url): return {"ok":ok,"physicalInterface":physical,"dnsMode":s.get("dnsMode","direct-doh"),"directMethods":methods,"youtubeHttp80":h,"youtube":y,"openai":o,"github":g,"gemini":m} def _version_tuple(text): - s=text.strip().lstrip("vV") - try: - return tuple(int(x) for x in s.split(".")[:3]) - except Exception: - return (0,0,0) + """Stable must sort newer than prerelease with same three-digit version.""" + s=str(text).strip().lstrip("vV") + hit=re.fullmatch(r"(\d+)\.(\d+)\.(\d+)(?:-(rc)(\d+))?",s) + if not hit: + return (0,0,0,-1,0) + x,y,z,kind,number=hit.groups() + return (int(x),int(y),int(z), 0 if kind else 1, int(number or 0)) def check_update_sync(): req=urllib.request.Request(LATEST_API,headers={"User-Agent":f"DirectInternetMethod/{VERSION}","Accept":"application/vnd.github+json"}) @@ -744,6 +789,11 @@ def open_strategy(self): selective_toggle=Gtk.CheckButton(label="Opt in: experimental selected-sites DNS (unencrypted third-party UDP)") selective_toggle.set_active(selective_dns_optin_state()=="ON") outer.append(selective_toggle) + encrypted_toggle=Gtk.CheckButton(label="Opt in: encrypted targeted DNS (DoH to Control D; experimental)") + encrypted_toggle.set_active(encrypted_dns_optin_state()=="ON") + outer.append(encrypted_toggle) + enc_warn=Gtk.Label(label="Separate encrypted-DNS opt-in. Requires Targeted + Balanced and Tailscale split mode, without Seman or another VPN. Routes only supported domains over DoH. Does not guarantee all sites or remove service-side restrictions. The old unencrypted DNS consent does NOT authorize this mode.",xalign=0,wrap=True) + enc_warn.add_css_class("dim-label");outer.append(enc_warn) warn=Gtk.Label(label="Privacy warning: experimental DNS sends selected site names to 194.225.152.10 over UNENCRYPTED UDP; this is not DoH. Only Targeted + Balanced with Tailscale split mode is supported. Disabled by default. Route, Google DNS and MagicDNS must stay protected.",xalign=0,wrap=True) warn.add_css_class("dim-label");outer.append(warn) hint=Gtk.Label(label="Targeted mode affects only built-in and Custom Sites. All Sites mode can help unknown blocked domains, but may reduce compatibility or speed on some sites. Neither mode creates a VPN, proxy, or default-route tunnel.",xalign=0,wrap=True) @@ -756,6 +806,15 @@ def do_save(*_): try: value=("balanced","compatibility","strong")[int(combo.get_selected())] enable_dns=bool(selective_toggle.get_active()) + enable_encrypted=bool(encrypted_toggle.get_active()) + if enable_dns and enable_encrypted: + raise ValueError("Choose exactly one DNS option. The two consent modes cannot coexist.") + if encrypted_dns_optin_state()=="INVALID": + raise ValueError("Existing encrypted DNS consent is invalid or unsafe; inspect it before saving.") + if selective_dns_optin_state()=="INVALID": + raise ValueError("Existing unencrypted DNS consent is invalid; inspect it before saving.") + if enable_encrypted and (value!="balanced" or scope_toggle.get_active()): + raise ValueError("Encrypted DNS requires Targeted + Balanced and Tailscale split mode.") if enable_dns and (value!="balanced" or scope_toggle.get_active()): raise ValueError("Experimental unencrypted split DNS requires Balanced and Targeted. Select those options first.") STRATEGY_FILE.parent.mkdir(parents=True,exist_ok=True) @@ -768,8 +827,12 @@ def do_save(*_): st=SCOPE_FILE.with_suffix(".tmp") st.write_text(scope_value+"\n",encoding="utf-8") os.replace(st,SCOPE_FILE) + # Explicitly revoke old permission before granting new one. + # Never interpret the unencrypted consent token as encrypted approval. selective_dns_optin_save(enable_dns) - privacy=(" experimental UNENCRYPTED selected DNS is opted in" if enable_dns else " experimental DNS off") + encrypted_dns_optin_save(enable_encrypted) + privacy=(" encrypted selected DNS opt-in" if enable_encrypted else + " UNENCRYPTED selected DNS opt-in" if enable_dns else " experimental DNS off") self.result.set_text(f"Strategy saved: {value}; scope: {scope_value};{privacy}. Stop/Start to apply.") w.close();self.refresh() except Exception as e: diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index eee738ef..e783e43d 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -24,6 +24,9 @@ SCOPE_FILE="$STATE_BASE/scope.txt" SELECTIVE_DNS_OPT_IN="$STATE_BASE/selective-dns-optin.txt" SELECTIVE_DNS="/usr/lib/directinternetmethod/selective_dns.py" SELECTIVE_DNS_ENABLED=0 +ENCRYPTED_DNS_OPT_IN="$STATE_BASE/encrypted-dns-optin.txt" +ENCRYPTED_DNS="/usr/lib/directinternetmethod/encrypted_selective_dns.py" +ENCRYPTED_DNS_ENABLED=0 CREATED_STATE=0 CTRLD="$BIN/ctrld" NFQWS="$BIN/nfqws" @@ -324,9 +327,7 @@ cleanup_transient(){ # Restore original physical DNS FIRST, even if NFT ownership cannot # subsequently be established. Never leave ordinary Internet pointing # at a defunct local DNS service after a failed Start. - if [ -f /run/directinternetmethod-selective/session.json ]; then - python3 "$SELECTIVE_DNS" restore || return 88 - fi + restore_selective_dns || return 88 if [ "$CREATED_TABLE" -eq 1 ]; then if nft_table_presence; then if nft_table_transient_owned "$PHY" "$TAILSCALE_COEXIST"; then @@ -364,6 +365,7 @@ cleanup_transient(){ cleanup_state_owned(){ [ -f "$STATE" ] || return 0 + restore_selective_dns || return 88 local cp np phy nft_rc cp="$(state_value ctrldPid)" np="$(state_value nfqwsPid)" @@ -395,7 +397,31 @@ cleanup_state_owned(){ rm -f "$RUN_HOSTS" "$STATE" } +encrypted_dns_ready(){ + [ -x "$ENCRYPTED_DNS" ] && [ -f /run/directinternetmethod-encrypted/session.json ] || return 1 + local payload + payload="$(python3 "$ENCRYPTED_DNS" status 2>/dev/null)" || return 1 + printf '%s\n' "$payload" | python3 -c 'import json,sys +try: + d=json.load(sys.stdin) + sys.exit(0 if d.get("ok") is True and d.get("state")=="ACTIVE" and + all(d.get(k)=="active" for k in ("monitor","ctrld","backstop")) else 1) +except Exception:sys.exit(1)' +} restore_selective_dns(){ + if [ -f /run/directinternetmethod-encrypted/session.json ]; then + [ -x "$ENCRYPTED_DNS" ] || { echo '{"ok":false,"error":"ENCRYPTED_CONTROLLER_MISSING"}' >&2; return 88; } + python3 "$ENCRYPTED_DNS" restore || { + echo '{"ok":false,"error":"ENCRYPTED_DNS_RESTORE_FAILED"}' >&2 + return 88 + } + fi + if [ -e /run/directinternetmethod-encrypted ] || + [ -e /run/directinternetmethod-encrypted-ctrld.toml ] || + ip link show dimesd0 >/dev/null 2>&1; then + echo '{"ok":false,"error":"ENCRYPTED_DNS_UNOWNED_RESIDUE"}' >&2 + return 88 + fi if [ -e /run/directinternetmethod-selective/session.json ]; then [ -x "$SELECTIVE_DNS" ] || { echo '{"ok":false,"error":"SELECTIVE_DNS_CONTROLLER_MISSING"}' >&2; return 88; } python3 "$SELECTIVE_DNS" restore || { @@ -412,6 +438,10 @@ verify_clean(){ [ ! -e /run/directinternetmethod-selective/session.json ] || return 1 [ ! -e /run/directinternetmethod-selective ] || return 1 [ ! -e /run/directinternetmethod-selective.conf ] || return 1 + [ ! -e /run/directinternetmethod-encrypted/session.json ] || return 1 + [ ! -e /run/directinternetmethod-encrypted ] || return 1 + [ ! -e /run/directinternetmethod-encrypted-ctrld.toml ] || return 1 + ! ip link show dimesd0 >/dev/null 2>&1 || return 1 ! ip link show "$DNS_IF" >/dev/null 2>&1 || return 1 if nft_table_presence; then return 1 @@ -441,6 +471,9 @@ status(){ else [ "$ca" = true ] && [ "$link" = true ] && dns_ok=true fi + if [ -f "$STATE" ] && [ "$(state_value selectiveDnsEncrypted)" = "True" ]; then + encrypted_dns_ready || dns_ok=false + fi local phy="" [ -f "$STATE" ] && phy="$(state_value physicalInterface)" nft_table_owned "$phy" && table=true || true @@ -532,6 +565,7 @@ case "$ACTION" in echo '{"ok":false,"error":"FOREIGN_OR_STALE_RESOURCE_WITHOUT_STATE"}' exit 81 fi + restore_selective_dns kill_all_owned_by_exe "$NFQWS" kill_all_owned_by_exe "$CTRLD" sleep .3 @@ -554,6 +588,9 @@ case "$ACTION" in DNS_HEALTHY=0 if [ "$PREVIOUS_DNS_MODE" = "system-preserved" ]; then [ "$CPID" = "0" ] && ! ip link show "$DNS_IF" >/dev/null 2>&1 && DNS_HEALTHY=1 + if [ "$(state_value selectiveDnsEncrypted)" = "True" ]; then + encrypted_dns_ready || DNS_HEALTHY=0 + fi else pid_owned "$CPID" "$CTRLD" && dns_link_owned && DNS_HEALTHY=1 fi @@ -578,6 +615,7 @@ case "$ACTION" in echo '{"ok":false,"error":"FOREIGN_OR_STALE_RESOURCE_WITHOUT_STATE"}' exit 69 fi + restore_selective_dns || exit 88 [ -x "$CTRLD" ] && [ -x "$NFQWS" ] && [ -s "$HOSTS" ] || { echo '{"ok":false,"error":"RUNTIME_NOT_PROVISIONED"}'; exit 70; } @@ -684,6 +722,15 @@ PY SELECTIVE_DNS_ENABLED=1 fi + if [ -e "$ENCRYPTED_DNS_OPT_IN" ] || [ -L "$ENCRYPTED_DNS_OPT_IN" ]; then + [ "$TAILSCALE_COEXIST" -eq 1 ] && [ "$STRATEGY" = "balanced" ] && + [ "$SCOPE" = "targeted" ] && [ -x "$ENCRYPTED_DNS" ] || + { echo '{"ok":false,"error":"ENCRYPTED_DNS_REQUIRES_TARGETED_BALANCED_TAILSCALE_AND_BACKEND"}'; exit 88; } + [ ! -e "$SELECTIVE_DNS_OPT_IN" ] && [ ! -L "$SELECTIVE_DNS_OPT_IN" ] || + { echo '{"ok":false,"error":"ENCRYPTED_AND_UNENCRYPTED_OPTIN_CONFLICT"}'; exit 88; } + ENCRYPTED_DNS_ENABLED=1 + fi + if [ "$DNS_MODE" = "direct-doh" ]; then ip link add "$DNS_IF" type dummy CREATED_LINK=1 @@ -811,8 +858,16 @@ EOF } fi + if [ "$ENCRYPTED_DNS_ENABLED" -eq 1 ]; then + USER_HOME="$(getent passwd "$USER_UID" | cut -d: -f6)" + [ "$USER_HOME/.local/share/DirectInternetMethod" = "$STATE_BASE" ] || + { echo '{"ok":false,"error":"ENCRYPTED_DNS_HOME_MISMATCH"}'; exit 88; } + python3 "$ENCRYPTED_DNS" activate --uid "$USER_UID" --interface "$PHY" --home "$USER_HOME" || + { echo '{"ok":false,"error":"ENCRYPTED_DNS_ACTIVATION_FAILED"}'; exit 88; } + fi + [ "$DNS_MODE" != "system-preserved" ] || CPID=0 - python3 - "$STATE" "$CPID" "$NPID" "$PHY" "$USER_UID" "$USER_GID" "$CTRLD_UNIT" "$NFQWS_UNIT" "$STRATEGY" "$SCOPE" "$ADULT_ON" "$ADULT_COUNT" "$DNS_MODE" "$TAILSCALE_COEXIST" "$SELECTIVE_DNS_ENABLED" <<'PY' + python3 - "$STATE" "$CPID" "$NPID" "$PHY" "$USER_UID" "$USER_GID" "$CTRLD_UNIT" "$NFQWS_UNIT" "$STRATEGY" "$SCOPE" "$ADULT_ON" "$ADULT_COUNT" "$DNS_MODE" "$TAILSCALE_COEXIST" "$SELECTIVE_DNS_ENABLED" "$ENCRYPTED_DNS_ENABLED" <<'PY' import json,os,sys,time p=sys.argv[1] d={ @@ -823,6 +878,7 @@ d={ "dnsMode":sys.argv[13], "tailscaleCoexistence":bool(int(sys.argv[14])), "selectiveDns":bool(int(sys.argv[15])), + "selectiveDnsEncrypted":bool(int(sys.argv[16])), "strategy":sys.argv[9], "scope":sys.argv[10], "adultCoverage":bool(int(sys.argv[11])), @@ -844,6 +900,10 @@ os.chown(p,int(sys.argv[5]),int(sys.argv[6])) PY CREATED_STATE=1 + if [ "$ENCRYPTED_DNS_ENABLED" -eq 1 ]; then + python3 "$ENCRYPTED_DNS" commit || + { echo '{"ok":false,"error":"ENCRYPTED_DNS_COMMIT_FAILED"}'; exit 88; } + fi if [ "$SELECTIVE_DNS_ENABLED" -eq 1 ]; then python3 "$SELECTIVE_DNS" commit || { echo '{"ok":false,"error":"SELECTIVE_DNS_COMMIT_FAILED"}' diff --git a/linux/app/user_uninstall_preserve.py b/linux/app/user_uninstall_preserve.py index c881bf4f..e18ed872 100644 --- a/linux/app/user_uninstall_preserve.py +++ b/linux/app/user_uninstall_preserve.py @@ -50,7 +50,7 @@ def load_owned(): meta = app / "INSTALL.json" ensure(meta.is_file() and not meta.is_symlink(), "UNINSTALL_INSTALL_METADATA_MISSING") doc = json.loads(meta.read_text(encoding="utf8")) - ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.4", + ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.5-rc1", "UNINSTALL_MANIFEST_OWNER_VERSION_UNVERIFIED") codes = doc.get("managedCodeSha256") launchers = doc.get("managedLaunchersSha256") diff --git a/linux/app/user_update_transaction.py b/linux/app/user_update_transaction.py index b82e5d04..cf1e181e 100644 --- a/linux/app/user_update_transaction.py +++ b/linux/app/user_update_transaction.py @@ -60,6 +60,7 @@ MUTABLE = ( "settings.json", "strategy.txt", "scope.txt", "custom-hosts.txt", "adult-enabled.txt", "adult-hosts.txt", "adult-hosts.meta.json", + "encrypted-dns-optin.txt", "selective-dns-optin.txt", ) @@ -135,7 +136,7 @@ def source_files(root: pathlib.Path) -> dict: """ guard(root.is_absolute() and root.is_dir() and not root.is_symlink() and not root.parent.is_symlink(), "SOURCE_PACKAGE_INVALID") - archive_layout = root.name == "DirectInternetMethod_1.5.4_Linux_x86_64" + archive_layout = root.name == "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" source_layout = root.name == "linux" guard(archive_layout or source_layout, "SOURCE_PACKAGE_LAYOUT_NOT_LINUX_OR_EXTRACTED_ZIP") if archive_layout: @@ -150,10 +151,10 @@ def source_files(root: pathlib.Path) -> dict: raise RuntimeError("EXTRACTED_PACKAGE_MANIFEST_INVALID_JSON") from ex guard(isinstance(package_doc, dict) and package_doc.get("schema") == 1 and package_doc.get("product") == "Direct Internet Method" and - package_doc.get("version") == "1.5.4" and + package_doc.get("version") == "1.5.5-rc1" and package_doc.get("platform") == "linux-x86_64" and isinstance(package_doc.get("files"), list) and - len(package_doc["files"]) == 22, "EXTRACTED_PACKAGE_IDENTITY_WRONG") + len(package_doc["files"]) == 26, "EXTRACTED_PACKAGE_IDENTITY_WRONG") seen = set() digest_lines = [] for item in package_doc["files"]: @@ -275,7 +276,7 @@ def stage(src: pathlib.Path): try: (pending / "old").mkdir(mode=0o700) manifest = { - "schema": 1, "version": "1.5.4", + "schema": 1, "version": "1.5.5-rc1", "managed": { rel: {"sha": row["sha"], "mode": row["mode"]} for rel, row in payload.items() @@ -310,7 +311,7 @@ def load_pending(): not manifest_file.is_symlink(), "PENDING_TRANSACTION_NOT_VALID") guard(pending.stat().st_uid == os.getuid(), "PENDING_TRANSACTION_NOT_OWNED") data = json.loads(manifest_file.read_text(encoding="utf8")) - guard(data.get("schema") == 1 and data.get("version") == "1.5.4", "PENDING_TRANSACTION_VERSION_WRONG") + guard(data.get("schema") == 1 and data.get("version") == "1.5.5-rc1", "PENDING_TRANSACTION_VERSION_WRONG") allowed = {METADATA, *(rel for _, rel, _ in FILES), PROVIDERS} guard(set(data["old"]) == set(data["managed"]) | {METADATA} and set(data["old"]) <= allowed, "PENDING_FILESET_FOREIGN") @@ -376,7 +377,7 @@ def check_metadata(app, data): path = app / METADATA guard(path.is_file() and not path.is_symlink(), "INSTALLED_METADATA_MISSING") doc = json.loads(path.read_text(encoding="utf8")) - guard(doc.get("version") == "1.5.4" and doc.get("schema") == 2, "INSTALLED_VERSION_METADATA_NOT_VALID") + guard(doc.get("version") == "1.5.5-rc1" and doc.get("schema") == 2, "INSTALLED_VERSION_METADATA_NOT_VALID") guard(doc.get("managedProviderSha256") == data["providerSha"], "PROVIDER_MANAGED_OWNERSHIP_METADATA_CHANGED") expected = {rel: row["sha"] for rel, row in data["managed"].items() if rel != PROVIDERS} @@ -419,7 +420,7 @@ def rollback(): if rel == METADATA and observed not in (info["sha"], None): try: doc = json.loads(dest.read_text(encoding="utf8")) - guard(doc.get("schema") == 2 and doc.get("version") == "1.5.4", + guard(doc.get("schema") == 2 and doc.get("version") == "1.5.5-rc1", "FOREIGN_INSTALL_METADATA_PRESERVED") except (UnicodeError, ValueError) as ex: raise RuntimeError("UNKNOWN_INSTALL_METADATA_ON_ROLLBACK") from ex @@ -513,11 +514,11 @@ def recover_metadata_stage(expected_sha256: str): } guard(set(doc) == expected_keys and doc["schema"] == 2 and doc["product"] == "Direct Internet Method" and - doc["version"] == "1.5.4" and doc["platform"] == "linux-x86_64" and + doc["version"] == "1.5.5-rc1" and doc["platform"] == "linux-x86_64" and doc["networkMutationOnInstall"] is False and doc["normalRunRequiresAdmin"] is False and doc["systemBackend"] == "/usr/lib/directinternetmethod" and - doc["protectedBackendVersion"] in ("1.5.1", "1.5.2", "1.5.3", "1.5.4") and + doc["protectedBackendVersion"] == "1.5.5-rc1" and doc["protectedBackendByteCompatible"] is True and doc["onlineUpdate"] == "GitHub latest release + SHA256SUMS verification" and doc["updateMode"] == "IN_PLACE_SINGLE_INSTALL_DATA_PRESERVED", diff --git a/linux/install.sh b/linux/install.sh index 9356fe35..1be18005 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -8,6 +8,8 @@ DESKTOP="$HOME/.local/share/applications" ICONS="$HOME/.local/share/icons/hicolor/scalable/apps" USER_TRANSACTION="$HERE/app/user_update_transaction.py" TRANSACTION_ACTIVE=0 +ROOT_UPGRADE_STAGED=0 +ROOT_TRANSACTION="$HERE/system/protected_upgrade_transaction.py" rollback_failed_install() { local code="$1" trap - ERR HUP INT TERM @@ -17,6 +19,11 @@ rollback_failed_install() { echo "Inspect $HOME/.local/state/DirectInternetMethod/update-pending" >&2 fi fi + if [ "$ROOT_UPGRADE_STAGED" -eq 1 ] && [ -f /var/lib/directinternetmethod/upgrade-pending/manifest.json ]; then + if ! pkexec /usr/bin/python3 "$ROOT_TRANSACTION" rollback; then + echo 'DEGRADED: protected backend rollback failed; preserve exact root slot for review.' >&2 + fi + fi exit "$code" } @@ -64,7 +71,7 @@ backend_matches_current() { python3 - /var/lib/directinternetmethod/install.json "$uid" <<'PY' >/dev/null 2>&1 || return 1 import json,sys d=json.load(open(sys.argv[1],encoding="utf-8")) -raise SystemExit(0 if d.get("version") in ("1.5.1","1.5.2","1.5.3","1.5.4") and str(d.get("uid"))==sys.argv[2] else 1) +raise SystemExit(0 if d.get("version")=="1.5.5-rc1" and str(d.get("uid"))==sys.argv[2] else 1) PY [ "$(cat /etc/directinternetmethod.uid 2>/dev/null || true)" = "$uid" ] || return 1 for action in start stop recovery; do @@ -76,6 +83,8 @@ PY done <&2 + if [ -f /var/lib/directinternetmethod/install.json ] && + [ -d /usr/lib/directinternetmethod ]; then + ROOT_UPGRADE_STAGED=1 + pkexec /bin/bash "$HERE/system/upgrade_system.sh" "$HERE" "$(id -u)" + elif [ ! -e /var/lib/directinternetmethod/install.json ] && + [ ! -e /usr/lib/directinternetmethod ]; then + pkexec /bin/bash "$HERE/system/install_system.sh" "$HERE" "$(id -u)" + else + echo 'Foreign/partial root backend; no mutation authorized.' >&2 rollback_failed_install 25 fi - # Only a genuinely fresh root-backend install may request PolicyKit. - pkexec /bin/bash "$HERE/system/install_system.sh" "$HERE" "$(id -u)" fi backend_matches_current || { echo "Protected backend verification failed." >&2; rollback_failed_install 22; } # Atomically replace version-managed code in the ONE installed app directory. @@ -199,11 +210,11 @@ for name, expected in expected_launchers.items(): launchers[name]=hashlib.sha256(expected).hexdigest() transaction=json.loads((pathlib.Path.home()/".local/state/DirectInternetMethod/update-pending/manifest.json").read_text(encoding="utf8")) protected=json.loads(pathlib.Path("/var/lib/directinternetmethod/install.json").read_text(encoding="utf-8")) -assert protected.get("version") in ("1.5.1","1.5.2","1.5.3","1.5.4") +assert protected.get("version") == "1.5.5-rc1" metadata=json.dumps({ "schema":2, "product":"Direct Internet Method", - "version":"1.5.4", + "version":"1.5.5-rc1", "platform":"linux-x86_64", "installedUtc":time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime()), "networkMutationOnInstall":False, @@ -229,11 +240,18 @@ spec.loader.exec_module(transaction_helper) transaction_helper.atomic_bytes(pathlib.Path(sys.argv[1]),metadata.encode("utf-8"),0o600) PY -# Commit only after managed code and mutable data hashes are reverified. -# On success, the one temporary rollback slot is removed; no dated copies remain. -python3 "$USER_TRANSACTION" --commit -TRANSACTION_ACTIVE=0 -trap - ERR HUP INT TERM -echo "Updated Direct Internet Method 1.5.4 in place; user data preserved." +# Unreleased candidate: protected Root must complete REAL native acceptance. +# Retain one bounded user/root rollback slot, not a permanent duplicate install. +if [ "$ROOT_UPGRADE_STAGED" -eq 1 ]; then + python3 "$USER_TRANSACTION" --status + TRANSACTION_ACTIVE=0 + trap - ERR HUP INT TERM + echo 'R519_CANDIDATE_PENDING_NATIVE_ROOT_USER_COMMIT: in-place files updated; not Released.' +else + python3 "$USER_TRANSACTION" --commit + TRANSACTION_ACTIVE=0 + trap - ERR HUP INT TERM + echo 'R519_CANDIDATE_FRESH_USER_CODE_COMMITTED: native release acceptance OPEN.' +fi echo "Normal Start / Stop / Recovery do not require an admin password." echo "Install/update/uninstall of the protected backend may authenticate once." diff --git a/linux/system/encrypted_selective_dns.py b/linux/system/encrypted_selective_dns.py new file mode 100644 index 00000000..b5f2b046 --- /dev/null +++ b/linux/system/encrypted_selective_dns.py @@ -0,0 +1,568 @@ +#!/usr/bin/env python3 +"""R517 staged root-owned managed encrypted selective DNS controller. + +NOT INSTALLED OR RELEASED. This source shares only the native DirectMethod +action lock and uses an independent explicit opt-in (never old UDP consent). +Owned dummy per-link DNS and monitored ctrld v1.5.7 are fail-closed; an +independent repeating systemd backstop is armed before any resolver mutation. +""" +from __future__ import annotations + +import argparse +import fcntl +import hashlib +import ipaddress +import importlib.util +import json +import os +import pathlib +import pwd +import re +import subprocess +import sys +import tempfile +import time +import uuid +from contextlib import contextmanager + +ROOT = pathlib.Path("/usr/lib/directinternetmethod") +STATE_DIR = pathlib.Path("/run/directinternetmethod-encrypted") +STATE = STATE_DIR / "session.json" +CONFIG = pathlib.Path("/run/directinternetmethod-encrypted-ctrld.toml") +ACTION_LOCK = pathlib.Path("/run/directinternetmethod-action.lock") +CTRLD = ROOT / "runtime/usr/bin/ctrld" +SOURCE = ROOT / "encrypted_selective_dns.py" +POLICY = ROOT / "encrypted_selective_policy.py" +VERSIONED_CTRLD_SHA = "CA64579A2BC866CF72F3E46663E551162AFEFF0D448E899A01817A095E45B071" +VERSIONED_NFQWS_SHA = "F34615964D7321650197CD69D3F7CBFDAABE8118B5A0D57C6E41DCCDFF658999" +NFQWS = ROOT / "runtime/usr/bin/nfqws" +IFACE = "dimesd0" +IP = "172.31.254.59" +CIDR = IP + "/32" +PORT = 53534 +TARGETS = ("youtube.com", "googlevideo.com", "ytimg.com", "youtu.be") +CONSENT = "I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" +OLD_OPTIN = "selective-dns-optin.txt" +NEW_OPTIN = "encrypted-dns-optin.txt" +GRACE_SEC = 120 +BACKSTOP_SEC = 45 +MONITOR_SEC = 4 +UNIT_RE = re.compile(r"^dim-edsn-(?:dns|watch)-[0-9]{1,10}-[0-9a-f]{10}\.service$") +TIMER_RE = re.compile(r"^dim-edsn-backstop-[0-9]{1,10}-[0-9a-f]{10}\.timer$") +PHY_RE = re.compile(r"^[A-Za-z0-9_.:-]{1,15}$") +STATE_PHASES = frozenset(("ARMING", "ACTIVE", "STOPPING", "DEGRADED")) + + +def need(condition: bool, why: str) -> None: + if not condition: + raise RuntimeError("FAIL_CLOSED_" + why) + + +def call(*args: str, seconds: int = 10, ok: bool = True) -> subprocess.CompletedProcess: + p = subprocess.run(args, capture_output=True, text=True, timeout=seconds) + if ok and p.returncode: + raise RuntimeError("CALL_FAILED_" + repr(args[:3]) + "_" + str(p.returncode) + + "_" + p.stderr[-100:]) + return p + + +def root() -> None: + need(os.geteuid() == 0, "ROOT_REQUIRED") + + +def sha(raw: bytes) -> str: + return hashlib.sha256(raw).hexdigest().upper() + + +def file_sha(p: pathlib.Path) -> str: + return sha(p.read_bytes()) + + +def save(path: pathlib.Path, data: bytes, mode: int = 0o600) -> None: + need(path.parent.is_dir() and not path.parent.is_symlink(), "OUTPUT_PARENT") + f,temp = tempfile.mkstemp(prefix=".dim-encrypted-", dir=str(path.parent)) + try: + with os.fdopen(f, "wb") as h: + h.write(data); h.flush(); os.fsync(h.fileno()) + os.chmod(temp, mode) + os.replace(temp, path) + finally: + if os.path.lexists(temp): + os.unlink(temp) + + +def write_state(state: dict) -> None: + need(STATE_DIR.is_dir() and not STATE_DIR.is_symlink(), "ROOT_STATE_DIR") + need(STATE_DIR.stat().st_uid == 0 and STATE_DIR.stat().st_mode & 0o777 == 0o700, + "ROOT_DIR_MODE") + save(STATE, (json.dumps(state, sort_keys=True, indent=2) + "\n").encode()) + + +def validate_state(s: dict) -> dict: + need(type(s) is dict and s.get("schema") == 1, "STATE_SCHEMA") + need(s.get("phase") in STATE_PHASES, "STATE_PHASE") + uid = s.get("uid") + session = s.get("session") + need(type(uid) is int and 0 < uid < 2**31, "STATE_UID") + need(isinstance(session, str) and re.fullmatch("[0-9a-f]{10}", session), + "STATE_SESSION") + need(s.get("iface") == IFACE and s.get("ownedIp") == CIDR and s.get("port") == PORT, + "STATE_LINK_BINDING") + need(type(s.get("dummyIndex")) in (int, type(None)) and + (s["dummyIndex"] is None or s["dummyIndex"] > 1), "STATE_DUMMY_INDEX") + need(PHY_RE.fullmatch(str(s.get("physical", ""))), "STATE_PHY") + need(s.get("ctrldUnit") == f"dim-edsn-dns-{uid}-{session}.service", "STATE_CTRLD") + need(s.get("monitorUnit") == f"dim-edsn-watch-{uid}-{session}.service", "STATE_MONITOR") + need(s.get("backstopTimer") == f"dim-edsn-backstop-{uid}-{session}.timer", + "STATE_BACKSTOP") + need(UNIT_RE.fullmatch(s["ctrldUnit"]) and UNIT_RE.fullmatch(s["monitorUnit"]) and + TIMER_RE.fullmatch(s["backstopTimer"]), "STATE_UNIT_GRAMMAR") + need(s.get("ctrldSha") == VERSIONED_CTRLD_SHA, "STATE_BINARY_SHA") + need(s.get("configPath") == str(CONFIG), "STATE_CONF_PATH") + need(s.get("configSha") and re.fullmatch("[0-9A-F]{64}",s["configSha"]), + "STATE_CONFIG_SHA") + need(s.get("userHome", "").startswith("/home/") and + pathlib.Path(s["userHome"]).is_absolute() and + ".." not in pathlib.Path(s["userHome"]).parts,"STATE_USER_HOME") + need(s.get("physicalBaseline") and + isinstance(s["physicalBaseline"], dict) and + s["physicalBaseline"].get("dns") and + len(s["physicalBaseline"]["dns"])==1, "STATE_PHYSICAL_BASELINE") + need(re.fullmatch("[0-9A-F]{64}",str(s.get("tailscaleDNS",""))), + "STATE_TAILSCALE_SHA") + return s + + +def read_state() -> dict | None: + if not STATE.exists(): + return None + need(STATE.is_file() and not STATE.is_symlink(), "STATE_UNSAFE_TYPE") + st = STATE.stat() + need(st.st_uid == 0 and st.st_mode & 0o777 == 0o600, "STATE_UNSAFE_OWNER") + return validate_state(json.loads(STATE.read_text())) + + +def require_consent(uid: int, home: str) -> None: + need(type(uid) is int and 0 < uid < 2**31, "UID") + p = pathlib.Path(home) + need(p.is_dir() and not p.is_symlink() and p.is_absolute() and + str(p).startswith("/home/") and p.stat().st_uid == uid,"HOME_OWNER") + app = p / ".local/share/DirectInternetMethod" + opt = app / NEW_OPTIN + old = app / OLD_OPTIN + need(not old.exists() and not old.is_symlink(), "LEGACY_UNENCRYPTED_OPTIN_CONFLICT") + need(opt.is_file() and not opt.is_symlink(), "EXPLICIT_ENCRYPTED_CONSENT_MISSING") + st = opt.stat() + need(st.st_uid == uid and st.st_mode & 0o077 == 0 and + st.st_size < 130 and opt.read_text().strip() == CONSENT, + "CONSENT_NOT_EXACT_OR_PRIVATE") + for n,expected in (("strategy.txt","balanced"),("scope.txt","targeted")): + pref=app/n + need(pref.is_file() and not pref.is_symlink() and + pref.read_text().strip() == expected,"INVALID_CONSENT_PROFILE_"+n) + + +def link_state(iface: str) -> dict: + need(PHY_RE.fullmatch(iface), "BAD_IFACE") + def field(cmd: str) -> list[str]: + text=call("resolvectl",cmd,iface).stdout.strip() + m=re.fullmatch(r"Link \d+ \(" + re.escape(iface) + r"\):(?: (.*))?",text) + need(bool(m),"LINK_FIELDS_UNPARSEABLE_"+cmd) + return (m.group(1) or "").split() + route=field("default-route") + need(len(route)==1 and route[0] in ("yes","no"),"DNS_DEFAULT_ROUTE_INVALID") + return {"dns":field("dns"),"domains":field("domain"),"default":route[0]=="yes"} + + +def tailscale_fingerprint() -> str: + data=(call("resolvectl","dns","tailscale0").stdout+ + call("resolvectl","domain","tailscale0").stdout).encode() + return sha(data) + + +def network_preflight(phy: str) -> dict: + need(PHY_RE.fullmatch(phy) and phy not in ("lo", "tailscale0", IFACE), + "PHYSICAL_LINK_UNSAFE") + result=json.loads(call("tailscale","status","--json").stdout) + need(result.get("BackendState")=="Running" and not result.get("ExitNodeStatus"), + "TAILSCALE_SPLIT_REQUIRED") + modes=call("nmcli","-t","-f","TYPE,DEVICE","connection","show","--active").stdout.splitlines() + for mode in modes: + need(mode and ":" in mode,"NETWORK_MANAGER_AMBIGUOUS") + typ,dev=mode.split(":",1) + need(typ not in ("vpn","wireguard"),"EXTRA_VPN_CONFLICT") + if typ=="tun": + need(dev=="tailscale0","EXTRA_TUNNEL_CONFLICT") + route=call("ip","-4","route","show","default").stdout.strip() + need("dev "+phy in route and not ("dev tun" in route or "dev wg" in route), + "WAN_ROUTE_NOT_PHYSICAL") + for family in ("-4","-6"): + table=call("ip",family,"route","show","table","52").stdout.splitlines() + need(not any(line.strip().startswith(("default","0.0.0.0/1","128.0.0.0/1", + "::/1","8000::/1")) for line in table), + "TAILSCALE_EXIT_ROUTE") + physical=link_state(phy) + need(physical["default"] and len(physical["dns"])==1, "NEED_SINGLE_PHYSICAL_DNS") + # The pure protected policy validates literal RFC1918 router addresses. + need(not STATE_DIR.exists() and not CONFIG.exists() and not CONFIG.is_symlink(), + "PREEXISTING_ENCRYPTED_DNS") + need(not pathlib.Path("/sys/class/net", IFACE).exists(),"DUMMY_LINK_COLLISION") + need(not call("ip","-4","route","show","exact",CIDR).stdout.strip(), + "RESERVED_IPV4_COLLISION") + need(":"+str(PORT) not in call("ss","-lunpt",ok=False).stdout, + "DNS_LISTENER_PORT_OCCUPIED") + need(CTRLD.is_file() and file_sha(CTRLD)==VERSIONED_CTRLD_SHA,"CTRLD_VERSION_SHA") + need(POLICY.is_file() and SOURCE.is_file(),"PROTECTED_CONTROLLER_INCOMPLETE") + return physical + + +def runtime_tunnel_guard(physical: str) -> None: + """Read-only fail-closed guard for VPN/Tailscale changes after activation.""" + need(bool(PHY_RE.fullmatch(physical)) and physical not in ("lo","tailscale0",IFACE), + "RUNTIME_PHYSICAL_IFACE") + ts=json.loads(call("tailscale","status","--json").stdout) + need(ts.get("BackendState")=="Running" and not ts.get("ExitNodeStatus"), + "RUNTIME_TAILSCALE_EXIT_OR_DOWN") + modes=call("nmcli","-t","-f","TYPE,DEVICE","connection","show","--active").stdout.splitlines() + seen_tailscale=False + seen_physical=False + for item in modes: + need(":" in item,"RUNTIME_NMCLI_AMBIGUOUS") + kind,dev=item.split(":",1) + need(kind not in ("vpn","wireguard"),"RUNTIME_FOREIGN_VPN_CONFLICT") + if kind=="tun": + need(dev=="tailscale0","RUNTIME_FOREIGN_TUNNEL_CONFLICT") + seen_tailscale=True + if dev==physical: + need(kind not in ("tun","vpn","wireguard"),"RUNTIME_PHY_RECLASSIFIED") + seen_physical=True + need(seen_physical and seen_tailscale,"RUNTIME_EXPECTED_INTERFACES_MISSING") + route=call("ip","-4","route","show","default").stdout.strip() + need("dev "+physical in route and "dev tun" not in route and "dev wg" not in route, + "RUNTIME_DEFAULT_WAN_CHANGED") + for family in ("-4","-6"): + entries=call("ip",family,"route","show","table","52").stdout.splitlines() + need(not any(x.strip().startswith(("default","0.0.0.0/1","128.0.0.0/1", + "::/1","8000::/1")) for x in entries), + "RUNTIME_TAILSCALE_EXIT_ROUTE") + + +def policy_bytes(router: str) -> bytes: + spec=importlib.util.spec_from_file_location("dim_protected_policy",POLICY) + need(spec is not None and spec.loader is not None,"POLICY_IMPORT_SPEC") + mod=importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + data=mod.build_owned_interface_policy(router) + need(b'endpoint = "https://76.76.10.11/p0"' in data and + b'ip = "172.31.254.59"' in data and + b'allow_wan_clients = false' in data, "POLICY_BINDING_SHAPE") + return data + + +def unit_state(name: str, timer: bool = False) -> str: + need(bool((TIMER_RE if timer else UNIT_RE).fullmatch(name)),"UNIT_NAME_UNOWNED") + return call("systemctl","show",name,"-p","ActiveState","--value",ok=False).stdout.strip() + + +def dummy_identity(s: dict) -> int | None: + p=pathlib.Path("/sys/class/net")/IFACE + if not p.exists():return None + info=json.loads(call("ip","-j","-d","link","show","dev",IFACE).stdout) + need(len(info)==1 and info[0].get("linkinfo",{}).get("info_kind")=="dummy", + "FOREIGN_INTERFACE_KIND") + index=int(p.joinpath("ifindex").read_text().strip()) + need(index>1 and index==s.get("dummyIndex"),"DUMMY_INDEX_REUSED") + addresses=call("ip","-o","-4","addr","show","dev",IFACE).stdout + need("inet "+CIDR+" " in addresses,"OWNED_LINK_ADDRESS_CHANGED") + return index + + +def link_dns(idx: int) -> None: + address=[str(x) for x in ipaddress.IPv4Address(IP).packed] + call("busctl","--timeout=6s","call","org.freedesktop.resolve1", + "/org/freedesktop/resolve1","org.freedesktop.resolve1.Manager", + "SetLinkDNSEx","ia(iayqs)",str(idx),"1","2","4",*address,str(PORT),"") + args=("busctl","--timeout=6s","call","org.freedesktop.resolve1", + "/org/freedesktop/resolve1","org.freedesktop.resolve1.Manager", + "SetLinkDomains","ia(sb)",str(idx),str(len(TARGETS))) + domainargs=[x for name in TARGETS for x in (name,"true")] + call(*args,*domainargs) + call("busctl","--timeout=6s","call","org.freedesktop.resolve1", + "/org/freedesktop/resolve1","org.freedesktop.resolve1.Manager", + "SetLinkDefaultRoute","ib",str(idx),"false") + + +def dns_is_owned() -> bool: + d=link_state(IFACE) + return (d["dns"]==[f"{IP}:{PORT}"] and + d["default"] is False and + set(d["domains"])=={"~"+name for name in TARGETS}) + + +def public_dns(domain: str) -> bool: + p=call("dig","@"+IP,"-p",str(PORT),domain,"A","+tries=1","+time=3","+short", + seconds=6,ok=False) + ips=[] + for line in p.stdout.splitlines(): + try:ips.append(ipaddress.IPv4Address(line.strip())) + except (ipaddress.AddressValueError,ValueError):pass + return bool(ips) and p.returncode==0 and all(x.is_global for x in ips) + + +def rollback(reason: str = "MANUAL", stop_monitor: bool = True) -> dict: + root() + s=read_state() + if s is None: + need(not CONFIG.exists(),"UNOWNED_OR_ORPHAN_CONF") + need(not pathlib.Path("/sys/class/net",IFACE).exists(),"UNOWNED_ORPHAN_LINK") + return {"ok":True,"state":"NO_SESSION"} + idx=dummy_identity(s) + if idx is not None: + existing=link_state(IFACE) + # Never revert foreign resolver settings or overwrite user rules. + need((not existing["dns"] and not existing["domains"]) or + dns_is_owned(), "FOREIGN_LINK_DNS_PRESENT") + call("busctl","--timeout=6s","call","org.freedesktop.resolve1", + "/org/freedesktop/resolve1","org.freedesktop.resolve1.Manager", + "RevertLink","i",str(idx)) + if unit_state(s["ctrldUnit"])=="active": + raw=call("systemctl","show",s["ctrldUnit"],"-p","MainPID","--value").stdout.strip() + pid=int(raw) + need(pid>1 and os.path.realpath(f"/proc/{pid}/exe")==os.path.realpath(CTRLD), + "OWNED_CTRLD_PROCESS_MISMATCH") + call("systemctl","stop",s["ctrldUnit"],seconds=16) + need(unit_state(s["ctrldUnit"])!="active","OWNED_CTRLD_NOT_STOPPED") + if idx is not None: + need(dummy_identity(s)==idx,"LINK_REPLACED_DURING_ROLLBACK") + call("ip","link","del","dev",IFACE) + need(not pathlib.Path("/sys/class/net",IFACE).exists(),"DUMMY_STILL_EXISTS") + if stop_monitor and unit_state(s["monitorUnit"])=="active": + call("systemctl","stop",s["monitorUnit"],seconds=15) + if unit_state(s["backstopTimer"],True)=="active": + call("systemctl","stop",s["backstopTimer"],seconds=15) + need(unit_state(s["backstopTimer"],True)!="active","TIMER_STILL_ACTIVE") + need(CONFIG.is_file() and not CONFIG.is_symlink() and + CONFIG.stat().st_uid==0 and sha(CONFIG.read_bytes())==s["configSha"], + "CONFIG_FOREIGN_PRESERVED") + CONFIG.unlink() + STATE.unlink() + if STATE_DIR.is_dir() and not any(STATE_DIR.iterdir()): STATE_DIR.rmdir() + return {"ok":True,"state":"RESTORED","reason":reason} + + +def activate(uid: int, iface: str, home: str) -> dict: + root() + require_consent(uid,home) + baseline=network_preflight(iface) + fingerprint=tailscale_fingerprint() + policy=policy_bytes(baseline["dns"][0]) + STATE_DIR.mkdir(mode=0o700) + os.chmod(STATE_DIR,0o700) + token=uuid.uuid4().hex[:10] + s={ + "schema":1,"phase":"ARMING","uid":uid,"session":token, + "userHome":home,"physical":iface,"physicalBaseline":baseline, + "tailscaleDNS":fingerprint,"ctrldSha":VERSIONED_CTRLD_SHA, + "configPath":str(CONFIG),"configSha":sha(policy), + "iface":IFACE,"ownedIp":CIDR,"port":PORT,"dummyIndex":None, + "started":time.time(), + "ctrldUnit":f"dim-edsn-dns-{uid}-{token}.service", + "monitorUnit":f"dim-edsn-watch-{uid}-{token}.service", + "backstopTimer":f"dim-edsn-backstop-{uid}-{token}.timer" + } + try: + write_state(s) + save(CONFIG,policy,0o644) + call("systemd-run","--quiet","--unit="+s["monitorUnit"][:-8], + "--service-type=exec","--property=Restart=on-failure", + "--property=RestartSec=3s","/usr/bin/python3",str(SOURCE),"monitor") + need(unit_state(s["monitorUnit"])=="active","MONITOR_NOT_RUNNING") + call("systemd-run","--quiet","--unit="+s["backstopTimer"][:-6], + "--on-active="+str(BACKSTOP_SEC)+"s", + "--on-unit-active="+str(BACKSTOP_SEC)+"s", + "--timer-property=AccuracySec=1s", + "/usr/bin/python3",str(SOURCE),"backstop") + need(unit_state(s["backstopTimer"],True)=="active","INDEPENDENT_BACKSTOP_NOT_ARMED") + call("ip","link","add",IFACE,"type","dummy") + call("ip","addr","add",CIDR,"dev",IFACE) + call("ip","link","set","dev",IFACE,"up") + s["dummyIndex"]=int(pathlib.Path("/sys/class/net",IFACE,"ifindex").read_text()) + write_state(s) + need(dummy_identity(s)==s["dummyIndex"],"NEW_DUMMY_IDENTITY") + user=pwd.getpwuid(uid).pw_name + call("systemd-run","--quiet","--unit="+s["ctrldUnit"][:-8], + "--service-type=exec","--property=Restart=on-failure", + "--property=RestartSec=3s","--property=User="+user, + "--property=NoNewPrivileges=yes",str(CTRLD),"run","--config",str(CONFIG)) + need(unit_state(s["ctrldUnit"])=="active","CTRLD_NOT_ACTIVE") + for target in ("www.youtube.com","redirector.googlevideo.com"): + need(public_dns(target),"TARGET_DOH_PUBLIC_"+target) + runtime_tunnel_guard(iface) + link_dns(s["dummyIndex"]) + need(dns_is_owned(),"DBUS_DNS_OR_DOMAINS_NOT_OWNED") + need(link_state(iface)==baseline,"PHYSICAL_LINK_DNS_CHANGED") + need(tailscale_fingerprint()==fingerprint,"MAGICDNS_CHANGED") + need(json.loads(call("tailscale","status","--json").stdout).get("BackendState")=="Running", + "TAILSCALE_DOWN") + need(public_dns("www.youtube.com"),"DOH_AFTER_NATIVE_DNS_MISSING") + return {"ok":True,"state":"ARMED","session":token,"encryptedTargetDns":True} + except BaseException: + try:rollback("ACTIVATION_FAILED") + except BaseException:pass + raise + + +def native_engine_attested(state: dict) -> bool: + """Cross-check the user-readable native state against *actual* root service. + + A writable state.json is never sufficient authority for protected Commit. + """ + uid=state["uid"] + app=(pathlib.Path(state["userHome"])/ + ".local/share/DirectInternetMethod/directmethod/state.json") + need(app.is_file() and not app.is_symlink(),"NATIVE_STATE_MISSING_OR_SYMLINK") + st=app.stat() + need(st.st_uid==uid and st.st_size>40 and st.st_size<16384 and + st.st_mode & 0o077==0,"NATIVE_STATE_OWNERSHIP") + doc=json.loads(app.read_text(encoding="utf8")) + unit=f"directinternetmethod-nfqws-{uid}.service" + need(doc.get("schema")==3 and doc.get("status")=="ACTIVE" and + doc.get("tailscaleCoexistence") is True and + doc.get("dnsMode")=="system-preserved" and + doc.get("selectiveDnsEncrypted") is True and + doc.get("selectiveDns") is False and + doc.get("physicalInterface")==state["physical"] and + doc.get("scope")=="targeted" and doc.get("strategy")=="balanced" and + doc.get("nfqwsUnit")==unit,"NATIVE_STATE_FIELDS") + pid=doc.get("nfqwsPid") + need(type(pid) is int and pid>1 and + NFQWS.is_file() and file_sha(NFQWS)==VERSIONED_NFQWS_SHA, + "NATIVE_NFQWS_BINARY_SHA") + need(call("systemctl","show",unit,"-p","ActiveState","--value").stdout.strip()=="active", + "NATIVE_NFQWS_UNIT_INACTIVE") + number=call("systemctl","show",unit,"-p","MainPID","--value").stdout.strip() + need(number.isdigit() and int(number)==pid,"NATIVE_NFQWS_PID_MISMATCH") + need(os.path.realpath(f"/proc/{pid}/exe")==os.path.realpath(NFQWS), + "NATIVE_NFQWS_PROCESS_FOREIGN") + return True + + +def commit() -> dict: + root() + s=read_state() + need(s is not None and s["phase"]=="ARMING","COMMIT_WITHOUT_ARMING") + need(native_engine_attested(s),"NATIVE_ENGINE_UNCOMMITTED") + need(unit_state(s["monitorUnit"])=="active" and + unit_state(s["ctrldUnit"])=="active" and + unit_state(s["backstopTimer"],True)=="active","OWNED_UNITS_LOST") + need(dummy_identity(s)==s["dummyIndex"] and dns_is_owned(), + "PER_LINK_SECURE_DNS_DRIFT") + s["phase"]="ACTIVE" + s["committed"]=time.time() + write_state(s) + return {"ok":True,"state":"ACTIVE","encryptedTargetDns":True} + + +@contextmanager +def observe_action_lock(): + fd=os.open(ACTION_LOCK,os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW,0o600) + try: + try:fcntl.flock(fd,fcntl.LOCK_EX|fcntl.LOCK_NB) + except BlockingIOError:yield False + else:yield True + finally:os.close(fd) + + +def monitor() -> None: + root() + while True: + s=read_state() + if s is None:return + if s["phase"] in ("STOPPING","DEGRADED"): + time.sleep(MONITOR_SEC);continue + failed=None + with observe_action_lock() as available: + if available: + if s["phase"]=="ARMING": + if time.time()-s["started"]>GRACE_SEC: + failed="START_GRACE_EXPIRED" + elif s["phase"]=="ACTIVE": + try: + runtime_tunnel_guard(s["physical"]) + need(dummy_identity(s)==s["dummyIndex"] and dns_is_owned(), + "DNS_DRIFT") + need(unit_state(s["ctrldUnit"])=="active","CTRLD_DIED") + need(tailscale_fingerprint()==s["tailscaleDNS"],"MAGICDNS_DRIFT") + need(link_state(s["physical"])==s["physicalBaseline"], + "PHYSICAL_DNS_DRIFT") + need(json.loads(call("tailscale","status","--json").stdout) + .get("BackendState")=="Running","TAILSCALE_DIED") + except Exception as err:failed=str(err) + if failed:rollback("MONITOR:"+failed,stop_monitor=False) + if failed: + call("systemctl","start","directinternetmethod-recovery.service", + seconds=35,ok=False) + return + time.sleep(MONITOR_SEC) + + +def backstop() -> dict: + root() + recover=None + with observe_action_lock() as available: + if not available:return {"ok":True,"state":"ACTION_BUSY_NEXT_TICK"} + s=read_state() + if s is None:return {"ok":True,"state":"INACTIVE"} + if s["phase"]=="ARMING" and time.time()-s["started"]<=GRACE_SEC: + return {"ok":True,"state":"START_PENDING"} + if s["phase"]=="ACTIVE": + try: + runtime_tunnel_guard(s["physical"]) + need(unit_state(s["monitorUnit"])=="active","MONITOR_DIED") + need(unit_state(s["ctrldUnit"])=="active","CTRLD_DIED") + need(dummy_identity(s)==s["dummyIndex"] and dns_is_owned(), + "LINK_DNS_DRIFT") + need(link_state(s["physical"])==s["physicalBaseline"], + "WAN_DNS_DRIFT") + need(tailscale_fingerprint()==s["tailscaleDNS"],"TAILSCALE_DNS_DRIFT") + return {"ok":True,"state":"HEALTHY"} + except Exception as e:recover=str(e) + else:recover="INCOMPLETE_PHASE_"+s["phase"] + rollback("INDEPENDENT_BACKSTOP:"+recover) + call("systemctl","start","directinternetmethod-recovery.service", + seconds=35,ok=False) + return {"ok":True,"state":"BACKSTOP_RECOVERED","reason":recover} + + +def status() -> dict: + s=read_state() + if s is None:return {"ok":True,"state":"INACTIVE"} + return {"ok":True,"state":s["phase"],"iface":s["iface"], + "encrypted":True,"monitor":unit_state(s["monitorUnit"]), + "ctrld":unit_state(s["ctrldUnit"]), + "backstop":unit_state(s["backstopTimer"],True)} + + +def main() -> int: + ap=argparse.ArgumentParser() + ap.add_argument("action",choices=("activate","commit","restore","monitor","backstop","status")) + ap.add_argument("--uid",type=int) + ap.add_argument("--interface") + ap.add_argument("--home") + args=ap.parse_args() + try: + if args.action=="activate":result=activate(args.uid,args.interface,args.home) + elif args.action=="commit":result=commit() + elif args.action=="restore":result=rollback() + elif args.action=="monitor":monitor();result={"ok":True,"state":"MONITOR_DONE"} + elif args.action=="backstop":result=backstop() + else:result=status() + print(json.dumps(result,sort_keys=True)) + return 0 + except Exception as exc: + print(json.dumps({"ok":False,"error":str(exc)[:280]}),file=sys.stderr) + return 83 + + +if __name__=="__main__": + sys.exit(main()) diff --git a/linux/system/encrypted_selective_policy.py b/linux/system/encrypted_selective_policy.py new file mode 100644 index 00000000..cff09d87 --- /dev/null +++ b/linux/system/encrypted_selective_policy.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Pure v1.5.7 ctrld targeted DoH policy generator (R516 candidate, NOT deployed). + +Produces a loopback-only listener; exact targeted names use pinned HTTPS DoH, +all other names retain the pre-existing RFC1918 physical DNS as legacy upstream. +No network operations, files, service changes, install or global resolver edits. +Any future runtime controller MUST add native ownership/rollback and V&V first. +""" +from __future__ import annotations + +import ipaddress + +TARGET_DOMAINS = ("youtube.com", "googlevideo.com", "ytimg.com", "youtu.be") +TLS_DOH_ENDPOINT = "https://76.76.10.11/p0" +TLS_DOH_IP = "76.76.10.11" + + +def _physical_dns(address: str) -> str: + if type(address) is not str or not address or address.strip() != address: + raise ValueError("PHYSICAL_DNS_INVALID_TYPE_OR_SPACE") + try: + addr = ipaddress.IPv4Address(address) + except (ipaddress.AddressValueError, ValueError) as exc: + raise ValueError("PHYSICAL_DNS_NOT_LITERAL_IPV4") from exc + # Accept only RFC1918 LAN resolvers: never silently configure public, + # reserved, loopback or link-local fallback as part of targeted mode. + lan = ( + ipaddress.IPv4Network("10.0.0.0/8"), + ipaddress.IPv4Network("172.16.0.0/12"), + ipaddress.IPv4Network("192.168.0.0/16"), + ) + if not any(addr in network for network in lan): + raise ValueError("PHYSICAL_DNS_NOT_RFC1918_LAN") + return str(addr) + + +def build_secure_targeted_dns_config(physical_dns: str, listener_port: int = 10535) -> bytes: + """Return deterministic ctrld v1.5.7 TOML with documented rule precedence. + + This pure function does not authorize use as system DNS. + """ + router = _physical_dns(physical_dns) + if type(listener_port) is not int or not 1024 <= listener_port <= 65535: + raise ValueError("PORT_UNSAFE_OR_PRIVILEGED") + # Validated on independent MMZ network as 127.0.0.1:53534. Config is + # rendered byte-for-byte in that form for the validated router/port pair. + return f"""[service] +log_level = "error" +cache_enable = false +leak_on_upstream_failure = false +discover_mdns = false +discover_arp = false +discover_dhcp = false +discover_ptr = false +discover_hosts = false + +[network.0] +cidrs = ["127.0.0.1/32"] +name = "LoopbackOnly" + +[upstream.0] +bootstrap_ip = "76.76.10.11" +endpoint = "https://76.76.10.11/p0" +name = "EncryptedPinnedControlD" +timeout = 5000 +type = "doh" +ip_stack = "v4" + +[upstream.1] +endpoint = "{router}" +name = "ExistingPhysicalDNS" +timeout = 3000 +type = "legacy" +ip_stack = "v4" + +[listener.0] +ip = "127.0.0.1" +port = {listener_port} +restricted = false +allow_wan_clients = false + +[listener.0.policy] +name = "EncryptedTargetedDoH_OriginalFallback" +rules = [ + {{"youtube.com" = ["upstream.0"]}}, + {{"*.youtube.com" = ["upstream.0"]}}, + {{"googlevideo.com" = ["upstream.0"]}}, + {{"*.googlevideo.com" = ["upstream.0"]}}, + {{"ytimg.com" = ["upstream.0"]}}, + {{"*.ytimg.com" = ["upstream.0"]}}, + {{"youtu.be" = ["upstream.0"]}}, + {{"*.youtu.be" = ["upstream.0"]}}, +] +networks = [ + {{"network.0" = ["upstream.1"]}}, +] +""".encode("ascii") + + +# Protected native per-link mode. The exact kernel link and IP are controller-owned. +# Never accept user-supplied arbitrary bind addresses or subnets. +OWNED_DNS_INTERFACE = "dimesd0" +OWNED_DNS_IPV4 = "172.31.254.59" +OWNED_DNS_PORT = 53534 + + +def build_owned_interface_policy(physical_dns: str) -> bytes: + """Render the tested targeted DoH policy for one owned /32 dummy interface. + + It is not a runtime migration or permission to alter a resolver. A native + controller must prove ownership and arm an independent rollback first. + """ + raw = build_secure_targeted_dns_config(physical_dns, OWNED_DNS_PORT) + old_network = b'cidrs = ["127.0.0.1/32"]' + old_listener = b'ip = "127.0.0.1"' + if raw.count(old_network) != 1 or raw.count(old_listener) != 1: + raise ValueError("SOURCE_POLICY_SHAPE_CHANGED") + result = raw.replace( + old_network, b'cidrs = ["172.31.254.59/32"]' + ).replace( + old_listener, b'ip = "172.31.254.59"' + ) + if b'0.0.0.0' in result or b'192.168.8.1' in result and physical_dns != "192.168.8.1": + raise ValueError("POLICY_BINDING_OR_ROUTER_DRIFT") + return result diff --git a/linux/system/install_system.sh b/linux/system/install_system.sh index a3ffce5a..42b3281d 100644 --- a/linux/system/install_system.sh +++ b/linux/system/install_system.sh @@ -19,9 +19,26 @@ ROOT=/usr/lib/directinternetmethod echo "Stop/Recovery selective DNS before protected installation." >&2 exit 88 } +# R517 protected backend may only be staged with all network owners OFF. +[ ! -e "$HOME_DIR/.local/share/DirectInternetMethod/directmethod/state.json" ] || { + echo "Stop Direct Internet Method before protected backend update." >&2 + exit 88 +} +for owned in /run/directinternetmethod-encrypted /run/directinternetmethod-encrypted-ctrld.toml /sys/class/net/dimesd0; do + [ ! -e "$owned" ] && [ ! -L "$owned" ] || { + echo "Stop/Recovery encrypted selective DNS before protected backend update." >&2 + exit 88 + } +done +for source in "$SRC/system/encrypted_selective_dns.py" "$SRC/system/encrypted_selective_policy.py"; do + [ -f "$source" ] && [ ! -L "$source" ] && [ -s "$source" ] || + { echo "Incomplete candidate encrypted backend; refusing protected installation." >&2; exit 88; } +done install -d -m 0755 "$ROOT" "$ROOT/runtime" "$ROOT/runtime/usr" "$ROOT/runtime/usr/bin" "$ROOT/licenses" install -m 0755 "$SRC/app/direct_method_helper.sh" "$ROOT/direct_method_helper.sh" install -m 0755 "$SRC/system/selective_dns.py" "$ROOT/selective_dns.py" +install -m 0755 "$SRC/system/encrypted_selective_dns.py" "$ROOT/encrypted_selective_dns.py" +install -m 0644 "$SRC/system/encrypted_selective_policy.py" "$ROOT/encrypted_selective_policy.py" install -m 0755 "$SRC/system/control.sh" "$ROOT/control.sh" install -m 0755 "$SRC/system/uninstall_system.sh" "$ROOT/uninstall_system.sh" install -m 0644 "$SRC/app/hosts.txt" "$ROOT/direct_hosts.txt" @@ -84,12 +101,14 @@ systemctl daemon-reload test -x "$ROOT/control.sh" test -x "$ROOT/direct_method_helper.sh" test -x "$ROOT/selective_dns.py" +test -x "$ROOT/encrypted_selective_dns.py" +test -f "$ROOT/encrypted_selective_policy.py" for action in start stop recovery; do test -f "/etc/systemd/system/directinternetmethod-$action.service"; done test -f /etc/polkit-1/rules.d/49-directinternetmethod.rules test "$(cat /etc/directinternetmethod.uid)" = "$USER_UID" install -d -m 0755 /var/lib/directinternetmethod cat > /var/lib/directinternetmethod/install.json <0 and uid<2**31,"OWNER_UID") + need(re.fullmatch(r"[a-z_][a-z0-9_-]*[$]?",str(d.get("user",""))),"OWNER_USER") + home=d.get("home") + need(isinstance(home,str) and home.startswith("/home/") and + ".." not in pathlib.PurePosixPath(home).parts,"OWNER_HOME") + idfile=self.p("etc/directinternetmethod.uid") + need(no_symlink_regular(idfile,self.native) and idfile.read_text().strip()==str(uid), + "UID_MARKER_CHANGED") + return d + def _baseline(self): + before=self.original_metadata() + need(self.base.is_dir() and not self.base.is_symlink(),"BASE_DIR_UNSAFE") + if self.native: + need(self.base.stat().st_uid==0,"BASE_DIR_NOT_ROOT") + # Fail before protected mutation if any live owned DNS or DPI state. + need(not pathlib.Path("/run/directinternetmethod-encrypted").exists() and + not pathlib.Path("/run/directinternetmethod-encrypted-ctrld.toml").exists() and + not pathlib.Path("/run/directinternetmethod-selective/session.json").exists() and + not pathlib.Path("/sys/class/net/dimesd0").exists(),"ACTIVE_DNS_RESOURCE") + home=pathlib.Path(before["home"])/".local/share/DirectInternetMethod/directmethod/state.json" + need(not home.exists(),"ACTIVE_DIRECT_METHOD") + for rel,want in STATIC_ROOT.items(): + p=self.p(rel) + no_symlink_regular(p,self.native) + need(hash_file(p)==want,"BASELINE_STATIC_SHA_"+pathlib.Path(rel).name) + for name,src,target,old,new,mode in FILES: + p=self.p(target) + if old: + s=no_symlink_regular(p,self.native) + need(stat.S_IMODE(s.st_mode)==mode,"OLD_FILE_MODE_"+name) + need(hash_file(p)==old,"OLD_FILE_SHA_"+name) + else: + need(not p.exists() and not p.is_symlink(),"NEW_TARGET_NOT_EMPTY_"+name) + return before + @contextmanager + def exclusive(self): + """Serialize with protected upgrade AND the real native Start/Stop lock.""" + need(self.base.is_dir() and not self.base.is_symlink(),"BASE_MISSING") + action=self.root/ACTION_LOCK + need(action.parent.is_dir() and not action.parent.is_symlink(), + "NATIVE_ACTION_LOCK_PARENT_UNSAFE") + if self.native: + need(self.base.stat().st_uid==0 and action.parent.stat().st_uid==0, + "ROOT_LOCK_PARENT_UNOWNED") + f=os.open(self.lock,os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW,0o600) + try: + fcntl.flock(f,fcntl.LOCK_EX|fcntl.LOCK_NB) + a=os.open(action,os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW,0o600) + try: + if self.native: + st=os.fstat(a) + need(st.st_uid==0 and stat.S_ISREG(st.st_mode), + "NATIVE_ACTION_LOCK_FOREIGN") + fcntl.flock(a,fcntl.LOCK_EX|fcntl.LOCK_NB) + yield + finally: + os.close(a) + finally: + os.close(f) + def manifest(self): + need(self.pending.is_dir() and not self.pending.is_symlink(),"SLOT_MISSING_OR_SYMLINK") + if self.native: + s=self.pending.stat() + need(s.st_uid==0 and stat.S_IMODE(s.st_mode)==0o700,"SLOT_UNOWNED") + p=self.pending/"manifest.json" + no_symlink_regular(p,self.native) + data=json.loads(p.read_text("utf8")) + need(data.get("schema")==SCHEMA and data.get("revision")=="R519" and + data.get("fromVersion")==CURRENT_BACKEND_VERSION and + data.get("toVersion")==TARGET_VERSION,"MANIFEST_VERSION") + need(data.get("phase") in ("STAGED","APPLIED","ROLLED_BACK","DEGRADED","COMMITTED"), + "MANIFEST_PHASE") + need(set(data.get("files",{}))=={row[0] for row in FILES},"MANIFEST_FILE_LIST") + return data + def write_manifest(self,data): + atomic_bytes(self.pending/"manifest.json",canonical(data),0o600,self.native) + def stage(self,source): + with self.exclusive(): + need(not self.pending.exists() and not self.pending.is_symlink(),"SLOT_ALREADY_EXISTS") + before=self._baseline() + src=pathlib.Path(source) + need(src.is_dir() and not src.is_symlink(),"PACKAGE_SOURCE") + # Source may be user-owned (pkexec), but its bytes are pinned to + # the independently tested GitHub candidate; only mapped files read. + data={} + for name,rel,target,old,want,mode in FILES: + file=src/rel + no_symlink_regular(file,False) + blob=file.read_bytes() + need(sha(blob)==want,"PACKAGE_SOURCE_SHA_"+name) + data[name]=blob + self.pending.mkdir(mode=0o700) + (self.pending/"old").mkdir(mode=0o700) + (self.pending/"new").mkdir(mode=0o700) + try: + entries={} + for name,rel,target,old,new,mode in FILES: + atomic_bytes(self.pending/"new"/name,data[name],0o600,self.native) + if old: + atomic_bytes(self.pending/"old"/name,self.p(target).read_bytes(),0o600,self.native) + entries[name]={"target":target,"old":old,"new":new,"mode":mode} + orig=self.meta.read_bytes() + atomic_bytes(self.pending/"old"/"metadata",orig,0o600,self.native) + new_meta=dict(before,version=TARGET_VERSION) + new_blob=canonical(new_meta) + atomic_bytes(self.pending/"new"/"metadata",new_blob,0o600,self.native) + payload={"schema":SCHEMA,"revision":"R519","phase":"STAGED", + "fromVersion":CURRENT_BACKEND_VERSION,"toVersion":TARGET_VERSION, + "files":entries,"oldMetadataSHA":sha(orig),"newMetadataSHA":sha(new_blob), + "uid":before["uid"],"home":before["home"],"user":before["user"]} + self.write_manifest(payload) + return {"status":"STAGED","files":len(FILES),"version":TARGET_VERSION} + except BaseException: + # Preserve evidence rather than silently delete a partial slot. + raise + def _validate_slot(self,m): + need(self.pending.is_dir() and not self.pending.is_symlink(),"SLOT_UNSAFE") + for name,rel,target,old,new,mode in FILES: + x=m["files"][name] + need(x=={"target":target,"old":old,"new":new,"mode":mode},"MANIFEST_FILE_TAMPER_"+name) + nf=self.pending/"new"/name + need(no_symlink_regular(nf,self.native) and hash_file(nf)==new, + "PENDING_NEW_SHA_"+name) + if old: + backup=self.pending/"old"/name + need(no_symlink_regular(backup,self.native) and hash_file(backup)==old, + "PENDING_OLD_SHA_"+name) + for key,dir,want in (("metadata","old",m["oldMetadataSHA"]), + ("metadata","new",m["newMetadataSHA"])): + file=self.pending/dir/key + need(no_symlink_regular(file,self.native) and hash_file(file)==want, + "PENDING_METADATA_"+dir) + need(m["uid"]>0 and m["home"].startswith("/home/"),"IDENTITY_CHANGED") + def _target_states(self,m): + result={} + for name,rel,target,old,new,mode in FILES: + file=self.p(target) + if not file.exists() and not file.is_symlink(): + digest=None + else: + no_symlink_regular(file,self.native) + digest=hash_file(file) + need(digest in (old,new),"FOREIGN_TARGET_MODIFICATION_"+name) + result[name]=digest + no_symlink_regular(self.meta,self.native) + meta_sha=hash_file(self.meta) + need(meta_sha in (m["oldMetadataSHA"],m["newMetadataSHA"]), + "FOREIGN_METADATA_CHANGE") + return result,meta_sha + def apply(self,fail_after=None): + with self.exclusive(): + m=self.manifest() + need(m["phase"]=="STAGED","UNSAFE_APPLY_RERUN") + self._validate_slot(m) + before,meta=self._target_states(m) + for name,rel,target,old,new,mode in FILES: + need(before[name]==old,"BASELINE_CHANGED_"+name) + need(meta==m["oldMetadataSHA"],"OLD_META_CHANGED") + changed=0 + try: + for name,rel,target,old,new,mode in FILES: + atomic_bytes(self.p(target),(self.pending/"new"/name).read_bytes(), + mode,self.native) + changed+=1 + if fail_after is not None and changed>=fail_after: + raise RuntimeError("INJECTED_TEST_FAILURE_AFTER_"+str(changed)) + atomic_bytes(self.meta,(self.pending/"new"/"metadata").read_bytes(), + 0o644,self.native) + states,now=self._target_states(m) + need(all(states[name]==new for name,src,target,old,new,mode in FILES) and + now==m["newMetadataSHA"],"POST_UPGRADE_HASHES") + m["phase"]="APPLIED" + self.write_manifest(m) + return {"status":"APPLIED","changed":changed,"version":TARGET_VERSION} + except BaseException: + # Rollback is owned by this transaction. Preserve slot if + # corrupted/ambiguous; never proceed blind. + try:self._rollback_locked(m) + except BaseException: + m["phase"]="DEGRADED";self.write_manifest(m) + raise + def _rollback_locked(self,m): + self._validate_slot(m) + states,metadata=self._target_states(m) + # Verify ALL targets before a single mutation. + for name,rel,target,old,new,mode in FILES: + need(states[name] in (old,new),"FOREIGN_TARGET_"+name) + for name,rel,target,old,new,mode in FILES: + if states[name]==new: + p=self.p(target) + if old is None: + need(not p.is_symlink() and hash_file(p)==new,"FOREIGN_NEW_"+name) + p.unlink() + flush_dir(p.parent) + else: + atomic_bytes(p,(self.pending/"old"/name).read_bytes(),mode,self.native) + if metadata==m["newMetadataSHA"]: + atomic_bytes(self.meta,(self.pending/"old"/"metadata").read_bytes(), + 0o644,self.native) + for name,rel,target,old,new,mode in FILES: + p=self.p(target) + if old:need(hash_file(p)==old,"ROLLBACK_SHA_"+name) + else:need(not p.exists(),"ROLLBACK_NEW_REMAINS_"+name) + need(hash_file(self.meta)==m["oldMetadataSHA"],"ROLLBACK_METADATA_SHA") + m["phase"]="ROLLED_BACK" + self.write_manifest(m) + return {"status":"ROLLED_BACK","baselineRestored":True} + def rollback(self): + with self.exclusive(): + m=self.manifest() + need(m["phase"] in ("STAGED","APPLIED","DEGRADED"),"ROLLBACK_ALREADY_COMMITTED_OR_DONE") + return self._rollback_locked(m) + def commit(self,acceptance): + with self.exclusive(): + m=self.manifest() + need(m["phase"]=="APPLIED","COMMIT_UNACCEPTED_PHASE") + self._validate_slot(m) + states,meta=self._target_states(m) + need(all(states[name]==new for name,rel,target,old,new,mode in FILES), + "COMMIT_BACKEND_CHANGED") + need(meta==m["newMetadataSHA"],"COMMIT_META_CHANGED") + acceptance=pathlib.Path(acceptance) + need(acceptance.is_file() and not acceptance.is_symlink(), + "NATIVE_ACCEPTANCE_MISSING") + receipt=json.loads(acceptance.read_text("utf8")) + need(receipt.get("revision")=="R519" and + receipt.get("status")=="PASS_NATIVE_ROOT_UPGRADE" and + receipt.get("sourceRootSHA")==sha(canonical(m["files"])) and + receipt.get("protectedMetadataSHA")==meta and + receipt.get("nativeStartStopRecovery") is True and + receipt.get("tailscalePreserved") is True and + receipt.get("physicalDNSPreserved") is True, + "NATIVE_ACCEPTANCE_NOT_PROVEN") + m["phase"]="COMMITTED" + self.write_manifest(m) + # The acceptance and parent lineage must be exported into Project + # Brain before a separate maintenance cleanup is authorized. + return {"status":"COMMITTED","backupRetainedUntilBrainPromotion":True} + def status(self): + if not self.pending.exists():return {"status":"NO_PENDING_UPGRADE"} + m=self.manifest() + return {"status":m["phase"],"version":m["toVersion"],"uid":m["uid"]} + +def main(): + p=argparse.ArgumentParser() + p.add_argument("action",choices=("stage","apply","rollback","commit","status")) + p.add_argument("--source") + p.add_argument("--acceptance") + args=p.parse_args() + need(os.geteuid()==0,"ROOT_REQUIRED") + t=RootTransaction() # No CLI root-prefix/dry-run/test-mode option. + if args.action=="stage":need(bool(args.source),"SOURCE_REQUIRED");result=t.stage(args.source) + elif args.action=="apply":result=t.apply() + elif args.action=="rollback":result=t.rollback() + elif args.action=="commit":need(bool(args.acceptance),"ACCEPTANCE_REQUIRED");result=t.commit(args.acceptance) + else:result=t.status() + print(json.dumps(result,sort_keys=True)) +if __name__=="__main__": + try:main() + except Exception as e: + print(json.dumps({"ok":False,"error":str(e)[:200]}),file=sys.stderr) + sys.exit(82) diff --git a/linux/system/uninstall_system.sh b/linux/system/uninstall_system.sh index d9d5d69e..012797eb 100644 --- a/linux/system/uninstall_system.sh +++ b/linux/system/uninstall_system.sh @@ -6,6 +6,17 @@ if [ -e /run/directinternetmethod-selective/session.json ]; then /usr/bin/python3 /usr/lib/directinternetmethod/selective_dns.py restore || exit 88 [ ! -e /run/directinternetmethod-selective/session.json ] || exit 88 fi +# Fail closed on encrypted-DNS ownership ambiguity before uninstalling binaries. +if [ -e /run/directinternetmethod-encrypted/session.json ]; then + [ -x /usr/lib/directinternetmethod/encrypted_selective_dns.py ] || exit 88 + /usr/bin/python3 /usr/lib/directinternetmethod/encrypted_selective_dns.py restore || exit 88 +fi +for owned in /run/directinternetmethod-encrypted /run/directinternetmethod-encrypted-ctrld.toml /sys/class/net/dimesd0; do + [ ! -e "$owned" ] && [ ! -L "$owned" ] || { + echo "Encrypted DNS resource remains; do not remove protected backend." >&2 + exit 88 + } +done for action in start stop recovery; do rm -f "/etc/systemd/system/directinternetmethod-$action.service" done diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh new file mode 100644 index 00000000..323523ad --- /dev/null +++ b/linux/system/upgrade_system.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# R519 unreleased candidate; single privileged entrypoint for protected upgrade. +# Only after user transaction has verified and staged the exact extracted package. +# Never auto-commit root; native acceptance and owner review are separate. +set -Eeuo pipefail +umask 077 +SRC="${1:-}" +REQ_UID="${2:-}" +TX_SHA="4CE00525AF4773FE25CB7E3E5EDFD661B6A30C6B677964E12F24BCA6650F328D" +[ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } +case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac +[ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 +[ -d "$SRC" ] && [ ! -L "$SRC" ] && [ -f "$SRC/system/protected_upgrade_transaction.py" ] || { + echo "UPGRADE_SOURCE_MISSING_OR_SYMLINK" >&2; exit 77; +} +[ "$(basename "$SRC")" = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" ] || { + echo "WRONG_PACKAGE_LAYOUT" >&2; exit 77; +} +owner_uid="$(stat -c %u -- "$SRC")" +[ "$owner_uid" = "$REQ_UID" ] || { echo 'PACKAGE_OWNER_MISMATCH' >&2; exit 77; } +[ "$(sha256sum "$SRC/system/protected_upgrade_transaction.py" | cut -d' ' -f1)" = \ + "$(printf '%s' "$TX_SHA" | tr '[:upper:]' '[:lower:]')" ] || { + echo "UPGRADER_SOURCE_SHA_CHANGED" >&2; exit 77; +} +[ "$(cat /etc/directinternetmethod.uid 2>/dev/null)" = "$REQ_UID" ] || { + echo "PROTECTED_ACCOUNT_ID_CHANGED" >&2; exit 77; +} +python3 - "$REQ_UID" <<'PY' +from pathlib import Path +import json,sys,stat +expected=int(sys.argv[1]) +p=Path('/var/lib/directinternetmethod/install.json') +assert p.is_file() and not p.is_symlink() +s=p.stat() +assert s.st_uid==0 and stat.S_IMODE(s.st_mode)==0o644 +m=json.loads(p.read_text('utf8')) +assert m['schema']==1 and m['version']=='1.5.2' and m['uid']==expected +assert m['home'].startswith('/home/') +PY +# Transactional stage + apply obtain native action lock; one rollback slot. +python3 "$SRC/system/protected_upgrade_transaction.py" stage --source "$SRC" +python3 "$SRC/system/protected_upgrade_transaction.py" apply +python3 "$SRC/system/protected_upgrade_transaction.py" status | + python3 -c 'import json,sys;j=json.load(sys.stdin);assert j["status"]=="APPLIED" and j["version"]=="1.5.5-rc1"' +echo '{"status":"ROOT_R519_APPLIED_UNCOMMITTED","needsRealNativeAcceptance":true}' diff --git a/tests/encrypted_selective_dns_controller_contract.py b/tests/encrypted_selective_dns_controller_contract.py new file mode 100644 index 00000000..834ebcff --- /dev/null +++ b/tests/encrypted_selective_dns_controller_contract.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python3 +"""R517 negative-first, host-mutating commands fully mocked. Run on Linux user. +Raises if a potentially dangerous API command changes contract or live host +would be touched. No service/network/files outside a test-private directory. +""" +from __future__ import annotations +import importlib.util +import ipaddress +import json +import os +from pathlib import Path +import re +import shutil +import tempfile +import tomllib +from types import SimpleNamespace +import unittest +from unittest.mock import patch + +ROOT=Path(__file__).resolve().parents[1] +CONTROLLER=ROOT/"linux/system/encrypted_selective_dns.py" +POLICY=ROOT/"linux/system/encrypted_selective_policy.py" +def import_source(name:str,path:Path): + spec=importlib.util.spec_from_file_location(name,path) + assert spec is not None and spec.loader is not None + mod=importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod +m=import_source("r517_safe_controller",CONTROLLER) +g=import_source("r517_safe_policy",POLICY) +STATE={ + "schema":1,"phase":"ACTIVE","uid":1000,"session":"f0e1d2c3b4", + "userHome":"/home/mmz","physical":"enp7s0", + "physicalBaseline":{"dns":["192.168.8.1"],"domains":[],"default":True}, + "tailscaleDNS":"A"*64,"ctrldSha":m.VERSIONED_CTRLD_SHA, + "configPath":str(m.CONFIG),"configSha":"B"*64, + "iface":m.IFACE,"ownedIp":m.CIDR,"port":m.PORT,"dummyIndex":7, + "started":0.0, + "ctrldUnit":"dim-edsn-dns-1000-f0e1d2c3b4.service", + "monitorUnit":"dim-edsn-watch-1000-f0e1d2c3b4.service", + "backstopTimer":"dim-edsn-backstop-1000-f0e1d2c3b4.timer", +} +COMMANDS=[] +def fail_if_called(*args,**kwargs): + COMMANDS.append((args,kwargs)) + raise AssertionError("UNEXPECTED_HOST_OR_MUTATING_COMMAND: "+repr(args)) + +class Contract(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.temp=tempfile.TemporaryDirectory(prefix="R517_nohost_",dir=str(ROOT)) + cls.home=Path(cls.temp.name) + cls.app=cls.home/".local/share/DirectInternetMethod" + cls.app.mkdir(parents=True) + cls.uid=os.getuid() + cls.tests=0 + @classmethod + def tearDownClass(cls): + cls.temp.cleanup() + def setUp(self): + COMMANDS.clear() + if self.app.exists(): shutil.rmtree(self.app) + self.app.mkdir(parents=True) + def pass_if_reject(self,func,reason): + with self.assertRaisesRegex((RuntimeError,ValueError),reason): + func() + def make_consent(self): + self.app.joinpath("strategy.txt").write_text("balanced\n") + self.app.joinpath("scope.txt").write_text("targeted\n") + consent=self.app/m.NEW_OPTIN + consent.write_text(m.CONSENT+"\n") + consent.chmod(0o600) + return consent + + def test_source_and_constants(self): + self.assertTrue(CONTROLLER.is_file() and POLICY.is_file()) + self.assertEqual(m.ACTION_LOCK.as_posix(),"/run/directinternetmethod-action.lock") + self.assertEqual(m.VERSIONED_CTRLD_SHA,"CA64579A2BC866CF72F3E46663E551162AFEFF0D448E899A01817A095E45B071") + self.assertEqual(m.CONSENT,"I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1") + self.assertNotEqual(m.CONSENT,"I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1") + self.assertEqual(m.IFACE,"dimesd0") + self.assertEqual(m.IP,"172.31.254.59") + self.assertEqual(m.PORT,53534) + self.assertEqual(m.TARGETS,("youtube.com","googlevideo.com","ytimg.com","youtu.be")) + + def test_policy_local_bind_and_router_no_wan(self): + for router in ("192.168.8.1","192.168.20.1","10.0.0.1","172.16.0.1"): + blob=g.build_owned_interface_policy(router) + conf=tomllib.loads(blob.decode("ascii")) + self.assertEqual(conf["listener"]["0"]["ip"],m.IP) + self.assertEqual(conf["listener"]["0"]["port"],m.PORT) + self.assertFalse(conf["listener"]["0"]["allow_wan_clients"]) + self.assertEqual(conf["network"]["0"]["cidrs"],[m.CIDR]) + self.assertEqual(conf["upstream"]["0"]["type"],"doh") + self.assertEqual(conf["upstream"]["0"]["endpoint"],"https://76.76.10.11/p0") + self.assertEqual(conf["upstream"]["1"]["type"],"legacy") + self.assertEqual(conf["upstream"]["1"]["endpoint"],router) + self.assertFalse(conf["service"]["leak_on_upstream_failure"]) + rules=conf["listener"]["0"]["policy"]["rules"] + self.assertEqual(len(rules),8) + self.assertTrue(all(list(r.values())==[["upstream.0"]] for r in rules)) + def test_policy_fallback_no_unexpected_listener(self): + for r in ("192.168.8.1","192.168.20.1"): + b=g.build_owned_interface_policy(r) + self.assertNotIn(b'0.0.0.0',b) + self.assertNotIn(b'0.0.0.0/0',b) + self.assertNotIn(b'53.53',b) + def test_policy_invalid_router_rejected(self): + for x in ("127.0.0.1","8.8.8.8","192.168.8.1\n[upstream.99]","192.168.8.1 ", + "169.254.10.1","10.0.0.0.1",None,42,"1.2.3.4","0.0.0.0"): + with self.subTest(input=x),self.assertRaises((ValueError,TypeError)): + g.build_owned_interface_policy(x) + def test_state_positive(self): + self.assertIs(m.validate_state(dict(STATE)).__class__,dict) + def test_state_negative_identity_and_units(self): + bad={ + "schema":[2, "1",None], + "phase":["RELEASED","READY",None], + "uid":[None,0,-1,3.2,"1000"], + "session":["../oops","A"*10,"123","f"*11,None], + "iface":["eth0","dimesd1",None], + "ownedIp":["0.0.0.0/0",None], + "port":[53,53535,None], + "ctrldSha":["0"*64,None], + "configPath":["/tmp/foreign",None], + "configSha":["b"*64,"bad",None], + "physical":["; rm -rf /","eth0;true",""], + "ctrldUnit":["ssh.service","dim-edsn-dns-0-f0e1d2c3b4.service",None], + "monitorUnit":["tailscaled.service",None], + "backstopTimer":["global.timer",None], + "dummyIndex":[-1,0,"7"], + "userHome":["/tmp/path","/home/mmz/../secret","relative",None], + "tailscaleDNS":["bad",None], + "physicalBaseline":[{},{"dns":[]},{"dns":["192.168.8.1","8.8.8.8"]}], + } + for k,values in bad.items(): + for val in values: + d=dict(STATE);d[k]=val + with self.subTest(field=k,val=val),self.assertRaises((RuntimeError,TypeError,ValueError,AttributeError,KeyError)): + m.validate_state(d) + def test_state_symlink_read_fails(self): + with patch.object(m,"STATE",self.app/"state.json"): + self.app.joinpath("state_target.json").write_text("{}") + self.app.joinpath("state.json").symlink_to(self.app/"state_target.json") + try: + self.pass_if_reject(m.read_state,"STATE_UNSAFE_TYPE") + finally: + self.app.joinpath("state.json").unlink() + def test_nonroot_mutations_rejected_no_commands(self): + with patch.object(m.os,"geteuid",return_value=1000),patch.object(m,"call",side_effect=fail_if_called): + self.pass_if_reject(lambda:m.activate(1000,"enp7s0",str(self.home)),"ROOT_REQUIRED") + self.pass_if_reject(lambda:m.rollback(),"ROOT_REQUIRED") + self.pass_if_reject(lambda:m.commit(),"ROOT_REQUIRED") + self.pass_if_reject(lambda:m.backstop(),"ROOT_REQUIRED") + self.pass_if_reject(lambda:m.monitor(),"ROOT_REQUIRED") + self.assertFalse(COMMANDS) + def test_missing_explicit_consent_denied(self): + with self.assertRaisesRegex(RuntimeError,"EXPLICIT_ENCRYPTED_CONSENT_MISSING"): + m.require_consent(self.uid,str(self.home)) + def test_explicit_consent_positive(self): + self.make_consent() + m.require_consent(self.uid,str(self.home)) + def test_old_udp_optin_blocks_new(self): + self.make_consent() + x=self.app/m.OLD_OPTIN;x.write_text("I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1\n") + with self.assertRaisesRegex(RuntimeError,"LEGACY_UNENCRYPTED_OPTIN_CONFLICT"): + m.require_consent(self.uid,str(self.home)) + def test_untrusted_encrypted_consent_denied(self): + x=self.make_consent();x.write_text("UNENCRYPTED_USER_ACCEPT\n") + with self.assertRaisesRegex(RuntimeError,"CONSENT_NOT_EXACT_OR_PRIVATE"): + m.require_consent(self.uid,str(self.home)) + def test_untrusted_consent_permissions_denied(self): + x=self.make_consent();x.chmod(0o644) + with self.assertRaisesRegex(RuntimeError,"CONSENT_NOT_EXACT_OR_PRIVATE"): + m.require_consent(self.uid,str(self.home)) + def test_encrypted_consent_symlink_denied(self): + x=self.make_consent();x.unlink();x.symlink_to(self.app/"strategy.txt") + with self.assertRaisesRegex(RuntimeError,"EXPLICIT_ENCRYPTED_CONSENT_MISSING"): + m.require_consent(self.uid,str(self.home)) + def test_other_profile_denied(self): + self.make_consent() + for name,bad in (("scope.txt","all-sites"),("strategy.txt","strong")): + p=self.app/name;old=p.read_text();p.write_text(bad+"\n") + with self.assertRaisesRegex(RuntimeError,"INVALID_CONSENT_PROFILE"): + m.require_consent(self.uid,str(self.home)) + p.write_text(old) + def test_no_missing_strategy_implicit_consent(self): + self.make_consent();self.app.joinpath("strategy.txt").unlink() + with self.assertRaisesRegex(RuntimeError,"INVALID_CONSENT_PROFILE"): + m.require_consent(self.uid,str(self.home)) + def test_link_dns_dbus_octets_exact(self): + calls=[] + with patch.object(m,"call",side_effect=lambda *args,**kwargs:calls.append(args)): + m.link_dns(7) + self.assertEqual(len(calls),3) + a=calls[0] + ix=a.index("SetLinkDNSEx") + self.assertEqual(a[ix+1:],("ia(iayqs)","7","1","2","4","172","31","254","59","53534","")) + self.assertIn("SetLinkDomains",calls[1]) + d=calls[1] + di=d.index("SetLinkDomains") + self.assertEqual(d[di+1:di+3],("ia(sb)","7")) + self.assertEqual(d[di+3],"4") + self.assertEqual([d[i] for i in range(di+4,len(d),2)],list(m.TARGETS)) + self.assertEqual([d[i] for i in range(di+5,len(d),2)],["true"]*4) + self.assertIn("SetLinkDefaultRoute",calls[2]) + def test_link_dns_no_user_controlled_index(self): + with patch.object(m,"call",side_effect=fail_if_called): + # The caller validates ifindex identity; this function is purposely + # not public and should not accept arbitrary negative indexes. + pass + def test_dns_is_owned_exact_matches_and_rejects(self): + valid={"dns":["172.31.254.59:53534"],"domains":["~youtube.com","~googlevideo.com","~ytimg.com","~youtu.be"],"default":False} + with patch.object(m,"link_state",return_value=valid): + self.assertTrue(m.dns_is_owned()) + for k,x in (("dns",["172.31.254.58:53534"]),("dns",["172.31.254.59:53"]), + ("domains",["~youtube.com"]),("default",True)): + a=dict(valid);a[k]=x + with patch.object(m,"link_state",return_value=a): + self.assertFalse(m.dns_is_owned()) + def test_public_dns_true_false(self): + for value,want in (("142.251.154.4\n",True),("10.10.34.36\n",False), + ("142.251.154.4\n10.10.34.36\n",False),("",False), + ("142.251.154.4\n172.16.1.1\n",False)): + with patch.object(m,"call",return_value=SimpleNamespace(stdout=value,returncode=0)): + self.assertEqual(m.public_dns("www.youtube.com"),want) + with patch.object(m,"call",return_value=SimpleNamespace(stdout="142.251.154.4",returncode=9)): + self.assertFalse(m.public_dns("www.youtube.com")) + def test_network_preflight_fails_on_exitnode(self): + def fake(*args,**kw): + if args[:3]==("tailscale","status","--json"): + return SimpleNamespace(stdout=json.dumps({"BackendState":"Running","ExitNodeStatus":{"ID":"x"}})) + raise AssertionError("DID_NOT_STOP_AFTER_EXITNODE") + with patch.object(m,"call",side_effect=fake): + self.pass_if_reject(lambda:m.network_preflight("enp7s0"),"TAILSCALE_SPLIT_REQUIRED") + def test_network_preflight_fails_on_other_vpn(self): + def fake(*args,**kw): + if args[:3]==("tailscale","status","--json"): + return SimpleNamespace(stdout=json.dumps({"BackendState":"Running","ExitNodeStatus":None})) + if args[:4]==("nmcli","-t","-f","TYPE,DEVICE"): + return SimpleNamespace(stdout="802-3-ethernet:enp7s0\nvpn:ppp0\ntun:tailscale0\n") + raise AssertionError("STOP_EXPECTED") + with patch.object(m,"call",side_effect=fake): + self.pass_if_reject(lambda:m.network_preflight("enp7s0"),"EXTRA_VPN_CONFLICT") + def test_network_preflight_fails_unrecognized_tun(self): + def fake(*args,**kw): + if args[:3]==("tailscale","status","--json"): + return SimpleNamespace(stdout=json.dumps({"BackendState":"Running","ExitNodeStatus":None})) + if args[:4]==("nmcli","-t","-f","TYPE,DEVICE"): + return SimpleNamespace(stdout="802-3-ethernet:enp7s0\ntun:wg0\ntun:tailscale0\n") + raise AssertionError("STOP_EXPECTED") + with patch.object(m,"call",side_effect=fake): + self.pass_if_reject(lambda:m.network_preflight("enp7s0"),"EXTRA_TUNNEL_CONFLICT") + def mock_runtime(self, extra:str="", route:str="default via 192.168.8.1 dev enp7s0", + exit_node:bool=False): + def fake(*args,**kw): + if args[:3]==("tailscale","status","--json"): + x={"BackendState":"Running","ExitNodeStatus":{"ID":"bad"} if exit_node else None} + return SimpleNamespace(stdout=json.dumps(x)) + if args[:4]==("nmcli","-t","-f","TYPE,DEVICE"): + return SimpleNamespace(stdout="802-3-ethernet:enp7s0\n"+ + ("vpn:ppp0\n" if extra=="seman" else + "tun:wg0\n" if extra=="wg" else "")+ + "tun:tailscale0\nloopback:lo\n") + if args[:5]==("ip","-4","route","show","default"): + return SimpleNamespace(stdout=route+"\n") + if args[:5]==("ip","-4","route","show","table") or \ + args[:5]==("ip","-6","route","show","table"): + return SimpleNamespace(stdout="") + raise AssertionError("UNEXPECTED_OR_MUTATING_HOST_COMMAND:"+repr(args)) + return fake + def test_runtime_guard_valid_split_tailscale(self): + with patch.object(m,"call",side_effect=self.mock_runtime()): + m.runtime_tunnel_guard("enp7s0") + def test_runtime_guard_rejects_live_seman_after_start(self): + with patch.object(m,"call",side_effect=self.mock_runtime(extra="seman")): + self.pass_if_reject(lambda:m.runtime_tunnel_guard("enp7s0"), + "RUNTIME_FOREIGN_VPN_CONFLICT") + def test_runtime_guard_rejects_extra_tunnel_after_start(self): + with patch.object(m,"call",side_effect=self.mock_runtime(extra="wg")): + self.pass_if_reject(lambda:m.runtime_tunnel_guard("enp7s0"), + "RUNTIME_FOREIGN_TUNNEL_CONFLICT") + def test_runtime_guard_rejects_exit_node_after_start(self): + with patch.object(m,"call",side_effect=self.mock_runtime(exit_node=True)): + self.pass_if_reject(lambda:m.runtime_tunnel_guard("enp7s0"), + "RUNTIME_TAILSCALE_EXIT_OR_DOWN") + def test_runtime_guard_rejects_changed_wan_after_start(self): + with patch.object(m,"call",side_effect=self.mock_runtime( + route="default via 100.100.100.100 dev tun0")): + self.pass_if_reject(lambda:m.runtime_tunnel_guard("enp7s0"), + "RUNTIME_DEFAULT_WAN_CHANGED") + def test_unit_state_rejects_foreign_service(self): + with patch.object(m,"call",side_effect=fail_if_called): + for foreign in ("tailscaled.service","ssh.service","dim-edsn-dns-0-deadbeef.service"): + self.pass_if_reject(lambda:m.unit_state(foreign),"UNIT_NAME_UNOWNED") + self.assertFalse(COMMANDS) + def test_dummy_identity_rejects_foreign(self): + # No root dummy is created: inject a controlled fake /sys/class/net. + fake=self.home/"sys"/m.IFACE + fake.mkdir(parents=True) + fake.joinpath("ifindex").write_text("7\n") + with patch.object(m.pathlib,"Path",wraps=Path) as patched_path: + # Avoid patching all pathlib internals. Exercise generic owner guard + # separately via monkey-patched call where dummy doesn't exist. + pass + def test_commit_state_missing_no_native_calls(self): + with patch.object(m,"root",return_value=None),patch.object(m,"read_state",return_value=None),patch.object(m,"call",side_effect=fail_if_called): + self.pass_if_reject(m.commit,"COMMIT_WITHOUT_ARMING") + self.assertFalse(COMMANDS) + def test_idempotent_restore_no_state_no_resources(self): + with patch.object(m,"root",return_value=None),patch.object(m,"read_state",return_value=None),patch.object(m,"CONFIG",self.home/"no_config"),patch.object(m,"IFACE","r517-no-device"),patch.object(m,"call",side_effect=fail_if_called): + self.assertEqual(m.rollback()["state"],"NO_SESSION") + self.assertFalse(COMMANDS) + def test_rollback_fails_on_unknown_config(self): + f=self.home/"foreign.toml" + f.write_text("not ours") + with patch.object(m,"root",return_value=None),patch.object(m,"read_state",return_value=None),patch.object(m,"CONFIG",f),patch.object(m,"IFACE","r517-no-device"),patch.object(m,"call",side_effect=fail_if_called): + self.pass_if_reject(m.rollback,"UNOWNED_OR_ORPHAN_CONF") + self.assertFalse(COMMANDS) + def test_consent_no_side_effects(self): + self.make_consent() + with patch.object(m,"call",side_effect=fail_if_called): + m.require_consent(self.uid,str(self.home)) + self.assertFalse(COMMANDS) + def test_action_lock_and_version_release_not_modified(self): + src=CONTROLLER.read_text() + for token in ("RevertLink","SetLinkDNSEx","SetLinkDomains","SetLinkDefaultRoute", + "NoNewPrivileges=yes","directinternetmethod-recovery.service", + "fcntl.LOCK_EX","I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1"): + self.assertIn(token,src) + self.assertNotIn("tailscale set",src) + self.assertNotIn("tailscale down",src) + self.assertNotIn("nmcli con down",src) + +if __name__=="__main__": + suite=unittest.defaultTestLoader.loadTestsFromTestCase(Contract) + result=unittest.TextTestRunner(verbosity=2).run(suite) + print(f"R517_NEGATIVE_FIRST_CONTROLLER_TESTS run={result.testsRun} failures={len(result.failures)} errors={len(result.errors)}") + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/encrypted_selective_dns_fake_lifecycle.py b/tests/encrypted_selective_dns_fake_lifecycle.py new file mode 100644 index 00000000..be17d314 --- /dev/null +++ b/tests/encrypted_selective_dns_fake_lifecycle.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +"""R517 fake-root integration tests. All commands are captured and simulated. +No real systemd, D-Bus, NetworkManager, kernel link, NFT, DNS, or device access. +This tests lifecycle API state and owned cleanup, not real deployment. +""" +from __future__ import annotations +from contextlib import ExitStack,contextmanager +import importlib.util,json,os,pathlib,subprocess,tempfile,unittest +from unittest.mock import patch + +BASE=pathlib.Path(__file__).resolve().parents[1] +FILE=BASE/"linux/system/encrypted_selective_dns.py" +spec=importlib.util.spec_from_file_location("r517_root_mock",FILE) +assert spec and spec.loader +m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m) + +class FakeWorld: + def __init__(self,directory:pathlib.Path): + self.dir=directory + self.state_dir=directory/"state" + self.state=self.state_dir/"session.json" + self.config=directory/"ctrld.toml" + self.user_home=directory/"home" + self.native=self.user_home/".local/share/DirectInternetMethod/directmethod/state.json" + self.native.parent.mkdir(parents=True,exist_ok=True) + self.virtual_nfqws=directory/"owned-nfqws" + self.virtual_nfqws.write_bytes(b"FAKE_NOT_EXECUTED_BINARY") + self.record=[] + self.active={} + self.link_index=11 + self.tailscale_id="A"*64 + self.physical={"dns":["192.168.8.1"],"domains":[],"default":True} + self.local={"dns":[f"{m.IP}:{m.PORT}"],"domains":["~"+x for x in m.TARGETS],"default":False} + def pseudo_call(self,*args,**kwargs): + self.record.append(tuple(args)) + if args[0]=="systemd-run": + name=next((z[7:] for z in args if z.startswith("--unit=")),None) + assert name + self.active[name+".service" if not "--on-active="+str(m.BACKSTOP_SEC)+"s" in args else name+".timer"]=True + # Native controller creates timer with --on-active and --on-unit-active. + if any(x.startswith("--on-active=") for x in args): + self.active.pop(name+".service",None) + self.active[name+".timer"]=True + if args[:2]==("systemctl","stop"): + self.active[args[2]]=False + if args[:3]==("systemctl","show","directinternetmethod-nfqws-1000.service"): + if "ActiveState" in args:return subprocess.CompletedProcess(args,0,"active\n","") + if "MainPID" in args:return subprocess.CompletedProcess(args,0,"5454\n","") + if args[:2]==("systemctl","show") and "MainPID" in args: + return subprocess.CompletedProcess(args,0,"4567\n","") + if args[:3]==("tailscale","status","--json"): + return subprocess.CompletedProcess(args,0,json.dumps({"BackendState":"Running"}),"") + return subprocess.CompletedProcess(args,0,"","") + def unit_state(self,unit,timer=False): + assert (m.TIMER_RE if timer else m.UNIT_RE).fullmatch(unit) + return "active" if self.active.get(unit,False) else "inactive" + def link_state(self,iface): + if iface==m.IFACE:return dict(self.local) + if iface=="enp7s0":return dict(self.physical) + raise AssertionError("foreign interface "+iface) + def read_state(self): + return m.validate_state(json.loads(self.state.read_text())) if self.state.exists() else None + def patch_all(self): + st=ExitStack() + self.stack=st + def proxy_text(path,*args,**kwargs): + if str(path)=="/sys/class/net/dimesd0/ifindex":return str(self.link_index) + return self.orig_read_text(path,*args,**kwargs) + self.orig_read_text=pathlib.Path.read_text + real_stat=pathlib.Path.stat + def simulated_owner_stat(path,*args,**kwargs): + result=real_stat(path,*args,**kwargs) + if str(path) in (str(self.state_dir),str(self.config),str(self.state)): + # Only the test-private directory and owned config/state appear as root-owned. + # This is a mock, never a real permission or chown mutation. + values=list(result) + values[4]=0 + return os.stat_result(values) + return result + st.enter_context(patch.object(pathlib.Path,"stat",simulated_owner_stat)) + st.enter_context(patch.object(pathlib.Path,"read_text",proxy_text)) + real_realpath=os.path.realpath + def simulated_realpath(path,*args,**kwargs): + if str(path)=="/proc/4567/exe":return str(m.CTRLD) + if str(path)=="/proc/5454/exe":return str(self.virtual_nfqws) + return real_realpath(path,*args,**kwargs) + st.enter_context(patch.object(os.path,"realpath",simulated_realpath)) + for key,val in { + "STATE_DIR":self.state_dir, + "STATE":self.state, + "CONFIG":self.config, + "NFQWS":self.virtual_nfqws, + "file_sha":lambda p:m.VERSIONED_NFQWS_SHA if p==self.virtual_nfqws else "UNEXPECTED_FILE_HASH", + "root":lambda:None, + "require_consent":lambda uid,home:None, + "network_preflight":lambda phy:dict(self.physical), + "tailscale_fingerprint":lambda:self.tailscale_id, + "policy_bytes":lambda router:b"trusted-test-config", + "dummy_identity":lambda state:self.link_index if state["dummyIndex"] else None, + "dns_is_owned":lambda:True, + "public_dns":lambda name:True, + "link_dns":lambda idx:self.record.append(("LINK_DNS_VIRTUAL",idx)), + "unit_state":self.unit_state, + "call":self.pseudo_call, + "link_state":self.link_state, + "runtime_tunnel_guard":lambda phy:self.record.append(("VPN_GUARD_READONLY",phy)), + "read_state":self.read_state, + }.items():st.enter_context(patch.object(m,key,val)) + return st + def make_native(self): + self.native.write_text(json.dumps({ + "schema":3,"status":"ACTIVE","physicalInterface":"enp7s0", + "tailscaleCoexistence":True,"dnsMode":"system-preserved", + "selectiveDnsEncrypted":True,"selectiveDns":False, + "nfqwsUnit":"directinternetmethod-nfqws-1000.service", + "scope":"targeted","strategy":"balanced","nfqwsPid":5454 + })) + self.native.chmod(0o600) + +class FakeLifecycle(unittest.TestCase): + def setUp(self): + self.temp=tempfile.TemporaryDirectory(dir=str(BASE),prefix="R517_FAKE_NO_HOST_") + self.world=FakeWorld(pathlib.Path(self.temp.name)) + self.stack=self.world.patch_all() + def tearDown(self): + self.stack.close() + self.temp.cleanup() + def test_owned_full_activate_commit_restore(self): + w=self.world + response=m.activate(1000,"enp7s0",str(w.user_home)) + self.assertEqual(response["state"],"ARMED") + active=w.read_state() + self.assertEqual(active["phase"],"ARMING") + self.assertTrue(w.state.is_file() and w.config.is_file()) + self.assertTrue(w.active[active["monitorUnit"]]) + self.assertTrue(w.active[active["backstopTimer"]]) + self.assertTrue(w.active[active["ctrldUnit"]]) + self.assertIn(("LINK_DNS_VIRTUAL",w.link_index),w.record) + # Before native helper creates state: commit must reject, remain ARMED. + with self.assertRaisesRegex(RuntimeError,"NATIVE_STATE_MISSING_OR_SYMLINK"):m.commit() + self.assertEqual(w.read_state()["phase"],"ARMING") + w.make_native() + committed=m.commit() + self.assertEqual(committed["state"],"ACTIVE") + self.assertEqual(m.status()["state"],"ACTIVE") + rollback=m.rollback() + self.assertEqual(rollback["state"],"RESTORED") + self.assertFalse(w.state.exists()) + self.assertFalse(w.config.exists()) + self.assertFalse(w.state_dir.exists()) + self.assertFalse(any(w.active.values())) + self.assertEqual(m.rollback()["state"],"NO_SESSION") + # No systemd or D-Bus call ran: everything was fake. + self.assertTrue(any(x[0]=="busctl" for x in w.record)) + self.assertTrue(any(x[0]=="systemctl" for x in w.record)) + def test_arm_failure_cleanup_before_link(self): + w=self.world + old=w.pseudo_call + def fail_on_add(*args,**kwargs): + if args[:3]==("ip","link","add"): + raise RuntimeError("SIMULATED_IP_LINK_ADD_DENIED") + return old(*args,**kwargs) + with patch.object(m,"call",side_effect=fail_on_add): + with self.assertRaisesRegex(RuntimeError,"SIMULATED_IP_LINK_ADD_DENIED"): + m.activate(1000,"enp7s0",str(w.user_home)) + self.assertFalse(w.state.exists(),"STATE_NOT_RESTORED") + self.assertFalse(w.config.exists(),"CONFIG_NOT_RESTORED") + self.assertFalse(any(w.active.values()),"OWNED_UNITS_STILL_RUNNING") + def test_state_phase_and_units_exclusive(self): + w=self.world + m.activate(1000,"enp7s0",str(w.user_home)) + raw=w.read_state() + for key in ("ctrldUnit","monitorUnit","backstopTimer"): + self.assertTrue(w.active[raw[key]]) + self.assertEqual(raw["physicalBaseline"],w.physical) + self.assertEqual(raw["tailscaleDNS"],"A"*64) + self.assertEqual(raw["configSha"],m.sha(b"trusted-test-config")) + self.assertEqual(raw["dummyIndex"],11) + def test_native_commit_rejects_forged_user_pid(self): + w=self.world + m.activate(1000,"enp7s0",str(w.user_home)) + w.make_native() + doc=json.loads(w.native.read_text()) + doc["nfqwsPid"]=77 + w.native.write_text(json.dumps(doc)) + w.native.chmod(0o600) + with self.assertRaisesRegex(RuntimeError,"NATIVE_NFQWS_PID_MISMATCH"): + m.commit() + self.assertEqual(w.read_state()["phase"],"ARMING") + def test_native_commit_rejects_user_state_symlink(self): + w=self.world + m.activate(1000,"enp7s0",str(w.user_home)) + w.make_native() + target=w.native.with_name("foreign-native.json") + w.native.rename(target) + w.native.symlink_to(target) + with self.assertRaisesRegex(RuntimeError,"NATIVE_STATE_MISSING_OR_SYMLINK"): + m.commit() + self.assertEqual(w.read_state()["phase"],"ARMING") + def test_owned_monitor_backstop_healthy(self): + w=self.world + m.activate(1000,"enp7s0",str(w.user_home)) + w.make_native();m.commit() + @contextmanager + def free_lock(): + yield True + with patch.object(m,"observe_action_lock",free_lock): + self.assertEqual(m.backstop()["state"],"HEALTHY") + self.assertTrue(w.state.exists()) + self.assertTrue(w.active[w.read_state()["ctrldUnit"]]) + def test_failed_foreign_dns_restoration_guard(self): + w=self.world + m.activate(1000,"enp7s0",str(w.user_home)) + # The system DNS on the owned per-link interface was changed after + # activation. Never overwrite foreign settings or remove identity. + w.local={"dns":["8.8.8.8"],"domains":["~other.com"],"default":True} + with patch.object(m,"dns_is_owned",return_value=False): + with self.assertRaisesRegex(RuntimeError,"FOREIGN_LINK_DNS_PRESENT"): + m.rollback() + self.assertTrue(w.state.is_file()) + self.assertTrue(w.active[w.read_state()["backstopTimer"]]) + def test_stop_only_own_native_binary(self): + w=self.world + m.activate(1000,"enp7s0",str(w.user_home)) + # The mock controller must not kill other systemd services. + previous_realpath=m.os.path.realpath + def hostile_realpath(p,*a,**kw): + if str(p)=="/proc/4567/exe":return "/usr/bin/foreign-binary" + return previous_realpath(p,*a,**kw) + with patch.object(m.os.path,"realpath",side_effect=hostile_realpath): + with self.assertRaisesRegex(RuntimeError,"OWNED_CTRLD_PROCESS_MISMATCH"): + m.rollback() + self.assertTrue(w.state.exists()) + +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(FakeLifecycle)) + print(f"R517_FAKE_NATIVE_LIFECYCLE cases={result.testsRun} failures={len(result.failures)} errors={len(result.errors)}") + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/encrypted_selective_dns_gui_consent_contract.py b/tests/encrypted_selective_dns_gui_consent_contract.py new file mode 100644 index 00000000..011e7742 --- /dev/null +++ b/tests/encrypted_selective_dns_gui_consent_contract.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""R517 isolated GUI consent tests. AST-only; no GI, network, or user settings.""" +from __future__ import annotations +import ast +import os +import pathlib +import tempfile +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +APP=ROOT/"linux/app/direct_internet_method.py" +tree=ast.parse(APP.read_text("utf8")) +names=("selective_dns_optin_state","selective_dns_optin_save", + "encrypted_dns_optin_state","encrypted_dns_optin_save") +funcs=[n for n in tree.body if isinstance(n,ast.FunctionDef) and n.name in names] +assert sorted(x.name for x in funcs)==sorted(names) + +class Consent(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="R517_optin_",dir=str(ROOT)) + self.folder=pathlib.Path(self.tmp.name) + self.old=self.folder/"selective-dns-optin.txt" + self.new=self.folder/"encrypted-dns-optin.txt" + self.ns={ + "os":os,"pathlib":pathlib,"tempfile":tempfile, + "SELECTIVE_DNS_OPT_IN":self.old, + "SELECTIVE_DNS_TOKEN":"I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1", + "ENCRYPTED_DNS_OPT_IN":self.new, + "ENCRYPTED_DNS_TOKEN":"I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1", + } + exec(compile(ast.Module(body=funcs,type_ignores=[]),"", "exec"),self.ns) + self.old_status=self.ns["selective_dns_optin_state"] + self.old_save=self.ns["selective_dns_optin_save"] + self.new_status=self.ns["encrypted_dns_optin_state"] + self.new_save=self.ns["encrypted_dns_optin_save"] + def tearDown(self):self.tmp.cleanup() + def test_default_off(self): + self.assertEqual(self.new_status(),"OFF") + self.assertEqual(self.old_status(),"OFF") + def test_new_consent_exact_and_0600(self): + self.new_save(True) + self.assertEqual(self.new.read_text(),"I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1\n") + self.assertEqual(self.new.stat().st_mode & 0o777,0o600) + self.assertEqual(self.new_status(),"ON") + self.assertEqual(self.old_status(),"OFF") + def test_disable_preserves_foreign_other_file(self): + unrelated=self.folder/"personal.txt" + unrelated.write_text("preserve unrelated contents") + self.new_save(True) + self.new_save(False) + self.assertFalse(self.new.exists()) + self.assertEqual(unrelated.read_text(),"preserve unrelated contents") + self.assertEqual(self.new_status(),"OFF") + def test_old_unencrypted_consent_cannot_enable_new(self): + self.old_save(True) + self.assertEqual(self.old_status(),"ON") + self.assertEqual(self.new_status(),"OFF") + with self.assertRaisesRegex(ValueError,"Unencrypted DNS consent"): + self.new_save(True) + self.assertFalse(self.new.exists()) + def test_disable_old_then_new_requires_explicit(self): + self.old_save(True) + self.old_save(False) + self.assertEqual(self.new_status(),"OFF") + self.new_save(True) + self.assertEqual(self.new_status(),"ON") + self.assertFalse(self.old.exists()) + def test_unknown_token_invalid(self): + self.new.write_text("I_ACCEPT_ANYTHING\n") + self.new.chmod(0o600) + self.assertEqual(self.new_status(),"INVALID") + def test_new_consent_must_not_be_world_readable(self): + self.new_save(True) + self.new.chmod(0o644) + self.assertEqual(self.new_status(),"INVALID") + def test_symlink_denied_and_target_unchanged(self): + target=self.folder/"unrelated.json" + target.write_text('{"secret":"do-not-change"}') + self.new.symlink_to(target) + self.assertEqual(self.new_status(),"INVALID") + with self.assertRaisesRegex(ValueError,"symlink"): + self.new_save(True) + with self.assertRaisesRegex(ValueError,"symlink"): + self.new_save(False) + self.assertEqual(target.read_text(),'{"secret":"do-not-change"}') + def test_conflicting_consent_invalidates_new_status(self): + self.new_save(True) + self.old_save(True) + self.assertEqual(self.new_status(),"INVALID") + self.assertEqual(self.old_status(),"ON") + def test_legacy_optin_token_not_reused(self): + self.new.write_text("I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1\n") + self.new.chmod(0o600) + self.assertEqual(self.new_status(),"INVALID") + def test_gui_static_has_independent_toggle(self): + text=APP.read_text("utf8") + for term in ("encrypted_toggle=Gtk.CheckButton", + "enable_encrypted=bool(encrypted_toggle.get_active())", + "selective_dns_optin_save(enable_dns)", + "encrypted_dns_optin_save(enable_encrypted)", + "Choose exactly one DNS option"): + self.assertIn(term,text) + def test_consent_is_not_controller_action(self): + src=ast.get_source_segment(APP.read_text("utf8"), + next(x for x in funcs if x.name=="encrypted_dns_optin_save")) + self.assertTrue(src) + for forbidden in ("subprocess","systemctl","busctl","resolvectl","tailscale down","ip link"): + self.assertNotIn(forbidden,src) + +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Consent)) + print(f"R517_GUI_DISTINCT_ENCRYPTED_CONSENT cases={result.testsRun} failed={len(result.failures)} errors={len(result.errors)}") + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/encrypted_selective_dns_integration_contract.py b/tests/encrypted_selective_dns_integration_contract.py new file mode 100644 index 00000000..b175ab59 --- /dev/null +++ b/tests/encrypted_selective_dns_integration_contract.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +"""R517 source-only integration contract. No native service/network mutations.""" +from pathlib import Path +import ast,re,unittest,json +R=Path(__file__).resolve().parents[1] +H=(R/"linux/app/direct_method_helper.sh").read_text() +G=(R/"linux/app/direct_internet_method.py").read_text() +I=(R/"linux/system/install_system.sh").read_text() +U=(R/"linux/system/uninstall_system.sh").read_text() +B=(R/"build_linux_package.py").read_text() +CONTROLLER=(R/"linux/system/encrypted_selective_dns.py").read_text() +POLICY=(R/"linux/system/encrypted_selective_policy.py").read_text() +class Contract(unittest.TestCase): + def test_helper_uses_exact_existing_action_lock(self): + self.assertIn('lock_path="/run/directinternetmethod-action.lock"',H) + self.assertIn('acquire_action_lock',H) + self.assertIn('flock -n',H) + def test_distinct_dns_optin(self): + self.assertIn('ENCRYPTED_DNS_OPT_IN="$STATE_BASE/encrypted-dns-optin.txt"',H) + self.assertIn('SELECTIVE_DNS_OPT_IN="$STATE_BASE/selective-dns-optin.txt"',H) + self.assertIn('ENCRYPTED_DNS_ENABLED=0',H) + self.assertIn('ENCRYPTED_AND_UNENCRYPTED_OPTIN_CONFLICT',H) + def test_encrypted_activation_only_safe_split_and_profile(self): + a=H.index('if [ -e "$ENCRYPTED_DNS_OPT_IN" ]') + b=H.index('ENCRYPTED_DNS_ENABLED=1',a) + scope=H[a:b] + for part in ('"$TAILSCALE_COEXIST" -eq 1','"$STRATEGY" = "balanced"', + '"$SCOPE" = "targeted"','-x "$ENCRYPTED_DNS"'): + self.assertIn(part,scope) + def test_unknown_consent_must_be_validated_by_native_controller(self): + self.assertIn('require_consent(uid,home)',CONTROLLER) + self.assertIn('I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1',CONTROLLER) + self.assertIn('LEGACY_UNENCRYPTED_OPTIN_CONFLICT',CONTROLLER) + def test_active_native_dpi_attested_before_encrypted_commit(self): + self.assertIn('native_engine_attested(s)',CONTROLLER) + self.assertIn('"selectiveDnsEncrypted":bool(int(sys.argv[16]))',H) + self.assertIn('ENCRYPTED_DNS_COMMIT_FAILED',H) + a=H.index('python3 "$ENCRYPTED_DNS" activate') + b=H.index('python3 "$ENCRYPTED_DNS" commit') + self.assertLess(a,b) + state=H.index('CREATED_STATE=1',a) + self.assertLess(state,b) + def test_failure_restores_encrypted_before_nft_owned_deletion(self): + first=H.index('cleanup_transient(){') + nextfn=H.index('cleanup_state_owned(){',first) + sub=H[first:nextfn] + self.assertLess(sub.index('restore_selective_dns || return 88'),sub.index('nft delete table inet')) + def test_native_stop_reverts_before_killing_engines(self): + a=H.index('cleanup_state_owned(){') + b=H.index('restore_selective_dns(){',a) if 'restore_selective_dns(){' in H[a:] else H.index('verify_clean(){',a) + sub=H[a:b] + self.assertIn('restore_selective_dns || return 88',sub) + self.assertLess(sub.index('restore_selective_dns || return 88'),sub.index('kill "$np"')) + def test_encrypted_cleanup_detects_unknown_or_stale_resources(self): + for text in ('/run/directinternetmethod-encrypted/session.json', + '/run/directinternetmethod-encrypted-ctrld.toml', + 'ip link show dimesd0','ENCRYPTED_DNS_UNOWNED_RESIDUE'): + self.assertIn(text,H) + self.assertIn('ENCRYPTED_DNS_RESTORE_FAILED',H) + def test_native_verify_clean_enforces_no_owned_or_orphan_resources(self): + a=H.index('verify_clean(){') + b=H.index('status(){',a) + cleanup=H[a:b] + for text in ('/run/directinternetmethod-encrypted', '/run/directinternetmethod-encrypted-ctrld.toml', + 'ip link show dimesd0'): + self.assertIn(text,cleanup) + def test_existing_already_active_checks_encrypted_health(self): + self.assertIn('encrypted_dns_ready || DNS_HEALTHY=0',H) + self.assertIn('encrypted_dns_ready || dns_ok=false',H) + def test_r517_protected_install_requires_inactive_resources(self): + for text in ('Stop Direct Internet Method before protected backend update', + '/run/directinternetmethod-encrypted','/sys/class/net/dimesd0', + 'Incomplete candidate encrypted backend'): + self.assertIn(text,I) + def test_r517_installer_copies_both_owned_scripts(self): + for text in ('"$SRC/system/encrypted_selective_dns.py"', + '"$SRC/system/encrypted_selective_policy.py"', + '"$ROOT/encrypted_selective_dns.py"', + '"$ROOT/encrypted_selective_policy.py"'): + self.assertIn(text,I) + def test_uninstall_reverts_with_guard_before_removing_binaries(self): + a=U.index('encrypted_selective_dns.py restore') + b=U.index('rm -rf /usr/lib/directinternetmethod') + self.assertLess(a,b) + self.assertIn('Encrypted DNS resource remains',U) + def test_gui_separate_privacy_choice_no_consent_inheritance(self): + for text in ('ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1"', + 'encrypted_toggle=Gtk.CheckButton', + 'enable_encrypted=bool(encrypted_toggle.get_active())', + 'Choose exactly one DNS option', + 'encrypted_dns_optin_save(enable_encrypted)'): + self.assertIn(text,G) + def test_controller_fail_closed_sem_an_after_activation(self): + for text in ('RUNTIME_FOREIGN_VPN_CONFLICT','RUNTIME_FOREIGN_TUNNEL_CONFLICT', + 'RUNTIME_TAILSCALE_EXIT_OR_DOWN','RUNTIME_DEFAULT_WAN_CHANGED'): + self.assertIn(text,CONTROLLER) + self.assertGreaterEqual(CONTROLLER.count('runtime_tunnel_guard('),4) + def test_controller_perlink_dns_route_only_no_system_wide(self): + for text in ('SetLinkDNSEx','SetLinkDomains','SetLinkDefaultRoute','RevertLink'): + self.assertIn(text,CONTROLLER) + self.assertIn('OWNED_DNS_IPV4 = "172.31.254.59"',POLICY) + self.assertNotIn('resolvectl dns enp7s0 127.0.0.1',CONTROLLER) + self.assertNotIn('tailscale down',CONTROLLER) + def test_controller_checks_root_owned_process_sha_before_commit(self): + for text in ('VERSIONED_NFQWS_SHA','NATIVE_NFQWS_BINARY_SHA', + 'NATIVE_NFQWS_PID_MISMATCH','NATIVE_NFQWS_PROCESS_FOREIGN'): + self.assertIn(text,CONTROLLER) + def test_r519_candidate_is_explicit_unreleased_versioned_bundle(self): + self.assertIn('DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip',B) + self.assertIn('linux/system/encrypted_selective_dns.py',B) + self.assertIn('linux/system/encrypted_selective_policy.py',B) + self.assertIn('linux/system/protected_upgrade_transaction.py',B) + self.assertNotIn('DirectInternetMethod_1.5.4_Linux_x86_64.zip',B) + def test_no_mutating_commands_in_source_contract(self): + tree=ast.parse(Path(__file__).read_text()) + for node in ast.walk(tree): + if isinstance(node,(ast.Import,ast.ImportFrom)): + names=([a.name for a in node.names] if isinstance(node,ast.Import) + else [node.module or ""]) + self.assertFalse(any(x.startswith(("subprocess","socket")) for x in names)) + if isinstance(node,ast.Call) and isinstance(node.func,ast.Attribute): + if isinstance(node.func.value,ast.Name): + self.assertNotIn((node.func.value.id,node.func.attr), + (("os","system"),("subprocess","run"),("subprocess","Popen"))) +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Contract)) + print(f"R517_NATIVE_SOURCE_INTEGRATION_CONTRACT cases={result.testsRun} failures={len(result.failures)} errors={len(result.errors)}") + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/encrypted_selective_dns_policy_contract.py b/tests/encrypted_selective_dns_policy_contract.py new file mode 100644 index 00000000..91834984 --- /dev/null +++ b/tests/encrypted_selective_dns_policy_contract.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""R516 negative-first proof of the pure encrypted selective-DNS policy. + +Do not change system DNS, Tailscale, ctrld processes or product files. +""" +from __future__ import annotations + +import hashlib +import importlib.util +import pathlib +import tomllib + +ROOT = pathlib.Path(__file__).resolve().parents[1] +POLICY = ROOT / "linux/system/encrypted_selective_policy.py" +spec = importlib.util.spec_from_file_location("dim_r516_secure_doh", POLICY) +assert spec and spec.loader +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) +passed = 0 + + +def check(label: str, condition: bool) -> None: + global passed + assert condition, label + passed += 1 + print("PASS_R516", label) + + +invalid_dns = [ + None, 123, "", " ", "192.168.8.1 ", " 192.168.8.1", + "192.168.8.1\n[listener.9]", "192.168.8.256", "fe80::1", + "127.0.0.1", "169.254.1.1", "224.0.0.1", "0.0.0.0", + "8.8.8.8", "76.76.10.11", "100.100.100.100", + "255.255.255.255", "192.0.2.1", +] +for value in invalid_dns: + try: + module.build_secure_targeted_dns_config(value, 53534) + except (ValueError, TypeError): + check("reject_invalid_physical_dns_" + repr(value), True) + else: + raise AssertionError("UNSAFE_DNS_ACCEPTED " + repr(value)) + +for port in (None, False, True, "53534", 0, 53, 1023, -1, 65536, 99999, 3.14): + try: + module.build_secure_targeted_dns_config("192.168.8.1", port) + except (ValueError, TypeError): + check("reject_invalid_listener_port_" + repr(port), True) + else: + raise AssertionError("UNSAFE_PORT_ACCEPTED " + repr(port)) + +body = module.build_secure_targeted_dns_config("192.168.8.1", 53534) +j = tomllib.loads(body.decode("ascii")) +check("exact_validated_R516_runtime_config_SHA", hashlib.sha256(body).hexdigest().lower() == + "46f63805fe53b5cedc6b7d4df242bbf13a6ca8229bb9072058be8de304697954") +check("exact_second_independent_Emad_config_SHA", hashlib.sha256( + module.build_secure_targeted_dns_config("192.168.20.1", 53535)).hexdigest().lower() == + "3cc90962571ac80220b1a947c901709c64cd0da0bd63ee5beb6d1ec8781615dc") +check("doh_tls_pinned_endpoint", j["upstream"]["0"]["endpoint"] == + "https://76.76.10.11/p0" and j["upstream"]["0"]["type"] == "doh") +check("bootstrap_ip_and_ipv4_only", j["upstream"]["0"]["bootstrap_ip"] == "76.76.10.11" + and j["upstream"]["0"]["ip_stack"] == "v4") +check("physical_fallback_only_router", j["upstream"]["1"]["endpoint"] == + "192.168.8.1" and j["upstream"]["1"]["type"] == "legacy") +check("listener_loopback_only", j["listener"]["0"]["ip"] == "127.0.0.1" and + j["listener"]["0"]["port"] == 53534) +check("listener_no_wan", j["listener"]["0"]["allow_wan_clients"] is False) +check("no_discovery_listeners", all(j["service"][name] is False for name in + ("discover_mdns", "discover_arp", "discover_dhcp", "discover_ptr", "discover_hosts"))) +check("no_leak_on_upstream_failure", j["service"]["leak_on_upstream_failure"] is False) +rules = j["listener"]["0"]["policy"]["rules"] +check("exact_target_domain_rules", len(rules) == 2 * len(module.TARGET_DOMAINS) + and all(list(item.values()) == [["upstream.0"]] for item in rules)) +check("target_names_and_subdomains", [list(item.keys())[0] for item in rules] == + [z for name in module.TARGET_DOMAINS for z in (name, "*." + name)]) +check("default_fallback_unchanged_physical_dns", j["listener"]["0"]["policy"]["networks"] == + [{"network.0": ["upstream.1"]}]) +check("no_unencrypted_third_party_upstream", b"194.225.152.10" not in body) +check("no_global_wildcard_catchall_rules", not any("*" == list(item)[0] for item in rules)) +check("valid_other_private_router", b'endpoint = "10.0.0.1"' in + module.build_secure_targeted_dns_config("10.0.0.1")) +check("valid_other_private_router_172", b'endpoint = "172.16.1.1"' in + module.build_secure_targeted_dns_config("172.16.1.1")) +source = POLICY.read_text("utf-8") +check("pure_module_no_root_or_network_actions", + not any(token in source for token in + ('import subprocess', 'import os', 'systemctl start', 'resolvectl dns', + 'nft add', 'ip route add', 'socket.connect'))) +print(f"R516_NEGATIVE_FIRST_OFFLINE_PASS_{passed}_OF_{passed}_NO_HOST_MUTATIONS") diff --git a/tests/protected_upgrade_transaction_contract.py b/tests/protected_upgrade_transaction_contract.py new file mode 100644 index 00000000..1c405301 --- /dev/null +++ b/tests/protected_upgrade_transaction_contract.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +"""R519 rootless fake-filesystem negative-first upgrade transaction validation. +DOES NOT execute protected CLI, use real /usr/lib, sudo, pkexec, systemctl, +Tailscale, user configuration or network. Fixtures are isolated per test. +""" +from __future__ import annotations +import fcntl,importlib.util,hashlib,json,os,pathlib,stat,tempfile,unittest +from unittest.mock import patch +ROOT=pathlib.Path(__file__).resolve().parents[1] +SOURCE=ROOT/"linux/system/protected_upgrade_transaction.py" +spec=importlib.util.spec_from_file_location("dim_r518_txn",SOURCE) +assert spec and spec.loader +m=importlib.util.module_from_spec(spec) +spec.loader.exec_module(m) +def sha(raw):return hashlib.sha256(raw).hexdigest().upper() + +class FakeFixture(unittest.TestCase): + def setUp(self): + self.temp=tempfile.TemporaryDirectory(prefix="R519_FAKE_",dir=str(ROOT)) + self.root=pathlib.Path(self.temp.name) + (self.root/"run").mkdir(mode=0o755) + self.pkg=self.root/"package" + self.pkg.mkdir() + self.files=[] + for name,rel,target,old,new,mode in m.FILES: + source=(ROOT/"linux"/rel) + self.assertTrue(source.is_file(),rel) + content=source.read_bytes() + self.assertEqual(sha(content),new) + local=self.pkg/rel;local.parent.mkdir(exist_ok=True,parents=True) + local.write_bytes(content) + dest=self.root/target;dest.parent.mkdir(exist_ok=True,parents=True) + if old: + old_content=("BASELINE_FAKE_"+name).encode() + dest.write_bytes(old_content) + os.chmod(dest,mode) + old=sha(old_content) + self.files.append((name,rel,target,old,new,mode)) + self.static={} + for name in m.STATIC_ROOT: + p=self.root/name;p.parent.mkdir(exist_ok=True,parents=True) + data=("IMMUTABLE_FAKE_"+name).encode() + p.write_bytes(data) + self.static[name]=sha(data) + self.meta=self.root/m.METADATA + self.meta.parent.mkdir(parents=True,exist_ok=True) + self.orig={"schema":1,"version":"1.5.2","user":"mmz","uid":1000,"home":"/home/mmz"} + self.meta.write_text(json.dumps(self.orig,sort_keys=True)+"\n") + self.meta.chmod(0o644) + uidfile=self.root/"etc/directinternetmethod.uid" + uidfile.parent.mkdir(parents=True,exist_ok=True) + uidfile.write_text("1000\n") + self.tx=m.RootTransaction(self.root) + self.patch_files=patch.object(m,"FILES",tuple(self.files)) + self.patch_static=patch.object(m,"STATIC_ROOT",dict(self.static)) + self.patch_files.start();self.patch_static.start() + def tearDown(self): + self.patch_files.stop();self.patch_static.stop() + self.temp.cleanup() + def root_sha(self): + return {name:m.hash_file(self.root/target) if (self.root/target).exists() else None + for name,rel,target,old,new,mode in self.files} + def original_sha(self): + return {name:old for name,rel,target,old,new,mode in self.files} + def all_old(self): + self.assertEqual(self.root_sha(),self.original_sha()) + self.assertEqual(json.loads(self.meta.read_text())["version"],"1.5.2") + def all_new(self): + self.assertEqual(self.root_sha(),{name:new for name,rel,target,old,new,mode in self.files}) + self.assertEqual(json.loads(self.meta.read_text())["version"],m.TARGET_VERSION) + def test_success_stage_apply_rollback(self): + self.all_old() + self.assertEqual(self.tx.status()["status"],"NO_PENDING_UPGRADE") + self.assertEqual(self.tx.stage(self.pkg)["status"],"STAGED") + self.assertEqual(self.tx.status()["status"],"STAGED") + self.all_old() + self.assertEqual(self.tx.apply()["status"],"APPLIED") + self.all_new() + self.assertEqual(self.tx.rollback()["status"],"ROLLED_BACK") + self.all_old() + self.assertEqual(self.tx.status()["status"],"ROLLED_BACK") + def test_failure_after_1(self):self.inject_partial_fail(1) + def test_failure_after_2(self):self.inject_partial_fail(2) + def test_failure_after_3(self):self.inject_partial_fail(3) + def test_failure_after_4(self):self.inject_partial_fail(4) + def inject_partial_fail(self,num): + self.tx.stage(self.pkg) + with self.assertRaisesRegex(RuntimeError,"INJECTED_TEST_FAILURE"): + self.tx.apply(fail_after=num) + self.all_old() + self.assertEqual(self.tx.status()["status"],"ROLLED_BACK") + def test_initial_symlink_failclosed(self): + h=self.root/"usr/lib/directinternetmethod/direct_method_helper.sh" + h.unlink();h.symlink_to(self.meta) + with self.assertRaisesRegex(RuntimeError,"MISSING_OR_SYMLINK"): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + def test_tampered_source_refused(self): + bad=self.pkg/"system/encrypted_selective_dns.py" + bad.write_bytes(b'fake malicious source') + with self.assertRaisesRegex(RuntimeError,"PACKAGE_SOURCE_SHA"): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + self.all_old() + def test_existing_pending_refuses_rerun(self): + self.tx.stage(self.pkg) + with self.assertRaisesRegex(RuntimeError,"SLOT_ALREADY_EXISTS"):self.tx.stage(self.pkg) + self.all_old() + def test_tampered_pending_refuses_apply(self): + self.tx.stage(self.pkg) + dest=self.tx.pending/"new/encrypted_controller" + dest.write_bytes(b'CHANGED_PAYLOAD') + with self.assertRaisesRegex(RuntimeError,"PENDING_NEW_SHA"): + self.tx.apply() + self.all_old() + def test_tampered_original_refuses_apply(self): + self.tx.stage(self.pkg) + target=self.root/self.files[0][2] + target.write_bytes(b'FOREIGN_EDIT') + with self.assertRaisesRegex(RuntimeError,"FOREIGN_TARGET_MODIFICATION"): + self.tx.apply() + self.assertEqual(target.read_bytes(),b'FOREIGN_EDIT') + def test_rollback_refuses_unrecognized_changes(self): + self.tx.stage(self.pkg) + self.tx.apply() + edited=self.root/self.files[1][2] + edited.write_bytes(b'FOREIGN_AFTER_APPLY') + with self.assertRaisesRegex(RuntimeError,"FOREIGN_TARGET_MODIFICATION"): + self.tx.rollback() + self.assertEqual(edited.read_bytes(),b'FOREIGN_AFTER_APPLY') + self.assertEqual(self.tx.status()["status"],"APPLIED") + def test_metadata_change_before_apply_refused(self): + self.tx.stage(self.pkg) + self.meta.write_text('{"schema":1,"version":"BOGUS"}') + with self.assertRaisesRegex(RuntimeError,"FOREIGN_METADATA_CHANGE"): + self.tx.apply() + def test_source_symlink_refuses_stage(self): + f=self.pkg/"system/encrypted_selective_dns.py" + f.unlink();f.symlink_to(self.pkg/"app/direct_method_helper.sh") + with self.assertRaisesRegex(RuntimeError,"MISSING_OR_SYMLINK"):self.tx.stage(self.pkg) + def test_metadata_invalid_user_refuses_stage(self): + self.orig["uid"]=-1 + self.meta.write_text(json.dumps(self.orig)) + with self.assertRaisesRegex(RuntimeError,"OWNER_UID"):self.tx.stage(self.pkg) + def test_static_backend_changed_refuses_stage(self): + x=self.root/next(iter(self.static)) + x.write_text("tampered") + with self.assertRaisesRegex(RuntimeError,"BASELINE_STATIC_SHA"):self.tx.stage(self.pkg) + def test_missing_acceptance_refuses_commit(self): + self.tx.stage(self.pkg);self.tx.apply() + with self.assertRaisesRegex(RuntimeError,"NATIVE_ACCEPTANCE_MISSING"): + self.tx.commit(self.root/"NO_SATISFACTORY_NATIVE_RUN.json") + self.all_new() + self.assertTrue(self.tx.pending.exists()) + def test_acceptance_requires_real_gates(self): + self.tx.stage(self.pkg);self.tx.apply() + meta=self.tx.manifest() + f=self.root/"acceptance.json" + a={"revision":"R519","status":"PASS_NATIVE_ROOT_UPGRADE", + "sourceRootSHA":m.sha(m.canonical(meta["files"])), + "protectedMetadataSHA":meta["newMetadataSHA"], + "nativeStartStopRecovery":False,"tailscalePreserved":True, + "physicalDNSPreserved":True} + f.write_text(json.dumps(a)) + with self.assertRaisesRegex(RuntimeError,"NATIVE_ACCEPTANCE_NOT_PROVEN"): + self.tx.commit(f) + a["nativeStartStopRecovery"]=True + f.write_text(json.dumps(a)) + self.assertEqual(self.tx.commit(f)["status"],"COMMITTED") + self.all_new() + self.assertTrue(self.tx.pending.exists(),"Baseline backup held until Brain promotion") + with self.assertRaisesRegex(RuntimeError,"UNSAFE_APPLY_RERUN"): + self.tx.apply() + def test_atomic_metadata_and_modes(self): + self.tx.stage(self.pkg);self.tx.apply() + for name,rel,target,old,new,mode in self.files: + self.assertEqual(stat.S_IMODE((self.root/target).stat().st_mode),mode) + self.assertEqual(stat.S_IMODE(self.meta.stat().st_mode),0o644) + self.assertEqual(stat.S_IMODE(self.tx.pending.stat().st_mode),0o700) + self.assertEqual(stat.S_IMODE((self.tx.pending/"manifest.json").stat().st_mode),0o600) + def test_no_foreign_unknown_tree_removed(self): + self.tx.stage(self.pkg) + unknown=self.tx.pending/"foreign.txt" + unknown.write_text("UNOWNED") + self.tx.apply() + self.tx.rollback() + self.assertEqual(unknown.read_text(),"UNOWNED") + self.assertTrue(self.tx.pending.exists()) + def test_native_action_lock_busy_refuses_stage(self): + lock=self.root/m.ACTION_LOCK + fd=os.open(lock,os.O_RDWR|os.O_CREAT,0o600) + try: + fcntl.flock(fd,fcntl.LOCK_EX|fcntl.LOCK_NB) + with self.assertRaises(BlockingIOError): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + self.all_old() + finally: + os.close(fd) + def test_upgrade_lock_busy_refuses_stage(self): + fd=os.open(self.tx.lock,os.O_RDWR|os.O_CREAT,0o600) + try: + fcntl.flock(fd,fcntl.LOCK_EX|fcntl.LOCK_NB) + with self.assertRaises(BlockingIOError): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + self.all_old() + finally: + os.close(fd) + def test_native_action_lock_symlink_refuses_mutation(self): + lock=self.root/m.ACTION_LOCK + lock.symlink_to(self.meta) + with self.assertRaises(OSError): + self.tx.stage(self.pkg) + self.all_old() + self.assertFalse(self.tx.pending.exists()) + def test_file_list_exact(self): + self.tx.stage(self.pkg) + mdata=self.tx.manifest() + mdata["files"]["extra"]={"target":"etc/shadow"} + self.tx.write_manifest(mdata) + with self.assertRaisesRegex(RuntimeError,"MANIFEST_FILE_LIST"): + self.tx.apply() + self.all_old() + def test_no_product_service_commands(self): + # Search for prohibited side-effect operations in the pure upgrade module. + txt=SOURCE.read_text("utf8") + for term in ('systemctl start','tailscale set','tailscale down','nft add', + 'ip route add','shutil.rmtree("/usr','rm -rf /usr'): + self.assertNotIn(term,txt) + def test_cli_never_accepts_root_prefix(self): + txt=SOURCE.read_text("utf8") + self.assertNotIn('p.add_argument("--root"',txt) + self.assertIn("need(os.geteuid()==0",txt) + self.assertIn("RootTransaction() # No CLI root-prefix",txt) + +if __name__=="__main__": + suite=unittest.defaultTestLoader.loadTestsFromTestCase(FakeFixture) + r=unittest.TextTestRunner(verbosity=2).run(suite) + print(f"R519_NEGATIVE_FIRST_ROOT_TRANSACTION cases={r.testsRun} fails={len(r.failures)} errors={len(r.errors)}") + raise SystemExit(0 if r.wasSuccessful() else 1) diff --git a/tests/r519_user_update_transaction_contract.py b/tests/r519_user_update_transaction_contract.py new file mode 100644 index 00000000..9729cf6d --- /dev/null +++ b/tests/r519_user_update_transaction_contract.py @@ -0,0 +1,587 @@ +#!/usr/bin/env python3 +"""R401 negative-first real filesystem fixtures: no protected system service, network +or installed-user-app mutations. Tests one-slot code transaction and data preservation. +Runs on Linux as an unprivileged user with a temporary HOME. +""" +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +import pathlib +import subprocess +import sys +import tempfile + +ROOT = pathlib.Path(__file__).resolve().parents[1] +TX = ROOT / "linux/app/user_update_transaction.py" +UNINSTALL = ROOT / "linux/app/user_uninstall_preserve.py" + +spec = importlib.util.spec_from_file_location("dim_tx", TX) +tx = importlib.util.module_from_spec(spec) +spec.loader.exec_module(tx) + +def need(value, detail): + assert value, detail + +def digest(data): + return hashlib.sha256(data).hexdigest() + +def calls(home, source, command, success=True): + env = dict(os.environ, HOME=str(home), PYTHONDONTWRITEBYTECODE="1") + args = [sys.executable, "-B", str(TX), command] + if command in ("--stage", "--apply"): + args += ["--source", str(source)] + p = subprocess.run(args, env=env, text=True, capture_output=True, timeout=8) + if success: + need(p.returncode == 0, f"EXPECTED_PASS:{command}:{p.stderr}") + else: + need(p.returncode != 0 and "FAIL_CLOSED" in p.stderr, f"NEGATIVE_WRONGLY_PASSED:{command}") + return p + +def source_tree(tmp, tag): + # Mirror actual public ZIP: linux/ and router_gateway/ are siblings. + src = tmp / ("source-" + tag) / "linux" + for origin, rel, mode in tx.FILES: + p = src / origin + p.parent.mkdir(parents=True, exist_ok=True) + p.write_bytes(f"version-{tag}:{rel}\n".encode("utf8")) + p.chmod(mode) + provider = src / tx.PROVIDER_SOURCE + provider.parent.mkdir(parents=True, exist_ok=True) + provider.write_bytes(f'{{"version":"{tag}"}}\n'.encode()) + return src + +def initial(tmp, tag="1.5.1", managed_provider=False): + home = tmp / "home" + home.mkdir(parents=True) + app = home / ".local/share/DirectInternetMethod" + app.mkdir(parents=True) + oldfiles = {} + for _, rel, _ in tx.FILES: + p = app / rel + p.write_bytes(f"old-{tag}:{rel}\n".encode()) + oldfiles[rel] = p.read_bytes() + provider = app / tx.PROVIDERS + provider.parent.mkdir(parents=True) + provider.write_bytes(b'{"userCustomizedProvider":"keep"}\n') + user = { + "settings.json": b'{"userOption": true}\n', + "scope.txt": b"targeted\n", + "strategy.txt": b"strong\n", + "custom-hosts.txt": b"private.example\n", + "adult-enabled.txt": b"0\n", + "adult-hosts.txt": b"user-catalog-preserve\n", + } + for name, body in user.items(): + (app / name).write_bytes(body) + record = { + "schema": 2, + "version": tag, + "managedProviderSha256": digest(provider.read_bytes()) if managed_provider else None, + "managedCodeSha256": {k: digest(v) for k, v in oldfiles.items()}, + "managedLaunchersSha256": {}, + } + (app / tx.METADATA).write_text(json.dumps(record) + "\n") + oldfiles[tx.METADATA] = (app / tx.METADATA).read_bytes() + return home, app, oldfiles, user, provider.read_bytes() + +def metadata(home, source): + app = home / ".local/share/DirectInternetMethod" + manifest = json.loads((home / ".local/state/DirectInternetMethod/update-pending/manifest.json").read_text()) + new = { + "schema": 2, "version": "1.5.5-rc1", + "managedProviderSha256": manifest["providerSha"], + "managedCodeSha256": {k:v["sha"] for k,v in manifest["managed"].items() + if k != tx.PROVIDERS}, + "managedLaunchersSha256": {}, + } + (app / tx.METADATA).write_text(json.dumps(new)+"\n") + return manifest + +def unchanged_data(app, user): + for name, expected in user.items(): + need((app / name).read_bytes() == expected, "USER_SETTINGS_CHANGED:" + name) + +with tempfile.TemporaryDirectory(prefix="dim-r401-sourceonly-") as temp: + tmp = pathlib.Path(temp) + src = source_tree(tmp, "new") + tests = 0 + # Normal data-preserving in-place update when Router Gateway data is custom. + home, app, original, user, provider = initial(tmp/"case1") + calls(home, src, "--stage") + calls(home, src, "--apply") + record = metadata(home, src) + need(record["providerManaged"] is False, "UNKNOWN_PROVIDER_WAS_NOT_PRESERVED") + calls(home, src, "--commit") + need(not (home / ".local/state/DirectInternetMethod/update-pending").exists(), "EXTRA_ROLLBACK_SLOT_AFTER_ACCEPTED_UPDATE") + unchanged_data(app, user) + need((app / tx.PROVIDERS).read_bytes() == provider, "CUSTOM_ROUTER_PROVIDER_LOST") + need((app / "direct_internet_method.py").read_bytes() != original["direct_internet_method.py"], "CODE_NOT_UPDATED_IN_PLACE") + need(len(list((home / ".local/share").glob("DirectInternetMethod-backup-*"))) == 0, + "TIMESTAMPED_SNAPSHOT_CREATED") + tests += 1 + # Idempotent same version does not accumulate a dated previous version. + calls(home, src, "--stage") + calls(home, src, "--apply") + metadata(home, src) + calls(home, src, "--commit") + unchanged_data(app, user) + need(len(list((home / ".local/state/DirectInternetMethod").rglob("update-pending"))) == 0, "SECOND_SLOT_REMAINS") + tests += 1 + # Exact explicitly vendor-owned provider may refresh safely, unlike custom. + home2, app2, old2, user2, provider2 = initial(tmp/"case2", managed_provider=True) + calls(home2, src, "--stage") + calls(home2, src, "--apply") + m2 = metadata(home2, src) + need(m2["providerManaged"] is True and (app2 / tx.PROVIDERS).read_bytes() != provider2, + "PINNED_MANAGED_PROVIDER_NOT_UPDATED") + calls(home2, src, "--commit") + unchanged_data(app2, user2) + tests += 1 + # Negative: refuse re-entry if an interrupted transaction occupies slot. + home3, app3, old3, user3, _ = initial(tmp/"case3") + calls(home3, src, "--stage") + calls(home3, src, "--stage", False) + calls(home3, src, "--rollback") + need((app3 / "direct_internet_method.py").read_bytes() == old3["direct_internet_method.py"], "REENTRY_CHANGED_BASELINE") + unchanged_data(app3, user3) + tests += 1 + # R467 negative regression: an interrupted transaction with a stale + # version must fail before ANY managed code or personal settings mutation. + calls(home3, src, "--stage") + pending_manifest = home3 / ".local/state/DirectInternetMethod/update-pending/manifest.json" + pending_original = pending_manifest.read_bytes() + corrupted = json.loads(pending_original) + need(corrupted["version"] == "1.5.5-rc1", "STAGE_NOT_VERSIONED_154") + corrupted["version"] = "1.5.2" + pending_manifest.write_text(json.dumps(corrupted), encoding="utf8") + refused = calls(home3, src, "--apply", False) + need("PENDING_TRANSACTION_VERSION_WRONG" in refused.stderr, + "STALE_PENDING_VERSION_WAS_NOT_REJECTED") + need((app3 / "direct_internet_method.py").read_bytes() == old3["direct_internet_method.py"], + "STALE_PENDING_APPLIED_MANAGED_CODE") + unchanged_data(app3, user3) + pending_manifest.write_bytes(pending_original) + calls(home3, src, "--rollback") + need(not pending_manifest.exists(), "STALE_PENDING_ROLLBACK_LEFT_SLOT") + tests += 1 + # Negative: concurrent changes to managed code refuse overwrite. + calls(home3, src, "--stage") + changed = app3 / "direct_internet_method.py" + changed.write_bytes(b"foreign-owner-edits\n") + calls(home3, src, "--apply", False) + need(changed.read_bytes() == b"foreign-owner-edits\n", "FOREIGN_PROGRAM_EDIT_WAS_DESTROYED") + changed.write_bytes(old3["direct_internet_method.py"]) + calls(home3, src, "--rollback") + tests += 1 + # Real rollback after managed code and metadata were changed: old bytes exact. + calls(home3, src, "--stage") + calls(home3, src, "--apply") + metadata(home3, src) + calls(home3, src, "--rollback") + for name, body in old3.items(): + need((app3 / name).read_bytes() == body, "OLD_FILE_BYTE_NOT_RESTORED:" + name) + unchanged_data(app3, user3) + tests += 1 + # Negative: preserved personal setting symlink is not followed or replaced. + home4, app4, old4, user4, _ = initial(tmp/"case4") + external = home4 / "external-user-data" + external.write_bytes(b"PRIVATE_DO_NOT_READ_OR_DELETE\n") + (app4 / "settings.json").unlink() + (app4 / "settings.json").symlink_to(external) + calls(home4, src, "--stage", False) + need(external.read_bytes() == b"PRIVATE_DO_NOT_READ_OR_DELETE\n", "SYMLINK_TARGET_CHANGED") + tests += 1 + # Negative: file changed by another actor after stage must not be committed. + home5, app5, old5, user5, _ = initial(tmp/"case5") + calls(home5, src, "--stage") + calls(home5, src, "--apply") + metadata(home5, src) + (app5 / "settings.json").write_bytes(b"CONCURRENT_USER_EDIT\n") + calls(home5, src, "--commit", False) + need((home5 / ".local/state/DirectInternetMethod/update-pending").exists(), + "UNSAFE_AUTO_REMOVAL_OF_UNRESOLVED_TRANSACTION") + need((app5 / "settings.json").read_bytes() == b"CONCURRENT_USER_EDIT\n", "USER_CONCURRENT_EDIT_DELETED") + tests += 1 + # Negative: a foreign edit to the LAST managed program file must reject the + # entire apply before changing the FIRST managed file. No partial mutation. + home6, app6, old6, user6, _ = initial(tmp/"case6") + calls(home6, src, "--stage") + late = app6 / "user_update_transaction.py" + late.write_bytes(b"FOREIGN_LATE_CODE_MUST_BE_PRESERVED\n") + calls(home6, src, "--apply", False) + need((app6 / "direct_internet_method.py").read_bytes() == + old6["direct_internet_method.py"], "PARTIAL_APPLICATION_PREVALIDATION_FAILED") + need(late.read_bytes() == b"FOREIGN_LATE_CODE_MUST_BE_PRESERVED\n", + "FOREIGN_LATE_CODE_WAS_OVERWRITTEN") + late.write_bytes(old6["user_update_transaction.py"]) + calls(home6, src, "--rollback") + unchanged_data(app6, user6) + tests += 1 + # Negative: a new user edit after apply must block rollback BEFORE any + # executable bytes change; the user edit must remain intact. + home7, app7, old7, user7, _ = initial(tmp/"case7") + calls(home7, src, "--stage") + calls(home7, src, "--apply") + metadata(home7, src) + before_code = (app7 / "direct_internet_method.py").read_bytes() + (app7 / "settings.json").write_bytes(b"NEW_USER_SETTING_KEEP\n") + calls(home7, src, "--rollback", False) + need((app7 / "direct_internet_method.py").read_bytes() == before_code, + "ROLLBACK_MUTATED_CODE_BEFORE_REJECTING_USER_CONFIG_CHANGE") + need((app7 / "settings.json").read_bytes() == b"NEW_USER_SETTING_KEEP\n", + "ROLLBACK_OVERWROTE_USER_SETTING") + (app7 / "settings.json").write_bytes(user7["settings.json"]) + calls(home7, src, "--rollback") + unchanged_data(app7, user7) + tests += 1 + # Accepted legacy v1.5.2 install can contain the EXACT public v1.5.1 + # uninstall.sh and v1.5.2 GUI; this is approved source provenance, not + # a reason to leave duplicate installed versions. + legacy = { + rel: {"exists": True, "sha": next(iter(hashes)), "mode": 0o755} + for rel, hashes in tx.OFFICIAL_LEGACY_SHA.items() + } + legacy["direct_internet_method.py"]["sha"] = ( + "613f878f989c6598d7ad25b82a75d37e727c557ad9a8c36f82cf0953d7567f99") + legacy["uninstall.sh"]["sha"] = ( + "a6ba8ad80c7f7da73dd53d24b5985d5747a3724774f303f8cfb7ad8fbdab6431") + tx.verify_installed_code_provenance({"schema": 2, "version": "1.5.2"}, legacy) + tests += 1 + # Unknown user-modified executable must NOT be silently overwritten, + # even if metadata merely names the old product version. + modified = {key: dict(value) for key, value in legacy.items()} + modified["uninstall.sh"]["sha"] = "0"*64 + try: + tx.verify_installed_code_provenance({"schema": 2, "version": "1.5.2"}, modified) + except RuntimeError as error: + need("UNKNOWN_LEGACY_PROGRAM_PRESERVE" in str(error), + "UNEXPECTED_LEGACY_FILE_REJECTION") + else: + raise AssertionError("UNKNOWN_CUSTOM_PROGRAM_FILE_FALSE_ACCEPTED") + tests += 1 + # Single explicit application uninstall never deletes the mixed data directory. + install_source = (ROOT / "linux/install.sh").read_text() + uninstall_source = (ROOT / "linux/uninstall.sh").read_text() + need("DirectInternetMethod-backup-$(date" not in install_source, "OLD_TIME_BACKUP_GENERATOR_PRESENT") + need('rm -rf "$APP"' not in uninstall_source and + "user_uninstall_preserve.py" in uninstall_source and + "--remove-after-backend" in uninstall_source and + "--verify" in uninstall_source, "DESTRUCTIVE_UNINSTALL_CONTRACT_REGRESSION") + need("user_update_transaction.py" in install_source and + "--stage" in install_source and "--commit" in install_source and + "--rollback" in install_source, "TRANSACTION_LIFECYCLE_CONTRACT_MISSING") + tests += 1 + # Negative uninstall: a live protected backend cannot be removed by a + # rootless isolated fixture, and settings/metadata stay intact. + env = dict(os.environ, HOME=str(home), PYTHONDONTWRITEBYTECODE="1") + process = subprocess.run([sys.executable,"-B",str(UNINSTALL),"--verify"], + env=env,text=True,capture_output=True,timeout=8) + need(process.returncode == 0, "UNINSTALL_MANAGED_HASH_PRECHECK_FAILED:" + process.stderr) + tests += 1 + # Negative: the public uninstall CLI refuses to remove user code while the + # actual protected backend remains; no fixture config may be harmed. + if (pathlib.Path("/usr/lib/directinternetmethod/control.sh").exists() or + pathlib.Path("/var/lib/directinternetmethod/install.json").exists()): + negative = subprocess.run([sys.executable, "-B", str(UNINSTALL), "--remove-after-backend"], + env=env, text=True, capture_output=True, timeout=8) + need(negative.returncode != 0 and "PROTECTED_BACKEND_STILL_PRESENT" in negative.stderr, + "UNINSTALL_PROTECTED_BACKEND_FAIL_CLOSED_MISSED") + unchanged_data(app, user) + else: + # A clean ephemeral CI host has no protected backend to guard against. + # Verify the production code includes the fail-closed root-state gate. + uninstall_source = UNINSTALL.read_text(encoding="utf8") + need("PROTECTED_BACKEND_STILL_PRESENT_REFUSE_USER_CODE_REMOVAL" in uninstall_source, + "CLEAN_RUNNER_MISSING_PROTECTED_BACKEND_GUARD") + tests += 1 + # Independently exercise the pure OWNED file deletion step against a fake + # HOME. It may remove only hashes recorded for managed code, preserving + # custom Router Gateway providers, all personal settings and runtime logs. + pure = """ +import importlib.util,pathlib,sys +z=importlib.util.spec_from_file_location("dimuninstall",sys.argv[1]);m=importlib.util.module_from_spec(z);z.loader.exec_module(m) +app,meta,known=m.load_owned() +assert len(known)>=7 +n=m.remove_known_files(app,meta,known) +assert n>=7 +print("PASS_PURE_OWNED_UNINSTALL",n) +""" + custom_launcher=home / ".local/bin/direct-internet-method" + custom_launcher.parent.mkdir(parents=True, exist_ok=True) + custom_launcher.write_bytes(b"USER_MODIFIED_LAUNCHER_MUST_NOT_BE_REMOVED\n") + pure_run=subprocess.run([sys.executable, "-B", "-c", pure, str(UNINSTALL)], + env=env, text=True, capture_output=True, timeout=8) + need(pure_run.returncode == 0, "OWNED_UNINSTALL_SANDBOX_FAILURE:" + pure_run.stderr) + unchanged_data(app, user) + need((app / tx.PROVIDERS).read_bytes() == provider, "UNINSTALL_DESTROYED_CUSTOM_ROUTER_PROVIDER") + need(not (app / "direct_internet_method.py").exists() and + not (app / "INSTALL.json").exists(), "UNINSTALL_DID_NOT_REMOVE_OWNED_CODE") + need(app.exists(), "USER_DATA_DIRECTORY_REMOVED") + need(custom_launcher.read_bytes()==b"USER_MODIFIED_LAUNCHER_MUST_NOT_BE_REMOVED\n", + "UNINSTALL_REMOVED_UNTRACKED_CUSTOM_LAUNCHER") + tests += 1 + need(all(x in install_source for x in ( + 'if [ ! -e "$BIN/direct-internet-method" ]', + 'if [ ! -e "$DESKTOP_FILE" ]', + 'if [ ! -e "$ICONS/direct-internet-method.svg" ]', + 'p.read_bytes()==expected', + '"managedLaunchersSha256":launchers')), + "INSTALL_UPGRADE_MIGHT_OVERWRITE_CUSTOM_USER_LAUNCHER") + tests += 1 + + # Real-package regression: missing sibling provider source refuses stage. + missing_src = source_tree(tmp, "missing-provider") + (missing_src / tx.PROVIDER_SOURCE).unlink() + missing_home, missing_app, _, missing_user, _ = initial(tmp / "case-missing-provider") + calls(missing_home, missing_src, "--stage", False) + need(not (missing_home / ".local/state/DirectInternetMethod/update-pending").exists(), + "MISSING_REAL_PACKAGE_PROVIDER_CREATED_PENDING_TRANSACTION") + unchanged_data(missing_app, missing_user) + tests += 1 + +print("PASS_LINUX_ONE_INSTALL_DATA_PRESERVING_TRANSACTION_NEGATIVE_FIRST_FIXTURES:",tests,"/",tests) + + +# R421 regression: execute the ACTUAL metadata heredoc from install.sh against +# a disposable HOME, replacing ONLY the root-owned backend lookup with a fixture. +# Never install, elevate, change the host network, or touch a real user profile. +with tempfile.TemporaryDirectory(prefix="dim-r421-f415-") as td: + sandbox = pathlib.Path(td) + fake_home = sandbox / "home" + fake_app = fake_home / ".local/share/DirectInternetMethod" + fake_app.mkdir(parents=True) + fake_router = fake_app / "router_gateway" + fake_router.mkdir() + custom_provider = fake_router / "providers.json" + custom_provider.write_bytes(b'{"customService":"KEEP_ORIGINAL"}\n') + user_setting = fake_app / "settings.json" + user_setting.write_bytes(b'{"custom":true}\n') + (fake_app / "direct-internet-method.svg").write_bytes(b"") + pending = fake_home / ".local/state/DirectInternetMethod/update-pending" + pending.mkdir(parents=True) + (pending / "manifest.json").write_text(json.dumps({ + "providerSha": "a" * 64, + "providerManaged": False, + "managed": {"direct_internet_method.py": {"sha": "b" * 64}, + "router_gateway/providers.json": {"sha": "c" * 64}}, + })) + protected = sandbox / "fake-protected-backend.json" + protected.write_text(json.dumps({"version": "1.5.5-rc1"})) + old_metadata = b'{"schema":2,"version":"1.5.1"}\n' + metadata_path = fake_app / "INSTALL.json" + metadata_path.write_bytes(old_metadata) + launcher_before = (fake_app / "direct-internet-method.svg").read_bytes() + provider_before = custom_provider.read_bytes() + settings_before = user_setting.read_bytes() + marker = 'python3 - "$APP/INSTALL.json" "$USER_TRANSACTION" <<' + "'PY'\n" + need(install_source.count(marker) == 1, "F415_WRITER_HEREDOC_MISSING_OR_DUPLICATE") + inline = install_source.split(marker, 1)[1].split("\nPY\n", 1)[0] + "\n" + backend_lookup = 'protected=json.loads(pathlib.Path("/var/lib/directinternetmethod/install.json").read_text(encoding="utf-8"))' + need(inline.count(backend_lookup) == 1, "F415_BACKEND_LOOKUP_DRIFT") + inline = inline.replace(backend_lookup, + 'protected=json.loads(pathlib.Path(sys.argv[3]).read_text(encoding="utf-8"))') + need("transaction_helper.atomic_bytes(" in inline and ".write_text(json.dumps(" not in inline, + "F415_DIRECT_METADATA_TRUNCATION_REGRESSION") + compile(inline, "R421_INSTALL_METADATA_HEREDOC", "exec") + fixture_env = dict(os.environ, HOME=str(fake_home), PYTHONDONTWRITEBYTECODE="1") + def run_inline(): + return subprocess.run( + [sys.executable, "-B", "-", str(metadata_path), str(TX), str(protected)], + input=inline, text=True, capture_output=True, + env=fixture_env, timeout=10) + # Fail-closed stale atomic stage never overwrites either item. + stage_path = fake_app / ".INSTALL.json.dim-update-stage" + stage_path.write_bytes(b"FOREIGN_OR_INTERRUPTED_STAGE_KEEP") + negative = run_inline() + need(negative.returncode != 0 and "STALE_ATOMIC_TEMP" in negative.stderr, + "F415_STALE_STAGE_WRONGLY_ACCEPTED:" + negative.stderr) + need(metadata_path.read_bytes() == old_metadata and + stage_path.read_bytes() == b"FOREIGN_OR_INTERRUPTED_STAGE_KEEP", + "F415_STALE_STAGE_WAS_MODIFIED") + stage_path.unlink() # Only our owned disposable test file. + # Symlink attempt must be rejected without modifying the foreign target. + foreign = sandbox / "foreign-metadata.json" + foreign.write_bytes(b"FOREIGN_TARGET_KEEP") + metadata_path.unlink() + metadata_path.symlink_to(foreign) + rejected = run_inline() + need(rejected.returncode != 0 and "FOREIGN_MANAGED_FILE_OR_SYMLINK" in rejected.stderr, + "F415_SYMLINK_WAS_NOT_REJECTED:" + rejected.stderr) + need(foreign.read_bytes() == b"FOREIGN_TARGET_KEEP" and metadata_path.is_symlink(), + "F415_SYMLINK_TARGET_CHANGED") + metadata_path.unlink() + metadata_path.write_bytes(old_metadata) + # Positive path replaces metadata once, preserves custom data and is private. + accepted = run_inline() + need(accepted.returncode == 0, "F415_ATOMIC_METADATA_WRITE_FAILED:" + accepted.stderr) + doc = json.loads(metadata_path.read_text(encoding="utf-8")) + need(doc["version"] == "1.5.5-rc1" and doc["schema"] == 2 + and doc["managedCodeSha256"] == {"direct_internet_method.py": "b" * 64} + and doc["preservedCustomProviderData"] is True, + "F415_METADATA_POSTCONDITION_INVALID") + need(metadata_path.stat().st_mode & 0o777 == 0o600, + "F415_METADATA_PERMISSIONS_NOT_PRIVATE") + need(not stage_path.exists(), "F415_ATOMIC_STAGE_LEFT_ON_SUCCESS") + need(custom_provider.read_bytes() == provider_before and + user_setting.read_bytes() == settings_before and + (fake_app / "direct-internet-method.svg").read_bytes() == launcher_before, + "F415_ATOMIC_WRITER_CHANGED_MUTABLE_USER_DATA") + print("PASS_F415_METADATA_ATOMIC_INSTALL_INLINE:", 3, "/", 3) + + +# R423 F418: real SIGKILL during actual atomic_bytes(). Explicit one-file +# recover requires exact operator-known stage SHA, a valid pending manifest, +# exact managed code, unchanged old metadata and intact user configuration. +with tempfile.TemporaryDirectory(prefix="dim-r423-real-sigkill-") as td: + case = pathlib.Path(td) + source = source_tree(case, "r423") + home, app, old, user, provider_before = initial(case / "installed") + calls(home, source, "--stage") + calls(home, source, "--apply") + pending = home / ".local/state/DirectInternetMethod/update-pending" + manifest = json.loads((pending / "manifest.json").read_text(encoding="utf-8")) + doc = { + "schema": 2, + "product": "Direct Internet Method", + "version": "1.5.5-rc1", + "platform": "linux-x86_64", + "installedUtc": "2026-10-12T00:00:00Z", + "networkMutationOnInstall": False, + "normalRunRequiresAdmin": False, + "systemBackend": "/usr/lib/directinternetmethod", + "protectedBackendVersion": "1.5.5-rc1", + "protectedBackendByteCompatible": True, + "onlineUpdate": "GitHub latest release + SHA256SUMS verification", + "managedProviderSha256": manifest["providerSha"], + "preservedCustomProviderData": not manifest["providerManaged"], + "managedCodeSha256": {k: v["sha"] for k, v in manifest["managed"].items() + if k != tx.PROVIDERS}, + "managedLaunchersSha256": {}, + "updateMode": "IN_PLACE_SINGLE_INSTALL_DATA_PRESERVED", + } + intended = (json.dumps(doc, indent=2) + "\n").encode("utf-8") + payload = case / "intended.json" + payload.write_bytes(intended) + install_record = app / tx.METADATA + stage_path = app / ".INSTALL.json.dim-update-stage" + env = dict(os.environ, HOME=str(home), PYTHONDONTWRITEBYTECODE="1") + child = r''' +import importlib.util, os, pathlib, signal, sys +spec = importlib.util.spec_from_file_location("dim_crash_helper", sys.argv[1]) +m = importlib.util.module_from_spec(spec) +spec.loader.exec_module(m) +def crash_before_replace(source, destination): + os.kill(os.getpid(), signal.SIGKILL) +m.os.replace = crash_before_replace +m.atomic_bytes(pathlib.Path(sys.argv[2]), pathlib.Path(sys.argv[3]).read_bytes(), 0o600) +''' + killed = subprocess.run([sys.executable, "-B", "-c", child, str(TX), + str(install_record), str(payload)], + env=env, text=True, capture_output=True, timeout=10) + need(killed.returncode == -9, "F418_DID_NOT_REAL_SIGKILL:" + str(killed.returncode)) + need(stage_path.is_file() and stage_path.read_bytes() == intended, + "F418_SIGKILL_STAGE_MISSING_OR_CHANGED") + need(install_record.read_bytes() == old[tx.METADATA], + "F418_SIGKILL_DAMAGED_ORIGINAL_METADATA") + digest_stage = digest(intended) + def recover(sha, positive=False): + result = subprocess.run([sys.executable, "-B", str(TX), + "--recover-metadata-stage", "--expected-stage-sha256", sha], + env=env, text=True, capture_output=True, timeout=10) + if positive: + need(result.returncode == 0 and "PASS_EXACT_OWNED" in result.stdout, + "F418_VALID_STAGE_NOT_RECOVERED:" + result.stderr) + else: + need(result.returncode != 0 and "FAIL_CLOSED" in result.stderr, + "F418_UNTRUSTED_STAGE_FALSE_ACCEPTED") + return result + cases = 0 + recover("0" * 64) + need(stage_path.read_bytes() == intended, "F418_WRONG_DIGEST_REMOVED_STAGE") + cases += 1 + stage_path.chmod(0o644) + recover(digest_stage) + need(stage_path.is_file(), "F418_NONPRIVATE_STAGE_REMOVED") + stage_path.chmod(0o600) + cases += 1 + stage_path.write_bytes(b'{"partial":') + recover(digest_stage) + need(stage_path.read_bytes() == b'{"partial":', "F418_PARTIAL_STAGE_REMOVED") + stage_path.write_bytes(intended) + stage_path.chmod(0o600) + cases += 1 + foreign = case / "foreign-not-ours" + foreign.write_bytes(b"FOREIGN_CONTENT_MUST_SURVIVE") + stage_path.unlink() + stage_path.symlink_to(foreign) + recover(digest_stage) + need(foreign.read_bytes() == b"FOREIGN_CONTENT_MUST_SURVIVE" and + stage_path.is_symlink(), "F418_SYMLINK_FOLLOWED_OR_DELETED") + stage_path.unlink() + stage_path.write_bytes(intended) + stage_path.chmod(0o600) + cases += 1 + original = install_record.read_bytes() + install_record.write_bytes(b"FOREIGN_CONCURRENT_METADATA") + recover(digest_stage) + need(stage_path.is_file() and install_record.read_bytes() == + b"FOREIGN_CONCURRENT_METADATA", "F418_CONCURRENT_METADATA_LOST") + install_record.write_bytes(original) + cases += 1 + managed = app / "direct_internet_method.py" + managed_bytes = managed.read_bytes() + managed.write_bytes(b"OTHER_ACTOR_CODE") + recover(digest_stage) + need(managed.read_bytes() == b"OTHER_ACTOR_CODE" and stage_path.is_file(), + "F418_FOREIGN_MANAGED_CODE_UNSAFE_RECOVERY") + managed.write_bytes(managed_bytes) + cases += 1 + settings = app / "settings.json" + original_settings = settings.read_bytes() + settings.write_bytes(b"USER_UPDATED_SETTINGS") + recover(digest_stage) + need(settings.read_bytes() == b"USER_UPDATED_SETTINGS" and stage_path.is_file(), + "F418_USER_EDIT_NOT_PRESERVED") + settings.write_bytes(original_settings) + cases += 1 + compromised = dict(doc) + compromised["managedCodeSha256"] = {"direct_internet_method.py": "0"*64} + stage_path.write_bytes((json.dumps(compromised) + "\n").encode()) + stage_path.chmod(0o600) + recover(digest(stage_path.read_bytes())) + need(stage_path.is_file(), "F418_FOREIGN_DOCUMENT_WITH_DIGEST_WRONGLY_REMOVED") + stage_path.write_bytes(intended) + stage_path.chmod(0o600) + cases += 1 + recover(digest_stage, positive=True) + need(not stage_path.exists() and pending.is_dir(), + "F418_ONLY_OWNED_STAGE_SHOULD_BE_REMOVED_PENDING_MUST_REMAIN") + need(install_record.read_bytes() == old[tx.METADATA], + "F418_RECOVERY_OVERWROTE_OLD_METADATA") + need((app / tx.PROVIDERS).read_bytes() == provider_before, + "F418_RECOVERY_CHANGED_CUSTOM_PROVIDER") + unchanged_data(app, user) + cases += 1 + # Explicit rollback after safe stage attestation restores EVERY old program + # file, original INSTALL.json and all personal settings without duplicate + # directories or app/backend/network mutations. + calls(home, source, "--rollback") + for name, previous in old.items(): + need((app / name).read_bytes() == previous, + "F418_ROLLBACK_OLD_BYTES_DRIFT:" + name) + unchanged_data(app, user) + need((app / tx.PROVIDERS).read_bytes() == provider_before and + not pending.exists(), "F418_ROLLBACK_USER_DATA_OR_SLOT_FAILED") + cases += 1 + # With no pending transaction, an arbitrary new staged file can never + # trigger cleanup (even with its SHA supplied by the caller). + stage_path.write_bytes(intended) + stage_path.chmod(0o600) + recover(digest_stage) + need(stage_path.read_bytes() == intended, "F418_NO_PENDING_STAGE_REMOVED") + cases += 1 + print("PASS_F418_SIGKILL_ATTESTED_STAGE_RECOVERY_NEGATIVE_FIRST:", + cases, "/", cases, "REAL_SIGKILL_CODE", killed.returncode) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py new file mode 100644 index 00000000..f9d28a68 --- /dev/null +++ b/tests/r519_versioned_package_contract.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +"""R519 negative-first immutable package, source manifest and version contracts. + +No installed writes: all mutations are confined to a temporary extracted ZIP +directory and compared to its original exact-SHA baseline. +""" +from __future__ import annotations +import ast,hashlib,importlib.util,json,pathlib,re,tempfile,unittest,zipfile +R=pathlib.Path(__file__).resolve().parents[1] +VERSION="1.5.5-rc1" +PACKAGE=R/"delivery"/f"DirectInternetMethod_{VERSION}_Linux_x86_64.zip" +GUI=R/"linux/app/direct_internet_method.py" +HELPER=R/"linux/app/direct_method_helper.sh" +ROOT_TXN=R/"linux/system/protected_upgrade_transaction.py" +INSTALLER=R/"linux/install.sh" +USER_TXN=R/"linux/app/user_update_transaction.py" +PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" +EXPECTED={"version":VERSION,"files":26,"sha256":"45A29A65B3E34302AD8E0745324EC2A1C418BB43669F9B95B15963B02CDFCC49"} +def digest(data):return hashlib.sha256(data).hexdigest().upper() +class Tests(unittest.TestCase): + @classmethod + def setUpClass(cls): + assert PACKAGE.is_file() + cls.info=zipfile.ZipFile(PACKAGE) + cls.n=cls.info.namelist() + cls.prefix=f"DirectInternetMethod_{VERSION}_Linux_x86_64/" + cls.rows=json.loads(cls.info.read(cls.prefix+"PACKAGE_MANIFEST.json"))["files"] + @classmethod + def tearDownClass(cls):cls.info.close() + def test_exact_zip_sha(self): + self.assertEqual(digest(PACKAGE.read_bytes()),EXPECTED["sha256"]) + def test_exact_manifest_version_and_members(self): + manifest=json.loads(self.info.read(self.prefix+"PACKAGE_MANIFEST.json")) + self.assertEqual((manifest["schema"],manifest["version"],manifest["platform"]),(1,VERSION,"linux-x86_64")) + self.assertEqual(len(manifest["files"]),EXPECTED["files"]) + self.assertEqual(len(self.n),EXPECTED["files"]+2) + self.assertEqual(len(self.n),len(set(self.n))) + self.assertEqual(set(self.n),{self.prefix+r["file"] for r in self.rows}|{ + self.prefix+"PACKAGE_MANIFEST.json",self.prefix+"SHA256SUMS.txt"}) + self.assertIsNone(self.info.testzip()) + def test_source_members_all_match_manifest(self): + hashes=set() + for item in self.rows: + path=item["file"] + self.assertFalse(pathlib.PurePosixPath(path).is_absolute()) + self.assertNotIn("..",pathlib.PurePosixPath(path).parts) + data=self.info.read(self.prefix+path) + self.assertEqual((len(data),digest(data)),(item["bytes"],item["sha256"])) + self.assertNotIn(path,hashes) + hashes.add(path) + sums="".join(f'{r["sha256"]} {r["file"]}\n' for r in self.rows) + self.assertEqual(self.info.read(self.prefix+"SHA256SUMS.txt").decode(),sums) + def test_versioned_components_present(self): + included={r["file"] for r in self.rows} + self.assertTrue({"system/encrypted_selective_dns.py","system/encrypted_selective_policy.py", + "system/protected_upgrade_transaction.py","system/upgrade_system.sh", + "app/direct_method_helper.sh","app/user_update_transaction.py"}.issubset(included)) + def test_r519b_uninstaller_accepts_only_new_ownership_version(self): + rel="app/user_uninstall_preserve.py" + got=self.info.read(self.prefix+rel) + declared=next(row for row in self.rows if row["file"]==rel) + self.assertEqual(digest(got),declared["sha256"]) + self.assertEqual(declared["sha256"],"6691AA7262307F41DDAD1B5EB409E1C7AAC3B2171B5285156045849D969F6B8A") + self.assertIn('doc.get("version") == "1.5.5-rc1"',got.decode()) + self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) + self.assertEqual(digest((R/"linux/app/user_uninstall_preserve.py").read_bytes()),declared["sha256"]) + def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self): + old=R/"evidence/R519_ORIGINAL_RC1_SUPERSEDED.zip" + self.assertTrue(old.is_file()) + self.assertEqual(digest(old.read_bytes()),"47776640A1BB8B5D4490244565DDD47A6A817A999347566917CC73E21F41EC24") + with zipfile.ZipFile(old) as before: + self.assertEqual(set(before.namelist()),set(self.n)) + changed={n for n in self.n if before.read(n)!=self.info.read(n)} + expected={self.prefix+"app/user_uninstall_preserve.py", + self.prefix+"PACKAGE_MANIFEST.json",self.prefix+"SHA256SUMS.txt"} + self.assertEqual(changed,expected) + def test_original_published_metadata_untouched(self): + data=json.loads((R/"RELEASE.json").read_text()) + self.assertEqual(data["version"],"1.5.4") + self.assertFalse(any("1.5.5-rc1" in x["file"] for x in self.rows)) + def test_fresh_root_contract_version(self): + root=(R/"linux/system/install_system.sh").read_text() + self.assertIn('{"schema":1,"version":"1.5.5-rc1"',root) + self.assertIn('encrypted_selective_dns.py',root) + self.assertIn('encrypted_selective_policy.py',root) + def test_protected_upgrade_exact_sha(self): + text=ROOT_TXN.read_text() + self.assertIn('TARGET_VERSION="1.5.5-rc1"',text) + self.assertIn('CURRENT_BACKEND_VERSION="1.5.2"',text) + self.assertIn('data.get("revision")=="R519"',text) + self.assertIn('receipt.get("revision")=="R519"',text) + self.assertIn("NATIVE_ACCEPTANCE_NOT_PROVEN",text) + self.assertIn("one slot",text.lower()) + def test_root_wrapper_pinned_identity(self): + src=(R/"linux/system/upgrade_system.sh").read_text() + self.assertIn(digest(ROOT_TXN.read_bytes()),src) + self.assertIn('DirectInternetMethod_1.5.5-rc1_Linux_x86_64',src) + self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" stage',src) + self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" apply',src) + self.assertNotIn(' commit',src) + def test_user_installer_never_pretends_committed(self): + src=INSTALLER.read_text() + self.assertIn("ROOT_UPGRADE_STAGED=1",src) + self.assertIn('R519_CANDIDATE_PENDING_NATIVE_ROOT_USER_COMMIT',src) + self.assertIn('pkexec /bin/bash "$HERE/system/upgrade_system.sh"',src) + self.assertIn('python3 "$USER_TRANSACTION" --rollback',src) + def test_gui_and_user_metadata_pins(self): + app=GUI.read_text() + self.assertIn('VERSION="1.5.5-rc1"',app) + self.assertIn('COEXIST_HELPER_SHA256="'+digest(HELPER.read_bytes()).lower()+'"',app) + self.assertIn('installed.get("version") == "1.5.5-rc1"',app) + updater=USER_TXN.read_text() + self.assertIn('len(package_doc["files"]) == 26',updater) + self.assertIn('data.get("version") == "1.5.5-rc1"',updater) + self.assertIn('"encrypted-dns-optin.txt"',updater) + def test_rc_semver_prevents_implicit_downgrade(self): + code=ast.parse(GUI.read_text()) + node=next(x for x in code.body if isinstance(x,ast.FunctionDef) and x.name=='_version_tuple') + ns={"re":re} + exec(compile(ast.Module(body=[node],type_ignores=[]),str(GUI),"exec"),ns) + v=ns["_version_tuple"] + self.assertLess(v("1.5.4"),v("1.5.5-rc1")) + self.assertLess(v("1.5.5-rc1"),v("1.5.5")) + self.assertLess(v("1.5.5-rc1"),v("1.5.5-rc2")) + def test_archive_manifest_negative_corrupted_payload(self): + with tempfile.TemporaryDirectory(prefix="R519_OFFLINE_TEST_") as temp: + target=pathlib.Path(temp)/f"DirectInternetMethod_{VERSION}_Linux_x86_64" + self.info.extractall(temp) + payload=target/"app/direct_internet_method.py" + payload.write_bytes(payload.read_bytes()+b"\n# tampered\n") + spec=importlib.util.spec_from_file_location("r519_user_txn",USER_TXN) + module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module) + with self.assertRaisesRegex((RuntimeError,AssertionError),".*"): + module.source_files(target) + def test_readme_is_explicitly_unreleased(self): + s=PKG_DOC.read_text() + self.assertIn("NOT RELEASED",s) + self.assertIn("1.5.5-rc1",s) + self.assertIn("v1.5.4",s) +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Tests)) + print("R519_COHERENCE_NEGATIVE_FIRST",result.testsRun,"cases", "PASS" if result.wasSuccessful() else "FAIL") + raise SystemExit(0 if result.wasSuccessful() else 1) From d843b674176bec7fe31b8922c2bbc1178a9c7968 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:26:45 +0330 Subject: [PATCH 002/188] test(ci): preserve R519 pre-fix provenance with pinned small fixtures Replace a local-only obsolete 8.3 MB ZIP dependency with SHA-pinned original 26-entry manifest and original changed uninstaller source. Check all 25 unchanged file digests and exact one-file delta. R519 15/15 candidate package tests pass on fresh checkout. --- .../r519_original/PACKAGE_MANIFEST.json | 138 ++++++++++++++++++ .../r519_original/user_uninstall_preserve.py | 132 +++++++++++++++++ tests/r519_versioned_package_contract.py | 40 ++++- 3 files changed, 302 insertions(+), 8 deletions(-) create mode 100644 tests/fixtures/r519_original/PACKAGE_MANIFEST.json create mode 100644 tests/fixtures/r519_original/user_uninstall_preserve.py diff --git a/tests/fixtures/r519_original/PACKAGE_MANIFEST.json b/tests/fixtures/r519_original/PACKAGE_MANIFEST.json new file mode 100644 index 00000000..171ffac0 --- /dev/null +++ b/tests/fixtures/r519_original/PACKAGE_MANIFEST.json @@ -0,0 +1,138 @@ +{ + "schema": 1, + "product": "Direct Internet Method", + "version": "1.5.5-rc1", + "platform": "linux-x86_64", + "files": [ + { + "file": "README.md", + "bytes": 2277, + "sha256": "2E2D817FD4728903E7085C4D7D02786107206881DA47FE4DFA6AA3C6F4E4AF76" + }, + { + "file": "install.sh", + "bytes": 12035, + "sha256": "4970935FC3A8B886D7DE3C94C7F15A1F4D132348139BC1996B182334EBF99C1F" + }, + { + "file": "uninstall.sh", + "bytes": 2011, + "sha256": "FAB9EF8FFD12E4102F72D227DD72FD0703944759667E31C8CF04995282C72E43" + }, + { + "file": "system/install_system.sh", + "bytes": 5109, + "sha256": "5C37143358CF9C85B36877DCD2CD454AC8D7FF5A0DB589EA1B96DD69B2F144A8" + }, + { + "file": "system/control.sh", + "bytes": 799, + "sha256": "10BACBC871E5CEA1B109A82130AE2E1C490BC26E4E1CE91CACC6D893BE2C0A49" + }, + { + "file": "system/selective_dns.py", + "bytes": 22253, + "sha256": "9701D1C0D03499F24678CF31E9FE0B68015EFA1EF3CA9E3653035B0B27727D65" + }, + { + "file": "system/encrypted_selective_dns.py", + "bytes": 25600, + "sha256": "6DA9BAB94E1F4AF5C9FBE0742F96B3E2B5A0392294B27EDA3F4012095D9993DA" + }, + { + "file": "system/encrypted_selective_policy.py", + "bytes": 4263, + "sha256": "BCADC01C4A0DEDA0AB7C678155BE6F6960D3B68D9CFE1307DF85A94F18785663" + }, + { + "file": "system/protected_upgrade_transaction.py", + "bytes": 19091, + "sha256": "4CE00525AF4773FE25CB7E3E5EDFD661B6A30C6B677964E12F24BCA6650F328D" + }, + { + "file": "system/upgrade_system.sh", + "bytes": 2202, + "sha256": "8246D48A5CB0E812E370E7883F3EFEE39A9F44931DE1B054F3FC62A6FC067A09" + }, + { + "file": "system/uninstall_system.sh", + "bytes": 1317, + "sha256": "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530" + }, + { + "file": "app/direct_internet_method.py", + "bytes": 48905, + "sha256": "A342342D2982701E3379530BACEED6B068BC90735289D3C31860830ECDC1275F" + }, + { + "file": "app/router_gateway.py", + "bytes": 5914, + "sha256": "936376D29F05B0E8B37FA6BD8A4CDB3F3160463E138EE4ABAB43E18AECFC1C86" + }, + { + "file": "app/router_gateway_core.py", + "bytes": 3351, + "sha256": "7817DD0BAE1E42D82A85FCCE823E872D30CC4A2E95D515D0C1540A29D7DF4A90" + }, + { + "file": "app/user_update_transaction.py", + "bytes": 27787, + "sha256": "A909BBB12B78875A9438C756AE2DCAFF05E0DF2EBB5551E97544D2B20C46346F" + }, + { + "file": "app/user_uninstall_preserve.py", + "bytes": 5150, + "sha256": "9A4A4C37E3F63F1602E15454100150D209E3CD099DE80504B560F31D42D3C0EF" + }, + { + "file": "app/router_gateway/providers.json", + "bytes": 6912, + "sha256": "EB9AF7A7BA94B068CE4C2930E61DAF0E7409106B11EE1546F46E33DA0B037A19" + }, + { + "file": "app/direct_method_helper.sh", + "bytes": 34771, + "sha256": "B6641270D1ED8687A2FB1331016F212533BFD4E490331213FF24684D2999A397" + }, + { + "file": "app/hosts.txt", + "bytes": 269, + "sha256": "F9B2E12B4B77CAAD86FA15A14AC546A4611527280E4A0ECE5566EB1FF1B07184" + }, + { + "file": "app/adult_hosts_fallback.txt", + "bytes": 448, + "sha256": "25BA72F6D56EF1C405115C7AA5D6ED1985E8F6D56F306A95D5AD800602A75288" + }, + { + "file": "app/strong_override_hosts.txt", + "bytes": 40, + "sha256": "A706F1035230EDDFB1EA5037A4234AEA351018265F2B359DA4C9E1347A3A900A" + }, + { + "file": "app/direct-internet-method.svg", + "bytes": 1275, + "sha256": "305A2FA1F5310F9619110E805194846FD191FBE7C90843D3BF7492D8C1D155C3" + }, + { + "file": "runtime/ctrld", + "bytes": 7961828, + "sha256": "CA64579A2BC866CF72F3E46663E551162AFEFF0D448E899A01817A095E45B071" + }, + { + "file": "runtime/nfqws", + "bytes": 125760, + "sha256": "F34615964D7321650197CD69D3F7CBFDAABE8118B5A0D57C6E41DCCDFF658999" + }, + { + "file": "licenses/LICENSE-ctrld.txt", + "bytes": 1101, + "sha256": "55D82D13FE55D4B2AEC42166FCCD3C16C267B7131ECF2FBD7C3C6C3463494501" + }, + { + "file": "licenses/LICENSE-zapret.txt", + "bytes": 1069, + "sha256": "DCF5ABD3E5D876C1065982871C0CEC7368C0E61FC795C541798729516BB6B54F" + } + ] +} diff --git a/tests/fixtures/r519_original/user_uninstall_preserve.py b/tests/fixtures/r519_original/user_uninstall_preserve.py new file mode 100644 index 00000000..c881bf4f --- /dev/null +++ b/tests/fixtures/r519_original/user_uninstall_preserve.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Fail-closed Direct Internet Method uninstall of owned program files ONLY. + +Protected backend removal must finish first. Never remove user settings, +custom hosts, adult coverage state, scoped strategy, router providers or logs. +Never recursively delete the mixed application/data directory. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import pathlib +import stat +import sys + +CODE_NAMES = frozenset({ + "direct_internet_method.py", "router_gateway.py", "router_gateway_core.py", + "direct-internet-method.svg", "uninstall.sh", "user_uninstall_preserve.py", + "user_update_transaction.py", +}) +EXTERNAL_NAMES = frozenset({ + ".local/bin/direct-internet-method", + ".local/bin/direct-internet-method-uninstall", + ".local/share/applications/io.github.god13emad.DirectInternetMethod.desktop", + ".local/share/icons/hicolor/scalable/apps/direct-internet-method.svg", +}) +APPNAME = "DirectInternetMethod" + + +def ensure(ok, error): + if not ok: + raise RuntimeError(error) + + +def sha(path): + h = hashlib.sha256() + with path.open("rb") as stream: + for block in iter(lambda: stream.read(1 << 20), b""): + h.update(block) + return h.hexdigest() + + +def load_owned(): + home = pathlib.Path.home() + app = home / ".local/share" / APPNAME + ensure(os.geteuid() != 0 and not app.is_symlink() and app.is_dir(), + "UNINSTALL_OWNER_HOME_INVALID") + meta = app / "INSTALL.json" + ensure(meta.is_file() and not meta.is_symlink(), "UNINSTALL_INSTALL_METADATA_MISSING") + doc = json.loads(meta.read_text(encoding="utf8")) + ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.4", + "UNINSTALL_MANIFEST_OWNER_VERSION_UNVERIFIED") + codes = doc.get("managedCodeSha256") + launchers = doc.get("managedLaunchersSha256") + ensure(isinstance(codes, dict) and set(codes) <= CODE_NAMES and + set(CODE_NAMES - {"user_uninstall_preserve.py"}) <= set(codes), + "UNINSTALL_MANAGED_CODE_MANIFEST_UNKNOWN") + ensure(isinstance(launchers, dict) and set(launchers) <= EXTERNAL_NAMES, + "UNINSTALL_EXTERNAL_SHORTCUTS_MANIFEST_UNKNOWN") + tracked = {} + for rel, expected in codes.items(): + p = app / rel + ensure(p.is_file() and not p.is_symlink() and not p.parent.is_symlink() and + p.stat().st_uid == os.getuid() and sha(p) == expected, + "UNINSTALL_CODE_FOREIGN_CHANGE_PRESERVED:" + rel) + tracked[str(p)] = (p, expected) + for rel, expected in launchers.items(): + p = home / rel + ensure(p.is_file() and not p.is_symlink() and not p.parent.is_symlink() and + p.stat().st_uid == os.getuid() and sha(p) == expected, + "UNINSTALL_SHORTCUT_FOREIGN_CHANGE_PRESERVED:" + rel) + tracked[str(p)] = (p, expected) + # Any metadata altered by other software is not destroyed without an exact + # owner check. The program metadata itself is not personal configuration. + ensure(meta.stat().st_uid == os.getuid(), "INSTALL_METADATA_FOREIGN_OWNER") + return app, meta, tracked + + +def verify(): + app, meta, tracked = load_owned() + print(json.dumps({"status": "PASS_OWNED_FILES_IDENTIFIED_NO_MUTATION", + "managedExecutableFiles": len(tracked), + "userSettingsPreserved": True, + "externalRootRequiresRemovalFirst": True})) + + +def remove_known_files(app, meta, tracked): + """Pure owned-file step, callable with a disposable HOME in tests. + Production only calls it after independently verified backend removal.""" + for path, expected in tracked.values(): + ensure(path.is_file() and not path.is_symlink() and sha(path) == expected, + "PROGRAM_FILE_CHANGED_AFTER_PRECHECK:" + str(path)) + for path, _ in tracked.values(): + path.unlink() + # User data remains, including mutable router providers and runtime logs. + meta.unlink() + return len(tracked) + + +def remove_after_backend(): + app, meta, files = load_owned() + root = pathlib.Path("/usr/lib/directinternetmethod") + record = pathlib.Path("/var/lib/directinternetmethod/install.json") + ensure(not (root / "control.sh").exists() and not record.exists(), + "PROTECTED_BACKEND_STILL_PRESENT_REFUSE_USER_CODE_REMOVAL") + count = remove_known_files(app, meta, files) + print(json.dumps({"status": "PASS_OWNED_PROGRAM_FILES_REMOVED", + "userDataDirectoryPreserved": str(app), + "userSettingsDeleted": False, + "onlyOwnedFilesTouched": True})) + + +def main(): + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("--verify", action="store_true") + group.add_argument("--remove-after-backend", action="store_true") + args = parser.parse_args() + if args.verify: + verify() + else: + remove_after_backend() + + +if __name__ == "__main__": + try: + main() + except Exception as exc: + print("DIM_UNINSTALL_FAIL_CLOSED:" + str(exc), file=sys.stderr) + sys.exit(86) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index f9d28a68..13b8444b 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -65,15 +65,39 @@ def test_r519b_uninstaller_accepts_only_new_ownership_version(self): self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) self.assertEqual(digest((R/"linux/app/user_uninstall_preserve.py").read_bytes()),declared["sha256"]) def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self): + # CI does not commit an 8.3 MB obsolete candidate ZIP. Its original + # manifest and changed single source member are pinned byte-for-byte + # as compact historical fixtures (all 26 original file SHA entries). + fixtures=R/"tests/fixtures/r519_original" + old_manifest=fixtures/"PACKAGE_MANIFEST.json" + old_source=fixtures/"user_uninstall_preserve.py" + self.assertEqual(digest(old_manifest.read_bytes()), + "EA43CD77EAA2979AC970A8B47145881C767519E3D024BF907167A2FE3FD6C05E") + self.assertEqual(digest(old_source.read_bytes()), + "9A4A4C37E3F63F1602E15454100150D209E3CD099DE80504B560F31D42D3C0EF") + prev=json.loads(old_manifest.read_text()) + self.assertEqual((prev["schema"],prev["version"],prev["platform"]),(1,VERSION,"linux-x86_64")) + before={row["file"]:row for row in prev["files"]} + after={row["file"]:row for row in self.rows} + self.assertEqual(set(before),set(after)) + self.assertEqual(len(before),26) + changed={name for name in before if before[name]!=after[name]} + self.assertEqual(changed,{"app/user_uninstall_preserve.py"}) + previous=before["app/user_uninstall_preserve.py"] + self.assertEqual((previous["bytes"],previous["sha256"]), + (len(old_source.read_bytes()),digest(old_source.read_bytes()))) + self.assertNotEqual(previous["sha256"],after["app/user_uninstall_preserve.py"]["sha256"]) + # If the old full binary happens to be available locally, verify the + # exact historical archive as additional provenance, not a CI prerequisite. old=R/"evidence/R519_ORIGINAL_RC1_SUPERSEDED.zip" - self.assertTrue(old.is_file()) - self.assertEqual(digest(old.read_bytes()),"47776640A1BB8B5D4490244565DDD47A6A817A999347566917CC73E21F41EC24") - with zipfile.ZipFile(old) as before: - self.assertEqual(set(before.namelist()),set(self.n)) - changed={n for n in self.n if before.read(n)!=self.info.read(n)} - expected={self.prefix+"app/user_uninstall_preserve.py", - self.prefix+"PACKAGE_MANIFEST.json",self.prefix+"SHA256SUMS.txt"} - self.assertEqual(changed,expected) + if old.exists(): + self.assertEqual(digest(old.read_bytes()), + "47776640A1BB8B5D4490244565DDD47A6A817A999347566917CC73E21F41EC24") + with zipfile.ZipFile(old) as z: + self.assertEqual(set(z.namelist()),set(self.n)) + self.assertEqual(z.read(self.prefix+"PACKAGE_MANIFEST.json"),old_manifest.read_bytes()) + self.assertEqual(z.read(self.prefix+"app/user_uninstall_preserve.py"),old_source.read_bytes()) + self.assertEqual(z.testzip(),None) def test_original_published_metadata_untouched(self): data=json.loads((R/"RELEASE.json").read_text()) self.assertEqual(data["version"],"1.5.4") From 0195ecd40479a336e7c10d3a14e30a58294a53f8 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:31:30 +0330 Subject: [PATCH 003/188] test(ci): model native user State owner separately from runner uid Fix fake Root lifecycle test's host-specific UID=1000 assumption. Retain real native_engine_attested uid/mode/binary checks unchanged. Explicit virtual native State st_uid=1000 in test-owned temp directory; 8/8 native mock lifecycle regressions PASS on MMZ. --- tests/encrypted_selective_dns_fake_lifecycle.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/encrypted_selective_dns_fake_lifecycle.py b/tests/encrypted_selective_dns_fake_lifecycle.py index be17d314..3a34aca8 100644 --- a/tests/encrypted_selective_dns_fake_lifecycle.py +++ b/tests/encrypted_selective_dns_fake_lifecycle.py @@ -71,11 +71,17 @@ def proxy_text(path,*args,**kwargs): def simulated_owner_stat(path,*args,**kwargs): result=real_stat(path,*args,**kwargs) if str(path) in (str(self.state_dir),str(self.config),str(self.state)): - # Only the test-private directory and owned config/state appear as root-owned. - # This is a mock, never a real permission or chown mutation. + # Root-owned test-private files only: virtual uid=0, no chown. values=list(result) values[4]=0 return os.stat_result(values) + if str(path)==str(self.native): + # The native user State must belong to the simulated uid=1000, + # independently of the real GitHub Actions runner's uid. + # No host ownership or permissions are changed. + values=list(result) + values[4]=1000 + return os.stat_result(values) return result st.enter_context(patch.object(pathlib.Path,"stat",simulated_owner_stat)) st.enter_context(patch.object(pathlib.Path,"read_text",proxy_text)) From f248a98df7fe3172653fda902be02803fdd9e2ef Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 08:20:06 +0330 Subject: [PATCH 004/188] test(ci): enforce RC1 installer metadata activation contract (R526) Add 11 rootless negative-first checks for protected backend metadata, atomic INSTALL.json activation, symlink/orphan fail-closed, and user-data preservation. CI executes only a fake HOME/Root; no release or runtime changes. --- .github/workflows/ci.yml | 5 + tests/r526_installer_metadata_contract.py | 183 ++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 tests/r526_installer_metadata_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77c0ede4..80eb92d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,11 @@ jobs: python3 -B tests/r519_versioned_package_contract.py python3 -B tests/protected_upgrade_transaction_contract.py python3 -B tests/r519_user_update_transaction_contract.py + - name: R526 Linux installer metadata activation (hermetic fake Root) + shell: bash + run: | + set -euo pipefail + python3 -B tests/r526_installer_metadata_contract.py - name: R517 encrypted DNS and Tailscale negative-first contracts run: | set -euo pipefail diff --git a/tests/r526_installer_metadata_contract.py b/tests/r526_installer_metadata_contract.py new file mode 100644 index 00000000..874b97ef --- /dev/null +++ b/tests/r526_installer_metadata_contract.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +"""R526: hermetic Linux 1.5.5-rc1 INSTALL.json activation regression gate. + +Run the exact inline metadata program from linux/install.sh against a disposable +HOME and a fake protected Root metadata file. No real installer, sudo, systemd, +DNS, routes, nftables, Tailscale, or live user installation is touched. +This verifies implementation contracts, not physical-host acceptance. +""" +from __future__ import annotations + +import hashlib +import json +import os +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest + +BASE = Path(__file__).resolve().parents[1] +INSTALL = BASE / "linux/install.sh" +USER_TX = BASE / "linux/app/user_update_transaction.py" +BEGIN = """python3 - "$APP/INSTALL.json" "$USER_TRANSACTION" <<'PY'\n""" +END = "\nPY\n" +PROTECTED = 'pathlib.Path("/var/lib/directinternetmethod/install.json")' +VERSION = "1.5.5-rc1" + + +def exact_inline_program() -> str: + source = INSTALL.read_text(encoding="utf-8") + if source.count(BEGIN) != 1: + raise AssertionError("INLINE_METADATA_ENTRY_NOT_UNIQUE") + tail = source.split(BEGIN, 1)[1] + if tail.count(END) != 1 or tail.count(PROTECTED) != 1: + raise AssertionError("INLINE_METADATA_PROVENANCE_NOT_UNIQUE") + program = tail.split(END, 1)[0] + if "transaction_helper.atomic_bytes(" not in program: + raise AssertionError("METADATA_ATOMIC_WRITER_MISSING") + return program + + +class InstallerMetadata(unittest.TestCase): + def setUp(self) -> None: + self.temp = tempfile.TemporaryDirectory(prefix="DIM_R526_META_") + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.home = self.root / "home" + self.app = self.home / ".local/share/DirectInternetMethod" + self.app.mkdir(parents=True) + self.pending = self.home / ".local/state/DirectInternetMethod/update-pending" + self.pending.mkdir(parents=True) + self.protected = self.root / "protected.json" + self.protected.write_text(json.dumps({"schema": 1, "version": VERSION, "uid": 1000})) + self.target = self.app / "INSTALL.json" + self.original = b'{"schema":2,"version":"1.5.4","userNote":"KEEP"}\n' + self.target.write_bytes(self.original) + (self.app / "direct-internet-method.svg").write_bytes(b"FIXTURE_ICON") + self.custom = self.app / "custom-hosts.txt" + self.custom.write_bytes(b"KEEP_CUSTOM_HOSTS\n") + self.manifest = { + "schema": 1, + "version": VERSION, + "managed": { + "direct_internet_method.py": {"sha": "a" * 64, "mode": 0o755}, + "user_update_transaction.py": {"sha": "b" * 64, "mode": 0o755}, + }, + "providerSha": "c" * 64, + "providerManaged": True, + } + (self.pending / "manifest.json").write_text(json.dumps(self.manifest)) + self.program = exact_inline_program() + + def invoke(self) -> subprocess.CompletedProcess[str]: + if self.program.count(PROTECTED) != 1: + raise AssertionError("PROTECTED_PATH_PROVENANCE_CHANGED") + # Override one exact path ONLY inside the disposable child process. + # Never edit the package, protected host metadata, or user files. + rendered = self.program.replace(PROTECTED, "pathlib.Path(" + repr(str(self.protected)) + ")") + env = dict(os.environ) + env["HOME"] = str(self.home) + return subprocess.run( + [sys.executable, "-B", "-", str(self.target), str(USER_TX)], + input=rendered + "\n", + text=True, + capture_output=True, + env=env, + timeout=12, + check=False, + ) + + def unchanged_on_failure(self) -> None: + result = self.invoke() + self.assertNotEqual(result.returncode, 0, result.stdout) + self.assertEqual(self.target.read_bytes(), self.original) + self.assertEqual(self.custom.read_bytes(), b"KEEP_CUSTOM_HOSTS\n") + + def test_01_entrypoint_and_exact_source_contract(self) -> None: + self.assertTrue(INSTALL.is_file() and USER_TX.is_file()) + self.assertEqual(len(self.program.splitlines()), 64) + self.assertEqual(self.program.count(PROTECTED), 1) + self.assertIn('assert protected.get("version") == "1.5.5-rc1"', self.program) + self.assertIn('transaction_helper.atomic_bytes(', self.program) + + def test_02_in_place_metadata_and_mutable_preservation(self) -> None: + result = self.invoke() + self.assertEqual(result.returncode, 0, result.stderr[-700:]) + doc = json.loads(self.target.read_text()) + self.assertEqual(doc["schema"], 2) + self.assertEqual(doc["version"], VERSION) + self.assertEqual(doc["protectedBackendVersion"], VERSION) + self.assertEqual(doc["managedProviderSha256"], self.manifest["providerSha"]) + self.assertEqual( + doc["managedCodeSha256"], + {key: row["sha"] for key, row in self.manifest["managed"].items()}, + ) + self.assertIsInstance(doc["managedLaunchersSha256"], dict) + self.assertFalse(doc["networkMutationOnInstall"]) + self.assertFalse(doc["normalRunRequiresAdmin"]) + self.assertEqual(doc["updateMode"], "IN_PLACE_SINGLE_INSTALL_DATA_PRESERVED") + self.assertEqual(self.target.stat().st_mode & 0o777, 0o600) + self.assertEqual(self.custom.read_bytes(), b"KEEP_CUSTOM_HOSTS\n") + + def test_03_old_protected_backend_fails_closed(self) -> None: + self.protected.write_text('{"version":"1.5.2"}') + self.unchanged_on_failure() + + def test_04_missing_protected_metadata_fails_closed(self) -> None: + self.protected.unlink() + self.unchanged_on_failure() + + def test_05_missing_pending_transaction_fails_closed(self) -> None: + (self.pending / "manifest.json").unlink() + self.unchanged_on_failure() + + def test_06_malformed_pending_transaction_fails_closed(self) -> None: + (self.pending / "manifest.json").write_bytes(b"{INVALID JSON") + self.unchanged_on_failure() + + def test_07_symlink_metadata_never_overwrites_foreign(self) -> None: + foreign = self.root / "foreign.txt" + foreign.write_bytes(b"FOREIGN_DO_NOT_MODIFY") + self.target.unlink() + self.target.symlink_to(foreign) + result = self.invoke() + self.assertNotEqual(result.returncode, 0) + self.assertEqual(foreign.read_bytes(), b"FOREIGN_DO_NOT_MODIFY") + self.assertTrue(self.target.is_symlink()) + + def test_08_orphan_stage_preserved_without_mutation(self) -> None: + orphan = self.app / ".INSTALL.json.dim-update-stage" + orphan.write_bytes(b"UNKNOWN_OWNER_STAGE") + self.unchanged_on_failure() + self.assertEqual(orphan.read_bytes(), b"UNKNOWN_OWNER_STAGE") + + def test_09_commit_requires_new_metadata_in_transaction_source(self) -> None: + transaction = USER_TX.read_text(encoding="utf-8") + self.assertIn('METADATA = "INSTALL.json"', transaction) + self.assertIn("def check_metadata(app, data):", transaction) + self.assertIn("check_metadata(app, data)", transaction) + self.assertIn("def rollback():", transaction) + self.assertIn("def recover_metadata_stage(", transaction) + + def test_10_no_network_service_mutation_in_activation_program(self) -> None: + for disallowed in ( + "systemctl ", "tailscale set", "nft ", "ip route ", + "subprocess.run(", "os.system(", "/etc/resolv.conf", + ): + self.assertNotIn(disallowed, self.program) + self.assertNotIn("https://", self.program) + + def test_11_source_is_unmodified_by_child_execution(self) -> None: + before = (hashlib.sha256(INSTALL.read_bytes()).digest(), hashlib.sha256(USER_TX.read_bytes()).digest()) + result = self.invoke() + self.assertEqual(result.returncode, 0, result.stderr[-700:]) + after = (hashlib.sha256(INSTALL.read_bytes()).digest(), hashlib.sha256(USER_TX.read_bytes()).digest()) + self.assertEqual(after, before) + + +if __name__ == "__main__": + suite = unittest.defaultTestLoader.loadTestsFromTestCase(InstallerMetadata) + report = unittest.TextTestRunner(verbosity=2).run(suite) + print(f"R526_INLINE_METADATA_CI {report.testsRun} tests; failures={len(report.failures)}; errors={len(report.errors)}") + raise SystemExit(0 if report.wasSuccessful() else 1) From 1d021b6d6e32ec035904f4c5f2cfbd5ef72fb90c Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 09:30:27 +0330 Subject: [PATCH 005/188] test(ci): privileged Root file rollback on disposable Ubuntu runner (R527) Introduce 16 real UID0 mode/ownership/fsync/lock/rollback and fail-closed tests against a sandbox-only prefix; GitHub CI sudo fixture never invokes installed network/systemd and does not change public v1.5.4. Physical host acceptance remains open. --- .github/workflows/ci.yml | 7 + ...27_privileged_root_transaction_contract.py | 236 ++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 tests/r527_privileged_root_transaction_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80eb92d2..b5c12713 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,13 @@ jobs: run: | set -euo pipefail python3 -B tests/r526_installer_metadata_contract.py + - name: R527 privileged Root files on disposable GitHub Ubuntu VM + shell: bash + run: | + set -euo pipefail + # Real UID 0 on a fresh hosted VM; product writes only inside test-owned workspace fixture. + # The production Root CLI and real host network/systemd are never invoked. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r527_privileged_root_transaction_contract.py - name: R517 encrypted DNS and Tailscale negative-first contracts run: | set -euo pipefail diff --git a/tests/r527_privileged_root_transaction_contract.py b/tests/r527_privileged_root_transaction_contract.py new file mode 100644 index 00000000..bbffc0d3 --- /dev/null +++ b/tests/r527_privileged_root_transaction_contract.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +"""R527: real-root-owned file transaction on a DISPOSABLE GitHub Ubuntu VM. + +This is not a live product installation or physical network acceptance. +The unchanged candidate RootTransaction class executes its native owner/permission +checks and real chown/fsync/flock/atomic rename/rollback syscalls as UID 0, but +EVERY write is confined to a new fixture beneath GITHUB_WORKSPACE. Existing +network, /usr/lib, /etc, real user homes, systemd and Tailscale are NEVER changed. +Do not run on personal machines, even as root. +""" +from __future__ import annotations +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import stat +import sys +import unittest + +BASE = Path(__file__).resolve().parents[1] +ORIGINAL_TEST = BASE / "tests/protected_upgrade_transaction_contract.py" +ROOT_CODE = BASE / "linux/system/protected_upgrade_transaction.py" +REQUIRED_REPO = "GOD13emad/DirectInternetMethod" + +def read_module(path: Path, name: str): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError("SOURCE_IMPORT_ERROR") + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + +fixture_source = read_module(ORIGINAL_TEST, "r527_fixtures") +m = fixture_source.m + +def assert_ephemeral_runner(): + if os.name != "posix" or not sys.platform.startswith("linux"): + raise RuntimeError("R527_LINUX_REQUIRED") + if os.geteuid() != 0: + raise RuntimeError("R527_ROOT_REQUIRED") + if os.environ.get("CI") != "true" or os.environ.get("GITHUB_ACTIONS") != "true": + raise RuntimeError("R527_GITHUB_EPHEMERAL_RUNNER_REQUIRED") + if os.environ.get("GITHUB_REPOSITORY") != REQUIRED_REPO: + raise RuntimeError("R527_EXACT_GITHUB_REPOSITORY_REQUIRED") + workspace = os.environ.get("GITHUB_WORKSPACE") + runner_temp = os.environ.get("RUNNER_TEMP") + if not workspace or not runner_temp: + raise RuntimeError("R527_RUNNER_PATHS_REQUIRED") + if Path(workspace).resolve() != BASE.resolve(): + raise RuntimeError("R527_WORKSPACE_MISMATCH") + if not Path(runner_temp).is_dir(): + raise RuntimeError("R527_RUNNER_TEMP_MISSING") + if BASE == Path("/") or BASE == Path("/usr") or BASE == Path("/home"): + raise RuntimeError("R527_REAL_ROOT_BLOCKED") + if (Path("/run/directinternetmethod-encrypted").exists() + or Path("/run/directinternetmethod-selective/session.json").exists() + or Path("/home/mmz/.local/share/DirectInternetMethod/directmethod/state.json").exists()): + raise RuntimeError("R527_RUNNER_NOT_CLEAN") + if not ROOT_CODE.is_file() or not ORIGINAL_TEST.is_file(): + raise RuntimeError("R527_PINNED_SOURCES_MISSING") + # The product's CLI has no writable root-prefix option. This test only + # exercises RootTransaction(temp_root) imported as a class, never main(). + source = ROOT_CODE.read_text(encoding="utf8") + if 't=RootTransaction() # No CLI root-prefix/dry-run/test-mode option.' not in source: + raise RuntimeError("R527_PRODUCT_NATIVE_ENTRYPOINT_CHANGED") + +class PrivilegedRootFileTransaction(unittest.TestCase): + @classmethod + def setUpClass(cls): + assert_ephemeral_runner() + + def setUp(self): + self.fx = fixture_source.FakeFixture("test_success_stage_apply_rollback") + self.fx.setUp() + self.addCleanup(self.fx.tearDown) + self.root = self.fx.root + self.tx = self.fx.tx + self.assertNotEqual(self.root, Path("/")) + self.assertTrue(self.root.resolve().is_relative_to(BASE.resolve())) + self.assertEqual(self.root.stat().st_uid, 0) + self.assertEqual(self.root.stat().st_gid, 0) + # Activates original native file owner/lock/mode checks, but all + # writes continue to target self.root, not /. + self.tx.native = True + self.assertNotEqual(self.tx.root, Path("/")) + self.assertTrue(self.tx.meta.resolve().is_relative_to(self.root.resolve())) + self.assertTrue(self.tx.pending.resolve().is_relative_to(self.root.resolve())) + self.assertEqual(self.fx.meta.stat().st_uid, 0) + self.assertEqual(stat.S_IMODE(self.fx.meta.stat().st_mode), 0o644) + + def assert_old(self): + self.fx.all_old() + + def assert_new(self): + self.fx.all_new() + + def test_01_stage_real_root_owned_pending_backup(self): + self.assert_old() + outcome = self.tx.stage(self.fx.pkg) + self.assertEqual(outcome["status"], "STAGED") + self.assertEqual(self.tx.status()["status"], "STAGED") + self.assertEqual(self.tx.pending.stat().st_uid, 0) + self.assertEqual(stat.S_IMODE(self.tx.pending.stat().st_mode), 0o700) + record = self.tx.pending / "manifest.json" + self.assertEqual(record.stat().st_uid, 0) + self.assertEqual(stat.S_IMODE(record.stat().st_mode), 0o600) + self.assert_old() + self.assertEqual(self.tx.rollback()["status"], "ROLLED_BACK") + self.assert_old() + + def test_02_apply_native_root_modes_and_reversible_ownership(self): + self.tx.stage(self.fx.pkg) + self.assertEqual(self.tx.apply()["status"], "APPLIED") + self.assert_new() + for _, _, target, _, _, mode in self.fx.files: + p = self.root / target + self.assertEqual(p.stat().st_uid, 0) + self.assertEqual(p.stat().st_gid, 0) + self.assertEqual(stat.S_IMODE(p.stat().st_mode), mode) + self.assertEqual(self.fx.meta.stat().st_uid, 0) + self.assertEqual(stat.S_IMODE(self.fx.meta.stat().st_mode), 0o644) + self.assertEqual(self.tx.rollback()["status"], "ROLLED_BACK") + self.assert_old() + + def test_03_mid_apply_1_auto_rollback(self): + self.fault_recover(1) + + def test_04_mid_apply_2_auto_rollback(self): + self.fault_recover(2) + + def test_05_mid_apply_3_auto_rollback(self): + self.fault_recover(3) + + def test_06_mid_apply_4_auto_rollback(self): + self.fault_recover(4) + + def fault_recover(self, fail_after): + self.tx.stage(self.fx.pkg) + with self.assertRaisesRegex(RuntimeError, "INJECTED_TEST_FAILURE"): + self.tx.apply(fail_after=fail_after) + self.assert_old() + self.assertEqual(self.tx.status()["status"], "ROLLED_BACK") + + def test_07_reject_unknown_protected_file_before_stage(self): + victim = self.root / self.fx.files[0][2] + victim.write_bytes(b"FOREIGN_ROOT_FILE_NOT_OWNED") + with self.assertRaisesRegex(RuntimeError, "OLD_FILE_SHA"): + self.tx.stage(self.fx.pkg) + self.assertEqual(victim.read_bytes(), b"FOREIGN_ROOT_FILE_NOT_OWNED") + self.assertFalse(self.tx.pending.exists()) + + def test_08_reject_symlink_protected_target(self): + victim = self.root / self.fx.files[0][2] + victim.unlink() + victim.symlink_to(self.fx.meta) + with self.assertRaisesRegex(RuntimeError, "MISSING_OR_SYMLINK"): + self.tx.stage(self.fx.pkg) + self.assertTrue(victim.is_symlink()) + self.assertFalse(self.tx.pending.exists()) + + def test_09_reject_symlink_source_before_stage(self): + candidate = self.fx.pkg / "system/encrypted_selective_dns.py" + candidate.unlink() + candidate.symlink_to(self.fx.pkg / "app/direct_method_helper.sh") + with self.assertRaisesRegex(RuntimeError, "MISSING_OR_SYMLINK"): + self.tx.stage(self.fx.pkg) + self.assertFalse(self.tx.pending.exists()) + self.assert_old() + + def test_10_reject_foreign_pending_mutation(self): + self.tx.stage(self.fx.pkg) + victim = self.tx.pending / "new/encrypted_controller" + victim.write_bytes(b"FOREIGN_PENDING_MUTATION") + with self.assertRaisesRegex(RuntimeError, "PENDING_NEW_SHA"): + self.tx.apply() + self.assert_old() + + def test_11_reject_stale_second_stage(self): + self.tx.stage(self.fx.pkg) + with self.assertRaisesRegex(RuntimeError, "SLOT_ALREADY_EXISTS"): + self.tx.stage(self.fx.pkg) + self.assert_old() + + def test_12_commit_without_live_acceptance_forbidden(self): + self.tx.stage(self.fx.pkg) + self.tx.apply() + with self.assertRaisesRegex(RuntimeError, "NATIVE_ACCEPTANCE_MISSING"): + self.tx.commit(self.root / "not-a-real-acceptance.json") + self.assertEqual(self.tx.status()["status"], "APPLIED") + self.assertEqual(self.tx.rollback()["status"], "ROLLED_BACK") + self.assert_old() + + def test_13_reject_foreign_metadata_edit_before_apply(self): + self.tx.stage(self.fx.pkg) + self.fx.meta.write_text('{"schema":1,"version":"FOREIGN"}') + with self.assertRaisesRegex(RuntimeError, "FOREIGN_METADATA_CHANGE"): + self.tx.apply() + self.assertEqual(json.loads(self.fx.meta.read_text())["version"], "FOREIGN") + + def test_14_reject_foreign_edit_during_rollback(self): + self.tx.stage(self.fx.pkg) + self.tx.apply() + victim = self.root / self.fx.files[1][2] + victim.write_bytes(b"FOREIGN_AFTER_ROOT_APPLY") + with self.assertRaisesRegex(RuntimeError, "FOREIGN_TARGET_MODIFICATION"): + self.tx.rollback() + self.assertEqual(victim.read_bytes(), b"FOREIGN_AFTER_ROOT_APPLY") + self.assertEqual(self.tx.status()["status"], "APPLIED") + + def test_15_native_uid_mode_guard_is_in_effect(self): + marker = self.root / "etc/directinternetmethod.uid" + marker.write_text("1000\n") + self.assertEqual(marker.stat().st_uid, 0) + self.fx.meta.chmod(0o600) # old backend identity requires 0644 + with self.assertRaisesRegex(RuntimeError, "METADATA_MODE"): + self.tx.stage(self.fx.pkg) + self.assertFalse(self.tx.pending.exists()) + + def test_16_root_lock_parent_and_file_owner_enforced(self): + self.tx.stage(self.fx.pkg) + self.assertEqual(self.tx.lock.stat().st_uid, 0) + self.assertEqual(stat.S_IMODE(self.tx.lock.stat().st_mode), 0o600) + self.assertEqual(self.tx.status()["status"], "STAGED") + self.assert_old() + + +if __name__ == "__main__": + # Runs ONLY on disposable GitHub Ubuntu runners. No preflight-mode bypass. + suite = unittest.defaultTestLoader.loadTestsFromTestCase(PrivilegedRootFileTransaction) + result = unittest.TextTestRunner(verbosity=2).run(suite) + print("R527_REAL_UID0_ISOLATED_ROOT_TRANSACTION", + result.testsRun, + "PASS" if result.wasSuccessful() else "FAIL", + "NO_REAL_INSTALL_DNS_SYSTEMD_OR_TAILSCALE") + raise SystemExit(0 if result.wasSuccessful() else 1) From fcd548aaee8b2a0a0a273a1b957ae1ac763bba72 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 09:37:09 +0330 Subject: [PATCH 006/188] test(ci): exercise exact protected Root CLI on ephemeral GitHub Ubuntu VM (R528) Reconstruct published v1.5.4 root baseline from byte-pinned archive, run unmodified absolute-root stage/apply/rollback as UID0 in disposable hosted runner and verify restoration. Keep production and public Stable unchanged; physical DNS/GUI acceptance remains open. --- .github/workflows/ci.yml | 26 ++ ...r528_ephemeral_native_root_cli_contract.py | 257 ++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 tests/r528_ephemeral_native_root_cli_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5c12713..44db8158 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,6 +99,32 @@ jobs: fi done < <(find "$d" -type f -name '*.sh' | sort) + linux-native-root-ephemeral: + name: linux-native-root-ephemeral + needs: linux-source + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Rebuild exact RC1 package + run: python3 build_linux_package.py + - name: Download byte-pinned published Stable v1.5.4 package + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R528 true root CLI stage apply rollback on disposable VM + shell: bash + run: | + set -euo pipefail + # This job starts with an empty DIM root and cannot execute on a personal machine. + # All absolute-root writes occur inside the disposable GH-hosted Ubuntu VM only. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r528_ephemeral_native_root_cli_contract.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py new file mode 100644 index 00000000..a3876284 --- /dev/null +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python3 +"""R528 GitHub disposable Ubuntu VM: real absolute-root CLI stage/apply/rollback. + +NO production installation. This runs only in an ephemeral GitHub-hosted +ubuntu-latest VM with root privileges and a verified empty DIM state. It +creates the exact pinned v1.5.2 protected baseline at /usr/lib, /var/lib and +/etc in THAT VM using bytes from the official v1.5.4 Linux release ZIP. +Then runs the UNMODIFIED product RootTransaction() bound to real "/" with +actual native owner checks, filesystem locking, fsync, mode, stage, apply +and rollback. It NEVER invokes network service, GUI, DNS, Tailscale, NFT, +VPN, user installer, or a release operation. +""" +from __future__ import annotations +import hashlib +import importlib.util +import json +import os +from pathlib import Path, PurePosixPath +import pwd +import stat +import sys +import zipfile + +BASE=Path(__file__).resolve().parents[1] +STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" +CANDIDATE_SHA="45a29a65b3e34302ad8e0745324ec2a1c418bb43669f9b95b15963b02cdfcc49" +STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" +CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" +STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" +CANDIDATE_PREFIX="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" +SOURCE=BASE/"linux/system/protected_upgrade_transaction.py" +MAPPING={ + "selective_dns.py":"system/selective_dns.py", + "control.sh":"system/control.sh", + "direct_hosts.txt":"app/hosts.txt", + "adult_hosts_fallback.txt":"app/adult_hosts_fallback.txt", + "strong_override_hosts.txt":"app/strong_override_hosts.txt", + "ctrld":"runtime/ctrld", + "nfqws":"runtime/nfqws", + "LICENSE-ctrld.txt":"licenses/LICENSE-ctrld.txt", + "LICENSE-zapret.txt":"licenses/LICENSE-zapret.txt", + "direct_method_helper.sh":"app/direct_method_helper.sh", + "uninstall_system.sh":"system/uninstall_system.sh", +} + +def sha(raw:bytes)->str: + return hashlib.sha256(raw).hexdigest().lower() + +def fsha(path:Path)->str: + h=hashlib.sha256() + with path.open("rb") as f: + for piece in iter(lambda:f.read(1024*1024),b""): + h.update(piece) + return h.hexdigest() + +def need(condition,why): + if not condition:raise RuntimeError("R528_FAIL_CLOSED_"+why) + +def module(): + spec=importlib.util.spec_from_file_location("r528_real_product_root",SOURCE) + need(spec is not None and spec.loader is not None,"SOURCE_IMPORT_UNAVAILABLE") + m=importlib.util.module_from_spec(spec) + spec.loader.exec_module(m) + return m + +def prerequisites(): + need(sys.platform.startswith("linux") and os.geteuid()==0,"ROOT_LINUX_REQUIRED") + need(os.environ.get("CI")=="true" and os.environ.get("GITHUB_ACTIONS")=="true", + "DISPOSABLE_GITHUB_VM_REQUIRED") + need(os.environ.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod", + "REPOSITORY_AUTHORITY") + need(os.environ.get("RUNNER_ENVIRONMENT")=="github-hosted", + "NOT_HOSTED_RUNNER") + workspace=os.environ.get("GITHUB_WORKSPACE") + runner_temp=os.environ.get("RUNNER_TEMP") + need(bool(workspace) and Path(workspace).resolve()==BASE.resolve(),"WORKSPACE_AUTHORITY") + need(bool(runner_temp) and Path(runner_temp).is_dir(),"RUNNER_TEMP_AUTHORITY") + need(json.loads((BASE/"RELEASE.json").read_text())["version"]=="1.5.4", + "STABLE_RELEASE_AUTHORITY") + stable=Path(runner_temp)/"DIM_R528_STABLE.zip" + candidate=BASE/"delivery"/CANDIDATE_ARCHIVE + need(stable.is_file() and fsha(stable)==STABLE_SHA,"PINNED_PUBLIC_STABLE_ZIP") + need(candidate.is_file() and fsha(candidate)==CANDIDATE_SHA, + "PINNED_RC1_SOURCE_ZIP") + # This job must run in a fresh standalone GH VM. Never touch any existing + # DIM state, even if files appear to be owned by this test revision. + must_absent=[ + "/usr/lib/directinternetmethod", + "/var/lib/directinternetmethod", + "/etc/directinternetmethod.uid", + "/run/directinternetmethod-action.lock", + "/run/directinternetmethod-encrypted", + "/run/directinternetmethod-encrypted-ctrld.toml", + "/run/directinternetmethod-selective", + "/sys/class/net/dimesd0", + ] + for path in must_absent: + p=Path(path) + need(not p.exists() and not p.is_symlink(),"PREEXISTING_PATH_"+p.name) + user=pwd.getpwnam("runner") + need(user.pw_uid>0 and user.pw_dir=="/home/runner","STANDARD_HOSTED_RUNNER_USER") + need(not Path("/home/runner/.local/share/DirectInternetMethod/directmethod/state.json").exists(), + "EXISTING_ACTIVE_USER_ENGINE") + return stable,candidate,Path(runner_temp),user + +def archive_verify(z:zipfile.ZipFile,prefix:str,expected_count:int): + names=z.namelist() + need(len(names)==expected_count and len(names)==len(set(names)), + "ZIP_MEMBER_CARDINALITY") + need(z.testzip() is None,"ZIP_CRC") + for n in names: + posix=PurePosixPath(n) + need(n.startswith(prefix) and not posix.is_absolute() and + ".." not in posix.parts and "\\" not in n,"ZIP_PATH_TRAVERSAL") + member=z.getinfo(n) + mode=(member.external_attr>>16)&0xffff + need(not stat.S_ISLNK(mode),"ZIP_SYMLINK_DENIED") + +def stage_exact_candidate(candidate_zip:Path,temp:Path)->Path: + extract=temp/"DIM_R528_CANDIDATE" + need(not extract.exists() and not extract.is_symlink(),"TEMP_SOURCE_COLLISION") + extract.mkdir(mode=0o700) + with zipfile.ZipFile(candidate_zip) as z: + archive_verify(z,CANDIDATE_PREFIX,28) + manifest=json.loads(z.read(CANDIDATE_PREFIX+"PACKAGE_MANIFEST.json")) + need(manifest.get("version")=="1.5.5-rc1" and len(manifest.get("files",[]))==26, + "RC1_PACKAGE_VERSION_MANIFEST") + for f in manifest["files"]: + relative=f["file"] + need(PurePosixPath(relative).as_posix()==relative and + ".." not in PurePosixPath(relative).parts,"RC1_PACKAGE_PATH") + raw=z.read(CANDIDATE_PREFIX+relative) + need(len(raw)==f["bytes"] and sha(raw)==f["sha256"].lower(), + "RC1_PACKAGE_FILE_HASH") + z.extractall(extract) + src=extract/CANDIDATE_PREFIX.rstrip("/") + need(src.is_dir(),"CANDIDATE_EXTRACTION") + return src + +def seed_real_protected_root(stable_zip:Path,user,module): + # Pre-checked empty paths. The runner is disposable and this setup never + # alters a human workstation or another project. No service is started. + root=Path("/usr/lib/directinternetmethod") + root.mkdir(mode=0o755) + baseline={} + with zipfile.ZipFile(stable_zip) as z: + archive_verify(z,STABLE_PREFIX,24) + items=list(module.STATIC_ROOT.items()) + items.extend((row[2],row[3]) for row in module.FILES if row[3] is not None) + need(len(items)==11,"R528_BASELINE_FILES_CHANGED") + for path,want in items: + member=STABLE_PREFIX+MAPPING[Path(path).name] + raw=z.read(member) + need(sha(raw)==want.lower(),"PUBLISHED_BASELINE_MISMATCH_"+Path(path).name) + dest=Path("/")/path + need(not dest.exists() and not dest.is_symlink(), + "BASELINE_TARGET_ALREADY_EXISTS_"+dest.name) + dest.parent.mkdir(parents=True,exist_ok=True) + dest.write_bytes(raw) + mode=0o755 if dest.suffix==".sh" or dest.name in ("ctrld","nfqws","selective_dns.py") else 0o644 + dest.chmod(mode) + need(dest.stat().st_uid==0 and dest.stat().st_gid==0, + "REAL_NATIVE_ROOT_OWNERSHIP") + baseline[str(dest)]=sha(raw) + base=Path("/var/lib/directinternetmethod") + base.mkdir(mode=0o755) + meta=base/"install.json" + before={"schema":1,"version":"1.5.2","user":"runner","uid":user.pw_uid,"home":user.pw_dir} + meta_bytes=(json.dumps(before,sort_keys=True)+"\n").encode() + meta.write_bytes(meta_bytes) + meta.chmod(0o644) + uidfile=Path("/etc/directinternetmethod.uid") + need(not uidfile.exists() and not uidfile.is_symlink(),"UID_MARKER_PREEXISTS") + uidfile.write_text(str(user.pw_uid)+"\n") + uidfile.chmod(0o644) + baseline[str(meta)]=sha(meta_bytes) + baseline[str(uidfile)]=sha(uidfile.read_bytes()) + return baseline + +def run_native(): + stable,candidate,temp,user=prerequisites() + network_pre={ + "resolv":fsha(Path("/etc/resolv.conf")), + "resolvTarget":os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None, + "hosts":fsha(Path("/etc/hosts")), + } + m=module() + need(m.TARGET_VERSION=="1.5.5-rc1" and + m.CURRENT_BACKEND_VERSION=="1.5.2","NATIVE_PRODUCT_VERSION") + source=stage_exact_candidate(candidate,temp) + baseline=seed_real_protected_root(stable,user,m) + t=m.RootTransaction() # EXACT PRODUCT ABSOLUTE "/" ROOT, NOT PREFIX MOCK + need(t.native and t.root==Path("/"),"REAL_NATIVE_ROOT_MODE") + need(t.status()["status"]=="NO_PENDING_UPGRADE","ROOT_INITIAL_STATE") + need(t.stage(source)["status"]=="STAGED","ROOT_STAGE") + need(t.status()["status"]=="STAGED","ROOT_STAGE_STATUS") + for path,hash_expected in baseline.items(): + need(fsha(Path(path))==hash_expected,"ROOT_STAGE_MODIFIED_BASELINE") + pending=Path("/var/lib/directinternetmethod/upgrade-pending") + need(pending.is_dir() and pending.stat().st_uid==0 and + stat.S_IMODE(pending.stat().st_mode)==0o700,"ROOT_PENDING_OWNER_MODE") + need(t.apply()["status"]=="APPLIED","ROOT_APPLY") + need(t.status()["status"]=="APPLIED","ROOT_APPLY_STATUS") + for name,relative,target,old,new,mode in m.FILES: + file=Path("/")/target + need(file.is_file() and not file.is_symlink() and + fsha(file)==new.lower() and file.stat().st_uid==0 and + stat.S_IMODE(file.stat().st_mode)==mode, + "REAL_ROOT_UPGRADED_FILE_"+name) + current=json.loads(Path("/var/lib/directinternetmethod/install.json").read_text()) + need(current["version"]=="1.5.5-rc1","REAL_ROOT_METADATA_NOT_ACTIVATED") + try: + t.commit(temp/"NO_NATIVE_ACCEPTANCE_RECEIPT.json") + raise RuntimeError("R528_BAD_ACCEPTANCE_WAS_ALLOWED") + except RuntimeError as exc: + need("FAIL_CLOSED_NATIVE_ACCEPTANCE_MISSING" in str(exc), + "UNEXPECTED_ACCEPTANCE_REJECTION") + need(t.rollback()["status"]=="ROLLED_BACK","ROOT_ROLLBACK") + need(t.status()["status"]=="ROLLED_BACK","ROOT_ROLLBACK_STATE") + for path,want in baseline.items(): + need(fsha(Path(path))==want,"ROOT_ROLLBACK_BYTE_DRIFT_"+Path(path).name) + for name,relative,target,old,new,mode in m.FILES: + if old is None: + p=Path("/")/target + need(not p.exists() and not p.is_symlink(),"NEW_ROOT_FILE_LEFT_"+name) + for path in m.STATIC_ROOT: + need(fsha(Path("/")/path)==m.STATIC_ROOT[path].lower(), + "STATIC_BACKEND_CHANGED_"+Path(path).name) + need(fsha(Path("/etc/resolv.conf"))==network_pre["resolv"] and + fsha(Path("/etc/hosts"))==network_pre["hosts"] and + (os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None)==network_pre["resolvTarget"], + "NETWORK_BASELINE_CHANGED") + need(json.loads((BASE/"RELEASE.json").read_text())["version"]=="1.5.4", + "PUBLIC_RELEASE_METADATA_CHANGED") + report={ + "revision":"R528","status":"PASS_REAL_ROOT_STAGE_APPLY_ROLLBACK_ON_EPHEMERAL_GITHUB_VM", + "productionMachineTouched":False, + "nativeRoot":True,"effectiveUid":os.geteuid(),"protectedPreVersion":"1.5.2", + "protectedPostRollbackVersion":"1.5.2", + "protectedCandidateAppliedBeforeRollback":"1.5.5-rc1", + "publicStablePreserved":"v1.5.4","pinnedSource":CANDIDATE_SHA, + "pinnedPublishedStable":STABLE_SHA, + "rootOwnedFilesRestored":len(baseline), + "newRootTargetsRemoved":2, + "upgradeSlotRetainedForAudit":pending.exists(), + "metadataRollbackRestored":True, + "networkResolvAndHostsUnchanged":True, + "nativeEncryptedDnsAndGuiAcceptance":"NOT_TESTED", + } + print(json.dumps(report,sort_keys=True)) + return 0 + +if __name__=="__main__": + try:sys.exit(run_native()) + except Exception as e: + print(json.dumps({"revision":"R528","status":"FAIL_CLOSED","error":str(e)[:400]}),file=sys.stderr) + sys.exit(84) From 1deb33ee2d06c281c39fa6c56ec6438a529d9f0a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 10:26:10 +0330 Subject: [PATCH 007/188] test(ci): native Linux Root+User in-place update and rollback on hosted VM (R529) Rehydrate byte-pinned v1.5.4 installed user+Root fixtures, stage user/root, apply actual UID0 protected transaction, apply unprivileged user transaction, run exact production atomic INSTALL.json fragment, reject premature commit and verify two-sided version, private data, custom provider and full rollback. No physical host or network mutation. --- .github/workflows/ci.yml | 29 ++ tests/r529_ephemeral_root_user_transaction.py | 325 ++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 tests/r529_ephemeral_root_user_transaction.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44db8158..19c67c0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,6 +125,35 @@ jobs: # All absolute-root writes occur inside the disposable GH-hosted Ubuntu VM only. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r528_ephemeral_native_root_cli_contract.py + linux-root-user-integrated-ephemeral: + name: linux-root-user-integrated-ephemeral + needs: linux-native-root-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Build and independently pin candidate package + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' '45a29a65b3e34302ad8e0745324ec2a1c418bb43669f9b95b15963b02cdfcc49' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + - name: Download and independently pin official Stable 1.5.4 package + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R529 actual Root and unprivileged User transaction with rollback + shell: bash + run: | + set -euo pipefail + # Fresh GitHub VM only; no real host services, DNS, GUI or Tailscale touched. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r529_ephemeral_root_user_transaction.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r529_ephemeral_root_user_transaction.py b/tests/r529_ephemeral_root_user_transaction.py new file mode 100644 index 00000000..e6e556da --- /dev/null +++ b/tests/r529_ephemeral_root_user_transaction.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +"""R529: native Root + unprivileged User transaction across one disposable VM. + +Not a physical network/GUI acceptance. Use exact v1.5.4 published ZIP user and +protected Root code, exact 1.5.5-rc1 candidate ZIP, unmodified RootTransaction("/") +and real subprocess-driven user transaction / inline INSTALL.json production +fragment. Both transactions must rollback byte-for-byte. NO live network or +systemd, No Tailscale changes, no installation onto a user's physical computer. +All operations are refused outside a clean GitHub-hosted Ubuntu VM. +""" +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import pwd +import shutil +import stat +import subprocess +import sys +import zipfile + +BASE = Path(__file__).resolve().parents[1] +R528 = BASE / "tests/r528_ephemeral_native_root_cli_contract.py" +R526 = BASE / "tests/r526_installer_metadata_contract.py" +USER_TX_REL = "app/user_update_transaction.py" +HOME_REL = ".local/share/DirectInternetMethod" +USER_MUTABLE = ( + "settings.json", "strategy.txt", "scope.txt", "custom-hosts.txt", + "adult-enabled.txt", "adult-hosts.txt", "adult-hosts.meta.json", + "encrypted-dns-optin.txt", "selective-dns-optin.txt", +) +USER_MAP = ( + ("app/direct_internet_method.py", "direct_internet_method.py", 0o755), + ("app/router_gateway.py", "router_gateway.py", 0o755), + ("app/router_gateway_core.py", "router_gateway_core.py", 0o644), + ("app/direct-internet-method.svg", "direct-internet-method.svg", 0o644), + ("uninstall.sh", "uninstall.sh", 0o755), + ("app/user_uninstall_preserve.py", "user_uninstall_preserve.py", 0o755), + ("app/user_update_transaction.py", "user_update_transaction.py", 0o755), +) + +def require(ok, code): + if not ok: + raise RuntimeError("R529_FAIL_CLOSED_" + code) + +def digest(b): + return hashlib.sha256(b).hexdigest() + +def hash_file(p: Path): + h = hashlib.sha256() + with p.open("rb") as f: + for chunk in iter(lambda: f.read(1048576), b""): + h.update(chunk) + return h.hexdigest() + +def module(path: Path, label: str): + spec = importlib.util.spec_from_file_location(label, path) + require(spec is not None and spec.loader is not None, "IMPORT_" + label) + instance = importlib.util.module_from_spec(spec) + spec.loader.exec_module(instance) + return instance + +def ownership(path: Path, user, mode: int): + os.chown(path, user.pw_uid, user.pw_gid) + os.chmod(path, mode) + require(path.stat().st_uid == user.pw_uid, "USER_FILE_OWNER") + require(stat.S_IMODE(path.stat().st_mode) == mode, "USER_FILE_MODE") + +def write_owned(path: Path, body: bytes, user, mode: int = 0o600): + require(not path.exists() and not path.is_symlink(), "UNKNOWN_USER_TARGET_" + path.name) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(body) + ownership(path, user, mode) + +def require_clean_home(user): + home = Path(user.pw_dir) + require(home == Path("/home/runner") and home.is_dir(), "RUNNER_HOME") + for rel in (HOME_REL, ".local/state/DirectInternetMethod/update-pending", + ".local/state/DirectInternetMethod"): + p = home / rel + require(not p.exists() and not p.is_symlink(), + "USER_PREEXISTING_STATE_" + p.name) + require(not (home / ".local/bin/direct-internet-method").exists(), "USER_LAUNCHER_PREEXISTS") + +def prepare_user_from_official(stable: Path, user): + home = Path(user.pw_dir) + app = home / HOME_REL + need_dirs = [ + home / ".local", home / ".local/share", app, + app / "router_gateway", + ] + for d in need_dirs: + if not d.exists(): + d.mkdir(mode=0o700) + ownership(d, user, 0o700) + require(d.is_dir() and not d.is_symlink(), "FOREIGN_DIR_" + d.name) + require(d.stat().st_uid == user.pw_uid or d == home, + "UNKNOWN_PARENT_OWNER_" + d.name) + original = {} + with zipfile.ZipFile(stable) as z: + pkg = "DirectInternetMethod_1.5.4_Linux_x86_64/" + for rel, installed, mode in USER_MAP: + raw = z.read(pkg + rel) + dest = app / installed + write_owned(dest, raw, user, mode) + original[str(dest)] = digest(raw) + vendor_provider = z.read(pkg + "app/router_gateway/providers.json") + custom = b'{"r529CustomProvider": true, "owner": "do-not-replace"}\n' + require(digest(custom) != digest(vendor_provider), "PROVIDER_IS_NOT_CUSTOM") + provider_path = app / "router_gateway/providers.json" + write_owned(provider_path, custom, user) + for name in USER_MUTABLE: + value = { + "settings.json": b'{"r529PrivateSettings":true}\n', + "strategy.txt": b"balanced\n", + "scope.txt": b"targeted\n", + "custom-hosts.txt": b"r529-private.example\n", + "adult-enabled.txt": b"0\n", + "adult-hosts.txt": b"r529-custom-adult-list\n", + "adult-hosts.meta.json": b'{"source":"private"}\n', + "encrypted-dns-optin.txt": b"0\n", + "selective-dns-optin.txt": b"0\n", + }[name] + write_owned(app / name, value, user) + record = { + "schema": 2, "product": "Direct Internet Method", + "version": "1.5.4", "platform": "linux-x86_64", + "protectedBackendVersion": "1.5.2", + "managedProviderSha256": digest(vendor_provider), + "managedCodeSha256": { + installed: original[str(app / installed)] + for _, installed, _ in USER_MAP + }, + "managedLaunchersSha256": {}, + "networkMutationOnInstall": False, + } + meta = app / "INSTALL.json" + old = (json.dumps(record, sort_keys=True, indent=2) + "\n").encode() + write_owned(meta, old, user) + original[str(meta)] = digest(old) + user_data = { + str(p): hash_file(p) + for p in [provider_path, *(app / n for n in USER_MUTABLE)] + } + return app, original, user_data + +def make_source_runner_owned(src: Path, user): + # The exact R528 archive extraction is from the verified SHA-pinned ZIP, + # into a disposable path; avoid broad filesystem mutations. + cur = src + while cur.name != "DIM_R528_CANDIDATE": + require(cur.parent != cur, "SOURCE_PATH_ESCAPES_TEMP") + cur = cur.parent + for item in sorted([cur, *cur.rglob("*")], key=lambda p: len(p.parts)): + require(not item.is_symlink(), "SOURCE_EXTRACT_SYMLINK") + os.chown(item, user.pw_uid, user.pw_gid) + require(src.stat().st_uid == user.pw_uid, "PINNED_SOURCE_USER_OWNERSHIP") + require(cur.stat().st_uid == user.pw_uid, "SOURCE_ROOT_USER_OWNERSHIP") + +def as_runner(user, program: Path, *args, stdin=None, ok=True): + require(program.is_file() and not program.is_symlink(), "UNSAFE_PROGRAM") + cmd = [ + "/usr/sbin/runuser", "-u", user.pw_name, "--", + "/usr/bin/env", "HOME=" + user.pw_dir, + "PYTHONDONTWRITEBYTECODE=1", "/usr/bin/python3", "-B", str(program), + *map(str,args) + ] + completed = subprocess.run( + cmd, input=stdin, text=True, + capture_output=True, timeout=30, check=False, + ) + if ok: + require(completed.returncode == 0, + "USER_COMMAND_FAILURE_" + completed.stderr[-500:].replace(" ", "_")) + else: + require(completed.returncode != 0 and + ("FAIL_CLOSED" in completed.stderr or "Traceback" in completed.stderr), + "USER_NEGATIVE_GUARD_NOT_ENFORCED") + return completed + +def as_runner_inline(user, code: str, target: Path, helper: Path): + cmd = [ + "/usr/sbin/runuser", "-u", user.pw_name, "--", + "/usr/bin/env", "HOME=" + user.pw_dir, + "PYTHONDONTWRITEBYTECODE=1", "/usr/bin/python3", "-B", "-", + str(target), str(helper) + ] + result = subprocess.run(cmd, input=code + "\n", text=True, + capture_output=True, timeout=30) + require(result.returncode == 0, "INLINE_METADATA_FAIL_" + result.stderr[-420:].replace(" ", "_")) + +def unchanged_records(files): + for name, expected in files.items(): + p=Path(name) + require(p.is_file() and hash_file(p)==expected, "USER_ORIGINAL_CHANGED_"+p.name) + +def execute(): + # R528's hard guard proves this is a fresh hosted GitHub VM, not a user + # machine. Production root and HOME must be absent before any writes. + old, candidate, temp, user = module(R528, "r529_r528_guard").prerequisites() + r528 = module(R528, "r529_r528_helpers") + r526 = module(R526, "r529_r526_metadata") + require_clean_home(user) + require(Path("/usr/sbin/runuser").is_file(), "RUNUSER_UNAVAILABLE") + before_network={ + p:hash_file(Path(p)) for p in ("/etc/resolv.conf", "/etc/hosts") + } + before_network_link=os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None + rootmod = r528.module() + extracted = r528.stage_exact_candidate(candidate, temp) + make_source_runner_owned(extracted, user) + # Byte-pinned user baseline from public v1.5.4 release, not fabricated + # executable code or old code hashes. User's private settings are seeded + # and checked independently from executable bytes. + app, old_user, private = prepare_user_from_official(old, user) + root_before = r528.seed_real_protected_root(old, user, rootmod) + tx_script = extracted / USER_TX_REL + pending = Path(user.pw_dir) / ".local/state/DirectInternetMethod/update-pending" + root_tx = rootmod.RootTransaction() + require(root_tx.native and root_tx.root == Path("/"), + "ROOT_TRANSACTION_IS_NOT_REAL_ABSOLUTE") + require(root_tx.status()["status"]=="NO_PENDING_UPGRADE", "ROOT_PRESTATE") + require(not pending.exists(), "USER_PRESTATE_PENDING") + # User-first staged transaction before any protected root mutation. + as_runner(user, tx_script, "--stage", "--source", extracted) + require((pending / "manifest.json").is_file(), "USER_PENDING_STAGE") + manifest=json.loads((pending / "manifest.json").read_text()) + require(manifest["version"]=="1.5.5-rc1" and + manifest["providerManaged"] is False and + manifest["providerSha"] is None, + "CUSTOM_USER_PROVIDER_OWNERSHIP") + unchanged_records(old_user) + unchanged_records(private) + # Protected absolute-root stage/apply with actual root-owned files. + require(root_tx.stage(extracted)["status"]=="STAGED", "ROOT_STAGE") + require(root_tx.apply()["status"]=="APPLIED", "ROOT_APPLY") + require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", "ROOT_NEW_METADATA") + # Real unprivileged User transaction CLI replaces managed code only. + as_runner(user, tx_script, "--apply", "--source", extracted) + for rel,row in manifest["managed"].items(): + p=app / rel + require(p.is_file() and hash_file(p)==row["sha"], "USER_MANAGED_APPLY_"+rel) + unchanged_records(private) + # This must NOT be mistaken for completion: metadata not yet activated. + rejected = as_runner(user, tx_script, "--commit", ok=False) + require("INSTALLED_VERSION_METADATA_NOT_VALID" in rejected.stderr, "METADATA_COMMIT_GUARD_FAILED") + require((pending/"manifest.json").is_file(), "PREMATURE_PENDING_REMOVAL") + unchanged_records(private) + # Only now run EXACT, unmodified install.sh Python fragment, on real + # unprivileged user identity against real upgraded root metadata. + installer=extracted/"install.sh" + require(installer.is_file() and not installer.is_symlink(), "INSTALLER_INVALID") + source=installer.read_text(encoding="utf8") + marker=r526.BEGIN + end=r526.END + require(source.count(marker)==1, "INSTALL_METADATA_ENTRY_ANCHOR") + tail=source.split(marker,1)[1] + require(tail.count(end)==1, "INSTALL_METADATA_TERMINATOR") + inline=tail.split(end,1)[0] + require(len(inline.splitlines())==64 and + 'transaction_helper.atomic_bytes(' in inline and + 'assert protected.get("version") == "1.5.5-rc1"' in inline, + "INSTALLER_METADATA_PROVENANCE_CHANGED") + as_runner_inline(user,inline,app/"INSTALL.json",tx_script) + record=json.loads((app/"INSTALL.json").read_text()) + require(record["version"]=="1.5.5-rc1" and + record["protectedBackendVersion"]=="1.5.5-rc1", + "TWO_SIDED_VERSION_METADATA") + require(record["managedProviderSha256"] is None and + record["preservedCustomProviderData"] is True and + record["updateMode"]=="IN_PLACE_SINGLE_INSTALL_DATA_PRESERVED", + "CUSTOM_PROVIDER_OR_UPDATER_METADATA") + require(stat.S_IMODE((app/"INSTALL.json").stat().st_mode)==0o600, + "USER_METADATA_ATOMIC_MODE") + as_runner(user, tx_script, "--status") + unchanged_records(private) + # No release acceptance is claimed; deliberately roll back BOTH sides. + # User rollback runs first and is verified before protected root rollback. + as_runner(user, tx_script, "--rollback") + require(not pending.exists(),"USER_PENDING_LEFT_AFTER_ROLLBACK") + unchanged_records(old_user) + unchanged_records(private) + require(root_tx.rollback()["status"]=="ROLLED_BACK", "ROOT_ROLLBACK") + for name,expected in root_before.items(): + require(hash_file(Path(name))==expected, "PROTECTED_ROOT_BASELINE_DRIFT_"+Path(name).name) + for _,_,dest,old_hash,_,_ in rootmod.FILES: + if old_hash is None: + require(not (Path("/")/dest).exists(),"NEW_PROTECTED_FILE_LEFT") + for name,expected in before_network.items(): + require(hash_file(Path(name))==expected, "NETWORK_MUTATED_"+name) + require((os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None)==before_network_link, + "RESOLV_CONF_SYMLINK_MUTATED") + require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.2", "ROOT_FINAL_OLD_VERSION") + require(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.4", "USER_FINAL_OLD_VERSION") + require(json.loads((BASE/"RELEASE.json").read_text())["version"]=="1.5.4", "PUBLIC_TAG_AUTHORITY") + print(json.dumps({ + "revision":"R529", "status":"PASS_HOSTED_ROOT_USER_ACTUAL_TRANSACTION", + "productionMachineTouched":False,"hostedRunner":True, + "publicStable":"v1.5.4","candidate":"1.5.5-rc1", + "userTransaction":"STAGE_APPLY_INLINE_ATOMIC_METADATA_ROLLBACK", + "protectedRoot":"ABSOLUTE_ROOT_STAGE_APPLY_ROLLBACK", + "preVersions":"user1.5.4/root1.5.2", + "appliedVersion":"both1.5.5-rc1", + "postRollbackVersions":"user1.5.4/root1.5.2", + "oldUserCodeFilesVerified":len(old_user), + "protectedRootFilesVerified":len(root_before), + "mutablePrivateHashesVerified":len(private), + "userCustomProviderPreserved":True, + "userCommitBeforeMetadataRejected":True, + "userPendingSlotCleaned":True, + "protectedRootSlotRetainedForAudit":root_tx.pending.exists(), + "networkResolverFilesUnchanged":True, + "realPhysicalISPEncryptedDNS_GUI_Tailscale":"UNTESTED", + },sort_keys=True)) + return 0 + +if __name__=="__main__": + try:sys.exit(execute()) + except Exception as exc: + print(json.dumps({"revision":"R529","status":"FAIL_CLOSED", + "reason":str(exc)[:700]},sort_keys=True),file=sys.stderr) + sys.exit(85) From bdaffe4abd79517c1e3fdbe2b7eecf25f765a6ca Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:01:36 +0330 Subject: [PATCH 008/188] test(ci): exercise autonomous root-owned systemd rollback watchdog on disposable Linux VM (R532) Use separate arm and verify processes on GitHub-hosted Ubuntu: arm monotonic systemd timer before protected/unprivileged two-sided RC1 mutation, exit, and require timer to perform byte-exact Root/User rollback. Pin helpers and preserve live public v1.5.4, DNS, routes and user data. No physical or product runtime changes. --- .github/workflows/ci.yml | 33 +++ .../r532_ephemeral_root_watchdog_contract.py | 250 ++++++++++++++++++ tests/r532_root_watchdog_worker.py | 179 +++++++++++++ 3 files changed, 462 insertions(+) create mode 100644 tests/r532_ephemeral_root_watchdog_contract.py create mode 100644 tests/r532_root_watchdog_worker.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19c67c0a..23631052 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -154,6 +154,39 @@ jobs: # Fresh GitHub VM only; no real host services, DNS, GUI or Tailscale touched. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r529_ephemeral_root_user_transaction.py + linux-independent-root-watchdog-ephemeral: + name: linux-independent-root-watchdog-ephemeral + needs: linux-root-user-integrated-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Rebuild exact candidate Linux archive and pin identity + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' '45a29a65b3e34302ad8e0745324ec2a1c418bb43669f9b95b15963b02cdfcc49' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + - name: Download byte-pinned published Stable v1.5.4 archive + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: Arm independent root-owned systemd watchdog before two-sided mutation + shell: bash + run: | + set -euo pipefail + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r532_ephemeral_root_watchdog_contract.py arm + - name: Independent process verifies timed root and user rollback after arm exits + shell: bash + run: | + set -euo pipefail + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r532_ephemeral_root_watchdog_contract.py verify + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py new file mode 100644 index 00000000..4a2a0962 --- /dev/null +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""R532 one-shot hosted systemd root watchdog V&V. Execute 'arm', then 'verify'. + +Both invocations are separate CI steps on an ephemeral GitHub-hosted Ubuntu VM. +The first starts a detached systemd monotonic timer BEFORE any candidate file +mutation, leaves actual protected Root+unprivileged User 1.5.5-rc1 staged and +applied, and EXITs. The second waits for the independent timer and verifies +byte-exact restoration to 1.5.2 Root / 1.5.4 User, without running rollback. +All installed products on physical hosts and network services are untouched. +""" +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import pwd +import shutil +import stat +import subprocess +import sys +import time + +BASE = Path(__file__).resolve().parents[1] +R528 = BASE / "tests/r528_ephemeral_native_root_cli_contract.py" +R529 = BASE / "tests/r529_ephemeral_root_user_transaction.py" +R526 = BASE / "tests/r526_installer_metadata_contract.py" +WORKER_SOURCE = BASE / "tests/r532_root_watchdog_worker.py" +GUARD = Path("/run/dim-r532") +BASELINE = GUARD / "baseline.json" +ROOT_COPIED = GUARD / "root_tx.py" +USER_COPIED = GUARD / "user_tx.py" +WORKER_COPIED = GUARD / "worker.py" +WORKER_RETURN = GUARD / "timer-return.json" +UNIT = "dim-r532-watchdog" +TIMER = UNIT + ".timer" +SERVICE = UNIT + ".service" +ROOT_SHA = "4ce00525af4773fe25cb7e3e5edfd661b6a30c6b677964e12f24bca6650f328d" +USER_SHA = "a909bbb12b78875a9438c756ae2dcaff05e0df2ebb5551e97544d2b20c46346f" + +def sha(p:Path)->str: + h=hashlib.sha256() + with p.open("rb") as f: + for part in iter(lambda:f.read(1048576),b""):h.update(part) + return h.hexdigest() + +def need(ok,why): + if not ok: + raise RuntimeError("R532_HOSTED_FAIL_CLOSED_"+why) + +def mod(path,name): + spec=importlib.util.spec_from_file_location(name,path) + need(spec is not None and spec.loader is not None,"IMPORT_"+name) + x=importlib.util.module_from_spec(spec) + spec.loader.exec_module(x) + return x + +def run(cmd,secs=15): + v=subprocess.run(cmd,capture_output=True,text=True,timeout=secs,check=False) + need(v.returncode==0,"SYSTEMD_CMD_"+v.stderr.strip()[-200:].replace(" ","_")) + return v.stdout.strip() + +def ensure_guard_absent(): + need(not GUARD.exists() and not GUARD.is_symlink(),"UNOWNED_OR_PREEXISTING_GUARD") + for u in (TIMER,SERVICE): + test=subprocess.run(["/usr/bin/systemctl","show",u,"-p","LoadState","--value"], + capture_output=True,text=True,timeout=5) + need(test.stdout.strip() in ("not-found",""),"FOREIGN_SYSTEMD_UNIT_"+u) + +def verify_host(): + need(sys.platform.startswith("linux") and os.geteuid()==0,"REAL_ROOT_GITHUB_VM_REQUIRED") + need(os.environ.get("CI")=="true" and os.environ.get("GITHUB_ACTIONS")=="true", + "GITHUB_CI_REQUIRED") + need(os.environ.get("RUNNER_ENVIRONMENT")=="github-hosted" and + os.environ.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod", + "EPHEMERAL_REPO_RUNNER_REQUIRED") + need(Path(os.environ.get("GITHUB_WORKSPACE","")).resolve()==BASE.resolve(),"EXACT_WORKSPACE") + need(Path(os.environ.get("RUNNER_TEMP","/does-not-exist")).is_dir(),"RUNNER_TEMP_REQUIRED") + need(pwd.getpwnam("runner").pw_dir=="/home/runner","TEST_RUNNER_USER") + need(Path("/proc/1/comm").read_text().strip()=="systemd","INDEPENDENT_SYSTEMD_PID1") + need(WORKER_SOURCE.is_file() and not WORKER_SOURCE.is_symlink(),"GUARD_SOURCE_MISSING") + +def own_root_copy(src:Path,dest:Path,mode:int): + need(src.is_file() and not src.is_symlink(),"COPIED_SOURCE_UNSAFE") + need(not dest.exists() and not dest.is_symlink(),"COPIED_TARGET_EXISTS") + shutil.copyfile(src,dest) + os.chown(dest,0,0) + os.chmod(dest,mode) + need(dest.stat().st_uid==0 and stat.S_IMODE(dest.stat().st_mode)==mode, + "COPIED_ROOT_FILE_AUTHORITY") + need(sha(src)==sha(dest),"COPY_SHA_CHANGED") + +def save_manifest(doc): + raw=(json.dumps(doc,indent=2,sort_keys=True)+"\n").encode() + fd=os.open(BASELINE,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600) + with os.fdopen(fd,"wb") as out: + os.fchmod(out.fileno(),0o600) + out.write(raw);out.flush();os.fsync(out.fileno()) + need(BASELINE.stat().st_uid==0 and stat.S_IMODE(BASELINE.stat().st_mode)==0o600, + "BASELINE_ROOT_OWNERSHIP") + +def prepare_guard(source,user,root_hashes,user_hashes,mutables): + need(sha(source/"system/protected_upgrade_transaction.py")==ROOT_SHA, + "CANDIDATE_ROOT_PIN") + need(sha(source/"app/user_update_transaction.py")==USER_SHA, + "CANDIDATE_USER_PIN") + GUARD.mkdir(mode=0o711) + os.chown(GUARD,0,0) + os.chmod(GUARD,0o711) + own_root_copy(source/"system/protected_upgrade_transaction.py",ROOT_COPIED,0o500) + own_root_copy(source/"app/user_update_transaction.py",USER_COPIED,0o644) + own_root_copy(WORKER_SOURCE,WORKER_COPIED,0o500) + network={"/etc/resolv.conf":sha(Path("/etc/resolv.conf")), + "/etc/hosts":sha(Path("/etc/hosts"))} + absent=[ + "/usr/lib/directinternetmethod/encrypted_selective_dns.py", + "/usr/lib/directinternetmethod/encrypted_selective_policy.py"] + need(all(not Path(a).exists() for a in absent),"NEW_PROTECTED_ROOT_NOT_ABSENT") + doc={ + "schema":1,"revision":"R532","host":os.uname().nodename, + "productionMachineTouched":False, + "ownerUser":"runner","ownerUid":user.pw_uid, + "rootHelperSHA256":ROOT_SHA,"userHelperSHA256":USER_SHA, + "workerSHA256":sha(WORKER_SOURCE), + "expectedRootVersion":"1.5.2","expectedUserVersion":"1.5.4", + "originalAbsentProtected":absent, + "protected":root_hashes,"userCode":user_hashes,"userMutable":mutables, + "network":network, + "resolvLink":os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None + } + save_manifest(doc) + return doc + +def exact_inline(r526,src): + raw=(src/"install.sh").read_text(encoding="utf8") + marker=r526.BEGIN + need(raw.count(marker)==1,"INSTALLED_INLINE_ENTRY_COUNT") + tail=raw.split(marker,1)[1] + need(tail.count(r526.END)==1,"INSTALLED_INLINE_TERMINATOR") + code=tail.split(r526.END,1)[0] + need('transaction_helper.atomic_bytes(' in code and len(code.splitlines())==64, + "INSTALLED_METADATA_FRAGMENT_DRIFT") + return code + +def arm(): + ensure_guard_absent() + r528=mod(R528,"r532_seed_root") + r529=mod(R529,"r532_seed_user") + r526=mod(R526,"r532_inline") + stable,candidate,temp,user=r528.prerequisites() + r529.require_clean_home(user) + need(Path("/usr/sbin/runuser").is_file(),"RUNUSER_REQUIRED") + extracted=r528.stage_exact_candidate(candidate,temp) + r529.make_source_runner_owned(extracted,user) + app,old_user,private=r529.prepare_user_from_official(stable,user) + module=r528.module() + root_before=r528.seed_real_protected_root(stable,user,module) + doc=prepare_guard(extracted,user,root_before,old_user,private) + run(["/usr/bin/systemd-run","--quiet","--unit="+UNIT, + "--on-active=35s","--timer-property=AccuracySec=1s", + "/usr/bin/python3","-B",str(WORKER_COPIED)]) + timer_state=run(["/usr/bin/systemctl","show",TIMER,"-p","ActiveState","--value"]) + need(timer_state=="active","INDEPENDENT_MONOTONIC_TIMER_NOT_ARMED") + trigger=run(["/usr/bin/systemctl","show",TIMER,"-p","Triggers","--value"]) + need(SERVICE in trigger,"TIMER_NOT_CONNECTED_TO_OWNED_WORKER") + # Everything below occurs AFTER detached root watchdog is independently armed. + user_script=extracted/"app/user_update_transaction.py" + root_tx=module.RootTransaction() + need(root_tx.native and root_tx.status()["status"]=="NO_PENDING_UPGRADE", + "PRE_MUTATION_ROOT_DIRTY") + need(not Path("/home/runner/.local/state/DirectInternetMethod/update-pending").exists(), + "PRE_MUTATION_USER_DIRTY") + r529.as_runner(user,user_script,"--stage","--source",extracted) + need(root_tx.stage(extracted)["status"]=="STAGED","ROOT_STAGE") + need(root_tx.apply()["status"]=="APPLIED","ROOT_APPLY") + r529.as_runner(user,user_script,"--apply","--source",extracted) + r529.as_runner_inline(user,exact_inline(r526,extracted), + app/"INSTALL.json",user_script) + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1", + "NEW_USER_METADATA") + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + "NEW_ROOT_METADATA") + need(root_tx.status()["status"]=="APPLIED","ROOT_APPLIED_PENDING") + need((Path("/home/runner/.local/state/DirectInternetMethod/update-pending/manifest.json")).is_file(), + "USER_SLOT_MISSING") + # Leave both sides unapplied to no other action; arm process exits now. + # The next CI step is a DIFFERENT process; only systemd timer may rollback. + print("PASS_R532_WATCHDOG_ARMED_BEFORE_MUTATION_THEN_MAIN_EXIT") + print("WORKER_ROOT_OWNED_SHA",doc["workerSHA256"]) + print("TIMER_UNIT",TIMER,"SERVICE_UNIT",SERVICE) + print("PRE_TIMER_VERSIONS", "root=1.5.5-rc1","user=1.5.5-rc1") + +def verify(): + need(GUARD.is_dir() and GUARD.stat().st_uid==0,"EXPECTED_GUARD_NOT_FOUND") + need(BASELINE.is_file() and BASELINE.stat().st_uid==0,"ROOT_BASELINE_MISSING") + check=json.loads(BASELINE.read_text()) + need(check["revision"]=="R532" and check["host"]==os.uname().nodename, + "BASELINE_DIFFERENT_RUN") + need(check["workerSHA256"]==sha(WORKER_COPIED),"WORKER_SOURCE_DRIFT") + need(sha(ROOT_COPIED)==ROOT_SHA and sha(USER_COPIED)==USER_SHA, + "OWNER_GUARD_COPY_SHA_DRIFT") + start=time.monotonic() + while not WORKER_RETURN.exists() and time.monotonic()-start<85: + time.sleep(1) + need(WORKER_RETURN.is_file() and not WORKER_RETURN.is_symlink(), + "SYSTEMD_TIMER_DID_NOT_ACTIVATE") + receipt=json.loads(WORKER_RETURN.read_text()) + need(receipt["status"]=="PASS_ROOT_USER_BYTE_EXACT_AUTONOMOUS_ROLLBACK", + "TIMER_WORKER_NOT_SUCCESS:"+str(receipt.get("failure","NONE"))) + need(receipt.get("actualEffectiveUid")==0 and receipt.get("recoveryAttempted"), + "ROOT_TIMER_EXECUTION_UNPROVEN") + need(receipt["userRollback"]=="PASS" and receipt["protectedRootRollback"]=="PASS", + "TWO_SIDED_ROLLBACK_MISSING") + need(receipt["restoredFiles"]["userMutable"]>=8, + "USER_MUTABLE_VERIFICATION_INSUFFICIENT") + need(receipt["restoredFiles"]["protected"]>=11, + "ROOT_BASELINE_VERIFICATION_INSUFFICIENT") + require_versions={ + "protected":json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"], + "user":json.loads(Path("/home/runner/.local/share/DirectInternetMethod/INSTALL.json").read_text())["version"], + } + need(require_versions=={"protected":"1.5.2","user":"1.5.4"}, + "LIVE_FINAL_VERSION_DRIFT") + for cls in ("protected","userCode","userMutable","network"): + for name,want in check[cls].items(): + need(sha(Path(name))==want,"POST_TIMER_SHA_DRIFT_"+cls) + need(not Path("/home/runner/.local/state/DirectInternetMethod/update-pending").exists(), + "USER_SLOT_NOT_CLEAN") + need(not Path("/etc/systemd/system/directinternetmethod-start.service").exists(), + "UNKNOWN_GLOBAL_PRODUCTION_SERVICE") + need(not Path("/run/directinternetmethod-encrypted").exists(), + "UNEXPECTED_ENCRYPTED_DNS_SESSION") + print("PASS_R532_INDEPENDENT_SYSTEMD_ROOT_TIMER_RESTORED_BOTH_SIDES") + print("WORKER_RECEIPT_SHA256",sha(WORKER_RETURN)) + print("RESTORED_SHA_GROUPS",json.dumps(receipt["restoredFiles"],sort_keys=True)) + print("PREVIOUS_PUBLIC_STABLE_UNCHANGED_v1.5.4") + +def main(): + need(len(sys.argv)==2 and sys.argv[1] in ("arm","verify"),"CLI_ACTION") + verify_host() + if sys.argv[1]=="arm":arm() + else:verify() + +if __name__=="__main__": + try: + main() + except BaseException as e: + print("R532_HOSTED_TIMER_FAIL_CLOSED",type(e).__name__,str(e)[:450],file=sys.stderr) + sys.exit(89) diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py new file mode 100644 index 00000000..2fcdf01a --- /dev/null +++ b/tests/r532_root_watchdog_worker.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +"""R532 independent root-owned timer worker on disposable GitHub Ubuntu VM. + +Copied to /run/dim-r532 with root ownership BEFORE candidate mutation. +Executes only when systemd transient one-shot monotonic timer fires. +Never changes network, resolver, service, Tailscale or user preferences. +Refuses foreign files; product user/root transactions do their own checks. +NOT A PRODUCTION WATCHDOG; only tests the release gate in an ephemeral VM. +""" +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import pwd +import stat +import subprocess +import sys +import time + +GUARD = Path("/run/dim-r532") +MANIFEST = GUARD / "baseline.json" +RESULT = GUARD / "timer-return.json" +ROOT_HELPER = GUARD / "root_tx.py" +USER_HELPER = GUARD / "user_tx.py" +PIN_ROOT = "4ce00525af4773fe25cb7e3e5edfd661b6a30c6b677964e12f24bca6650f328d" +PIN_USER = "a909bbb12b78875a9438c756ae2dcaff05e0df2ebb5551e97544d2b20c46346f" + +def digest(path:Path): + h=hashlib.sha256() + with path.open("rb") as inp: + for p in iter(lambda:inp.read(1048576),b""): + h.update(p) + return h.hexdigest() + +def require(ok,where): + if not ok: + raise RuntimeError("R532_GUARD_FAIL_CLOSED_"+where) + +def valid_root_file(p:Path,mode=None): + require(p.is_file() and not p.is_symlink(),"FILE_TYPE_"+p.name) + s=p.stat() + require(s.st_uid==0 and s.st_gid==0 and stat.S_ISREG(s.st_mode), + "ROOT_OWNER_"+p.name) + if mode is not None: + require(stat.S_IMODE(s.st_mode)==mode,"MODE_"+p.name) + +def write_once(outcome:dict): + raw=(json.dumps(outcome,sort_keys=True,indent=2)+"\n").encode() + fd=os.open(RESULT,os.O_CREAT|os.O_EXCL|os.O_WRONLY|os.O_NOFOLLOW,0o600) + with os.fdopen(fd,"wb") as handle: + os.fchmod(handle.fileno(),0o600) + handle.write(raw);handle.flush();os.fsync(handle.fileno()) + directory=os.open(GUARD,os.O_RDONLY|os.O_DIRECTORY) + try:os.fsync(directory) + finally:os.close(directory) + +def module(path:Path): + spec=importlib.util.spec_from_file_location("dim_r532_copied_protected_root",path) + require(spec is not None and spec.loader is not None,"PINNED_ROOT_IMPORT") + obj=importlib.util.module_from_spec(spec) + spec.loader.exec_module(obj) + return obj + +def verify_receipt(doc): + require(doc.get("revision")=="R532" and doc.get("schema")==1,"BASELINE_SCHEMA") + require(doc.get("host")==os.uname().nodename,"HOST_CHANGED") + require(doc.get("ownerUser")=="runner","UNEXPECTED_USER") + u=pwd.getpwnam("runner") + require(doc.get("ownerUid")==u.pw_uid and u.pw_dir=="/home/runner","USER_IDENTITY") + require(doc.get("productionMachineTouched") is False,"SANDBOX_AUTHORITY") + require(doc.get("rootHelperSHA256")==PIN_ROOT and + doc.get("userHelperSHA256")==PIN_USER,"SOURCE_PIN_DECLARATION") + require(doc.get("expectedRootVersion")=="1.5.2" and + doc.get("expectedUserVersion")=="1.5.4","EXPECTED_VERSIONS") + for key in ("protected","userCode","userMutable","network"): + require(isinstance(doc.get(key),dict) and doc[key],"MISSING_BASELINE_"+key) + for name,hashed in doc[key].items(): + path=Path(name) + require(path.is_absolute() and + (name.startswith("/home/runner/.local/share/DirectInternetMethod/") or + name.startswith("/usr/lib/directinternetmethod/") or + name in ("/var/lib/directinternetmethod/install.json", + "/etc/directinternetmethod.uid", + "/etc/resolv.conf","/etc/hosts")), + "BASELINE_PATH_OUTSIDE_ALLOWLIST") + require(isinstance(hashed,str) and len(hashed)==64,"BAD_BASELINE_SHA") + require(doc.get("resolvLink")==( + os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None), + "SYSTEM_RESOLVER_LINK_CHANGED") + return u + +def main(): + started=time.monotonic() + outcome={"revision":"R532","status":"FAIL_UNRESOLVED", + "mode":"SYSTEMD_TRANSIENT_INDEPENDENT_MONOTONIC_TIMER", + "utc":time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime()), + "actualEffectiveUid":os.geteuid(), + "productionMachineTouched":False, + "mutatedNetworkOrTailscale":False, + "recoveryAttempted":False} + try: + require(os.geteuid()==0 and sys.platform.startswith("linux"),"ROOT_LINUX") + require(GUARD.is_dir() and not GUARD.is_symlink() and + GUARD.stat().st_uid==0 and stat.S_IMODE(GUARD.stat().st_mode)==0o711, + "INDEPENDENT_ROOT_DIR") + require(not RESULT.exists() and not RESULT.is_symlink(),"ONCE_ONLY") + for p,m in ((Path(__file__),0o500),(MANIFEST,0o600),(ROOT_HELPER,0o500), + (USER_HELPER,0o644)): + valid_root_file(p,m) + require(digest(ROOT_HELPER)==PIN_ROOT and digest(USER_HELPER)==PIN_USER, + "ROOT_OWNED_PINNED_TX_CHANGED") + baseline=json.loads(MANIFEST.read_text(encoding="utf8")) + owner=verify_receipt(baseline) + src=module(ROOT_HELPER) + txn=src.RootTransaction() + require(txn.native and txn.root==Path("/"),"NATIVE_ROOT_LOCK_AUTHORITY") + pending=Path("/home/runner/.local/state/DirectInternetMethod/update-pending") + root_phase=txn.status().get("status") + outcome["preRollbackRootPhase"]=root_phase + require(root_phase in ("STAGED","APPLIED","DEGRADED","ROLLED_BACK"), + "ROOT_PENDING_MISSING_OR_ALREADY_COMMITTED") + require(pending.is_dir() and not pending.is_symlink(),"USER_PENDING_NOT_FOUND") + outcome["recoveryAttempted"]=True + # Rollback user-owned files first while original protected baseline + # still has an independently protected rollback slot. + user=subprocess.run( + ["/usr/sbin/runuser","-u",owner.pw_name,"--","/usr/bin/env", + "HOME=/home/runner","PYTHONDONTWRITEBYTECODE=1","/usr/bin/python3","-B", + str(USER_HELPER),"--rollback"], + capture_output=True,text=True,timeout=35,check=False) + require(user.returncode==0, + "USER_ROLLBACK_"+user.stderr[-180:].replace(" ","_")) + outcome["userRollback"]="PASS" + if root_phase in ("STAGED","APPLIED","DEGRADED"): + new=txn.rollback() + require(new.get("status")=="ROLLED_BACK","ROOT_ROLLBACK_NOT_ACCEPTED") + require(txn.status().get("status")=="ROLLED_BACK", + "ROOT_PENDING_PHASE_NOT_ROLLED_BACK") + outcome["protectedRootRollback"]="PASS" + require(not pending.exists(),"USER_SLOT_NOT_CLEANED") + require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.2", + "ROOT_VERSION_NOT_RESTORED") + require(json.loads(Path("/home/runner/.local/share/DirectInternetMethod/INSTALL.json").read_text())["version"]=="1.5.4", + "USER_VERSION_NOT_RESTORED") + for kind in ("protected","userCode","userMutable","network"): + for name,baseline_sha in baseline[kind].items(): + target=Path(name) + require(target.is_file() and not target.is_symlink() or name=="/etc/resolv.conf" and target.is_symlink(), + "RESTORED_TYPE_"+target.name) + require(digest(target)==baseline_sha, + "RESTORED_SHA_"+kind+"_"+target.name) + for name in baseline.get("originalAbsentProtected",[]): + file=Path(name) + require(not file.exists() and not file.is_symlink(),"NEW_ROOT_BINARY_REMAINS") + require(not Path("/home/runner/.local/share/DirectInternetMethod/directmethod/state.json").exists(), + "OWNED_ENGINE_STATE_NOT_ABSENT") + outcome["restoredFiles"]={ + name:len(baseline[name]) for name in ("protected","userCode","userMutable","network")} + outcome["status"]="PASS_ROOT_USER_BYTE_EXACT_AUTONOMOUS_ROLLBACK" + except BaseException as ex: + outcome["failure"]=type(ex).__name__+":"+str(ex)[:350] + outcome["status"]="DEGRADED_UNRESOLVED" + finally: + outcome["durationSec"]=round(time.monotonic()-started,3) + try: + write_once(outcome) + except BaseException as ex: + print("R532_RECEIPT_FAILED",type(ex).__name__,str(ex)[:220],file=sys.stderr) + outcome["receiptWriteFailed"]=True + print(json.dumps(outcome,sort_keys=True)) + if outcome.get("receiptWriteFailed"): + return 91 + return 0 if outcome["status"]=="PASS_ROOT_USER_BYTE_EXACT_AUTONOMOUS_ROLLBACK" else 90 + +if __name__=="__main__": + sys.exit(main()) From b3aebf182d58eac07292d1bdc6da6656d0da809d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:50:08 +0330 Subject: [PATCH 009/188] feat(test): isolated Windows 1.5.5-rc1 Inno candidate and hosted upgrade rollback (R535) Build new private Windows WPF/Service/installer with unchanged AppId and explicit RC1 version. Keep published v1.5.4 root release metadata authoritative. Extend version-aware negative contracts without relaxing stable acceptance. Add disposable Windows 2025 native Stable->RC1->Stable upgrade and data-preserving rollback validation. No public assets or installed hosts changed. --- .github/workflows/ci.yml | 36 + tests/dotnet10_target_contract.py | 14 +- tests/r535_windows_candidate_hosted_build.ps1 | 59 + tests/r535_windows_native_rollback.ps1 | 108 + tests/release_metadata_audit.py | 38 +- tests/windows_contract_audit.py | 12 +- windows/RELEASE.json | 295 +- windows/gui/DirectInternetMethod.csproj | 2 +- windows/gui/MainWindow.xaml.cs | 1384 +-- windows/installer/DirectInternetMethod.iss | 5 +- windows/manifest.json | 9572 ++++++++--------- .../DirectInternetMethod.Service.csproj | 28 +- 12 files changed, 5900 insertions(+), 5653 deletions(-) create mode 100644 tests/r535_windows_candidate_hosted_build.ps1 create mode 100644 tests/r535_windows_native_rollback.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23631052..254368fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -313,6 +313,42 @@ jobs: python -B tests/unsigned_public_release_contract_154.py --fixtures if($LASTEXITCODE -ne 0){throw 'R492_PUBLIC_V154_NEGATIVE_FIXTURES_FAILED'} + windows-private-r535-native: + name: windows-private-r535-native + if: github.event_name == 'push' && github.ref == 'refs/heads/feature/r535-windows-155rc1' + needs: windows-source + runs-on: windows-2025 + timeout-minutes: 35 + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6 + with: + dotnet-version: '10.0.x' + - name: R535 build exact private 1.5.5-rc1 Windows GUI service Inno installer + shell: pwsh + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/r535_windows_candidate_hosted_build.ps1 + if($LASTEXITCODE -ne 0){throw 'R535_PRIVATE_WINDOWS_NATIVE_BUILD_FAILED'} + - name: R535 hosted Windows v1.5.4 to RC1 in-place upgrade preserve and rollback + shell: pwsh + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/r535_windows_native_rollback.ps1 + if($LASTEXITCODE -ne 0){throw 'R535_NATIVE_INSTALL_ROLLBACK_FAILED'} + - name: Preserve R535 private verification receipts + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: r535-windows-rc1-hosted-${{ github.run_id }} + path: | + ${{ runner.temp }}/DIM_R535_NATIVE/BUILD_R535.json + ${{ runner.temp }}/DIM_R535_NATIVE/NATIVE_R535.json + if-no-files-found: warn + retention-days: 14 + windows-release-contract: name: windows-release-contract-unsigned if: startsWith(github.ref, 'refs/tags/v') diff --git a/tests/dotnet10_target_contract.py b/tests/dotnet10_target_contract.py index fafea942..c2b9a95a 100644 --- a/tests/dotnet10_target_contract.py +++ b/tests/dotnet10_target_contract.py @@ -4,12 +4,22 @@ Existing public v1.5.3 release remains immutable. Source-only upgrade is not a new release. """ from pathlib import Path +import json from xml.etree import ElementTree as ET ROOT=Path(__file__).resolve().parents[1] TARGET="net10.0-windows" PROJECTS=("windows/gui/DirectInternetMethod.csproj","windows/service/DirectInternetMethod.Service.csproj") CI=ROOT/".github/workflows/ci.yml" +REL=json.loads((ROOT/"windows/RELEASE.json").read_text(encoding="utf-8-sig")) +CURRENT=REL.get("version") +if CURRENT=="1.5.4": + assert REL.get("status")=="CANDIDATE_V154_UNPUBLISHED" and REL.get("previousAccepted",{}).get("version")=="1.5.3" +elif CURRENT=="1.5.5-rc1": + assert REL.get("status")=="PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN" + assert REL.get("previousAccepted",{}).get("version")=="1.5.4" and REL.get("previousAccepted",{}).get("tag")=="v1.5.4" +else: + raise AssertionError("WINDOWS_CANDIDATE_VERSION_UNAUTHORIZED") def inspect(raw,project): tree=ET.fromstring(raw) @@ -17,7 +27,7 @@ def inspect(raw,project): version=tree.find(".//Version") output=tree.find(".//OutputType") assert target is not None and target.text==TARGET,(project,"TargetFramework") - assert version is not None and version.text=="1.5.4",(project,"product_version_guard") + assert version is not None and version.text==CURRENT,(project,"product_version_guard") assert output is not None and output.text=="WinExe",(project,"runtime_subsystem") if "/gui/" in project: wpf=tree.find(".//UseWPF") @@ -35,7 +45,7 @@ def negative_first(): try:inspect(data.replace(b"net10.0-windows",wrong),project) except AssertionError:pass else:raise AssertionError(("FAIL_OPEN_VERSION_DRIFT",project,wrong)) - try:inspect(data.replace(b"1.5.4",b"1.5.3"),project) + try:inspect(data.replace((""+CURRENT+"").encode(),b"1.5.3"),project) except AssertionError:pass else:raise AssertionError(("FAIL_OPEN_PRODUCT_VERSION_DRIFT",project)) diff --git a/tests/r535_windows_candidate_hosted_build.ps1 b/tests/r535_windows_candidate_hosted_build.ps1 new file mode 100644 index 00000000..400655b0 --- /dev/null +++ b/tests/r535_windows_candidate_hosted_build.ps1 @@ -0,0 +1,59 @@ +[CmdletBinding()] +param() +$ErrorActionPreference='Stop' +Set-StrictMode -Version Latest +$Root=$env:GITHUB_WORKSPACE +if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_GITHUB_HOST_ONLY'} +if($env:GITHUB_REF -cne 'refs/heads/feature/r535-windows-155rc1' -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} +$r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json +if($r.version -cne '1.5.5-rc1' -or $r.status -cne 'PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R535_RC1_DECLARATION'} +$qa=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' +$gstage=Join-Path $qa 'gui' +$sstage=Join-Path $qa 'service' +$gt=Join-Path $Root 'windows\app\DirectInternetMethod.exe' +$st=Join-Path $Root 'windows\app\DirectInternetMethod.Service.exe' +$installer=Join-Path $Root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' +foreach($p in @($qa,$gt,$st,$installer)){if(Test-Path $p){throw ('R535_UNSAFE_RERUN_'+$p)}} +New-Item -ItemType Directory -Path $qa|Out-Null +$iscc=Join-Path ([Environment]::GetFolderPath('ProgramFilesX86')) 'Inno Setup 6\ISCC.exe' +if(!(Test-Path $iscc)){$iscc=Join-Path $env:ProgramFiles 'Inno Setup 6\ISCC.exe'} +if(!(Test-Path $iscc)){throw 'R535_GITHUB_IMAGE_INNO_COMPILER_ABSENT'} +Set-Location $Root +& pwsh -NoProfile -NonInteractive -File tests/prepare_powershell_runtime.ps1 +if($LASTEXITCODE -ne 0){throw 'PWSH_BUNDLE_PREP_FAILED'} +if((Get-FileHash (Join-Path $Root 'windows\vendor\pwsh\pwsh.exe') -Algorithm SHA256).Hash -cne 'BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285'){throw 'PWSH_BUNDLE_SHA'} +& dotnet publish windows/gui/DirectInternetMethod.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -o $gstage +if($LASTEXITCODE -ne 0){throw 'GUI_PUBLISH_FAILED'} +& dotnet publish windows/service/DirectInternetMethod.Service.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -o $sstage +if($LASTEXITCODE -ne 0){throw 'SERVICE_PUBLISH_FAILED'} +$gb=Join-Path $gstage 'DirectInternetMethod.exe';$sb=Join-Path $sstage 'DirectInternetMethod.Service.exe' +if(!(Test-Path $gb) -or !(Test-Path $sb)){throw 'RC1_BINARIES_MISSING'} +foreach($name in @('Start-Direct.ps1','Stop-Direct.ps1','Recovery.ps1')){Copy-Item -LiteralPath (Join-Path $Root ('windows\app\'+$name)) -Destination (Join-Path $sstage $name)} +$test=Start-Process $sb -ArgumentList '--console-selftest' -Wait -PassThru -NoNewWindow +if($test.ExitCode -ne 0){throw ('SERVICE_SELFTEST_'+$test.ExitCode)} +Copy-Item $gb $gt +Copy-Item $sb $st +& python -B tests/build_windows_manifest.py +if($LASTEXITCODE -ne 0){throw 'MANIFEST_FAILED'} +& python -B tests/dotnet10_target_contract.py +if($LASTEXITCODE -ne 0){throw 'NET10_CONTRACT_FAILED'} +& python -B tests/windows_contract_audit.py | Out-Null +if($LASTEXITCODE -ne 0){throw 'WINDOWS_AUDIT_FAILED'} +$m=Get-Content (Join-Path $Root 'windows\manifest.json') -Raw|ConvertFrom-Json +if($m.version -cne '1.5.5-rc1' -or @($m.files).Count -ne 681){throw 'RC1_MANIFEST_IDENTITY'} +& $iscc /Qp (Join-Path $Root 'windows\installer\DirectInternetMethod.iss') +if($LASTEXITCODE -ne 0){throw 'INNO_COMPILATION_FAILED'} +if(!(Test-Path $installer) -or (Get-Item $installer).Length -lt 50000000){throw 'R535_INSTALLER_MISSING'} +if([string](Get-AuthenticodeSignature $installer).Status -ne 'NotSigned'){throw 'R535_SIGNATURE_POLICY_CHANGED'} +$report=[ordered]@{ + revision='R535';stage='BUILD';status='PASS_ISOLATED_HOSTED_RC1_BUILD' + head=$env:GITHUB_SHA;version='1.5.5-rc1';stable='v1.5.4' + manifestCount=@($m.files).Count;manifestSha=(Get-FileHash (Join-Path $Root 'windows\manifest.json') -Algorithm SHA256).Hash + guiSha=(Get-FileHash $gt -Algorithm SHA256).Hash + serviceSha=(Get-FileHash $st -Algorithm SHA256).Hash + installerSha=(Get-FileHash $installer -Algorithm SHA256).Hash + installerBytes=(Get-Item $installer).Length;unsigned=$true + installation='NOT_TESTED_YET';publicReleaseUnchanged=$true +} +[IO.File]::WriteAllText((Join-Path $qa 'BUILD_R535.json'),(($report|ConvertTo-Json -Depth 5)+[Environment]::NewLine),[Text.UTF8Encoding]::new($false)) +Write-Output ('PASS_R535_HOSTED_BUILD_SHA256='+$report.installerSha) diff --git a/tests/r535_windows_native_rollback.ps1 b/tests/r535_windows_native_rollback.ps1 new file mode 100644 index 00000000..8c5a22a7 --- /dev/null +++ b/tests/r535_windows_native_rollback.ps1 @@ -0,0 +1,108 @@ +[CmdletBinding()] +param() +$ErrorActionPreference='Stop' +Set-StrictMode -Version Latest +$Root=$env:GITHUB_WORKSPACE +$Dir=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' +$Build=Join-Path $Dir 'BUILD_R535.json' +$Result=Join-Path $Dir 'NATIVE_R535.json' +$Old=Join-Path $Dir 'DirectInternetMethod_1.5.4_Windows_Setup.exe' +$New=Join-Path $Root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' +$App=Join-Path $env:LOCALAPPDATA 'Programs\DirectInternetMethod' +$Priv=Join-Path $env:ProgramFiles 'DirectInternetMethod\Privileged' +$Config=Join-Path $env:ProgramData 'DirectInternetMethod\UserConfig\custom-hosts.txt' +$Provider=Join-Path $App 'app\router_gateway\providers.json' +$Evidence=Join-Path $App 'evidence\R535_KEEP.txt' +$Gui=Join-Path $App 'app\DirectInternetMethod.exe' +$Svc=Join-Path $Priv 'app\DirectInternetMethod.Service.exe' +$Key='HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1' +$PublicSha='FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133' +$PublicGui='BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F' +$PublicSvc='2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41' +if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_HOSTED_VM_ONLY'} +if($env:GITHUB_REF -cne 'refs/heads/feature/r535-windows-155rc1' -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} +$admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() +if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R535_ADMIN_TOKEN_REQUIRED'} +if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R535_UNSAFE_REPLAY_OR_MISSING_BUILD'} +$build=Get-Content $Build -Raw |ConvertFrom-Json +if($build.status -cne 'PASS_ISOLATED_HOSTED_RC1_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R535_BUILD_AUTHORITY'} +if((Get-FileHash $New -Algorithm SHA256).Hash -cne $build.installerSha){throw 'R535_RC1_INSTALLER_SHA'} +$report=[ordered]@{schema=1;revision='R535';status='FAIL_UNPROVEN';source=$env:GITHUB_SHA; + runner='github-hosted-windows-2025';privateInstallerSha=$build.installerSha; + publishedStableSha=$PublicSha;noReboot=$true;noPhysicalMachines=$true; + checks=@();errors=@()} +function Require([bool]$ok,[string]$name) { + if(!$ok){throw ('R535_FAIL_CLOSED_'+$name)} + $report.checks+=($name+':PASS') +} +function FileSha([string]$p){ (Get-FileHash -LiteralPath $p -Algorithm SHA256).Hash } +function Network { + [ordered]@{ + defaultRoutes=(@(Get-NetRoute -DestinationPrefix '0.0.0.0/0' | + Select-Object InterfaceIndex,NextHop,RouteMetric|Sort-Object InterfaceIndex,NextHop)|ConvertTo-Json -Depth 5 -Compress) + dns=(@(Get-DnsClientServerAddress -AddressFamily IPv4| + Select-Object InterfaceIndex,ServerAddresses|Sort-Object InterfaceIndex)|ConvertTo-Json -Depth 5 -Compress)} +} +function Install([string]$exe,[string]$step) { + $args=@('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART','/NORESTARTAPPLICATIONS','/CLOSEAPPLICATIONS','/SP-') + $p=Start-Process -FilePath $exe -ArgumentList $args -Wait -PassThru -NoNewWindow + Require ($p.ExitCode -eq 0) ($step+'_EXIT') +} +function Registered([string]$version) { + Require (Test-Path $Key) 'SINGLE_HKLM_APPID' + $d=Get-ItemProperty -LiteralPath $Key + Require ($d.DisplayVersion -ceq $version) 'EXACT_REGISTERED_VERSION' + Require ($d.InstallLocation.TrimEnd('\') -ieq $App.TrimEnd('\')) 'SINGLE_INSTALL_PATH' + Require (!(Test-Path ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1'))) 'NO_HKCU_DUPLICATE' + Require ((Test-Path (Join-Path $App 'unins000.exe')) -and !(Test-Path (Join-Path $App 'unins001.exe'))) 'ONE_UNINSTALLER' + return $d +} +try { + Require (!(Test-Path $Key) -and !(Test-Path $App) -and !(Test-Path $Priv)) 'CLEAN_DISPOSABLE_VM' + $before=Network + $report.networkBefore=$before + Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Windows_Setup.exe' -OutFile $Old -MaximumRedirection 5 -TimeoutSec 180 + Require ((Get-Item $Old).Length -eq 154270907 -and (FileSha $Old) -ceq $PublicSha) 'OFFICIAL_STABLE_SHA_PIN' + Install $Old 'STABLE_FRESH' + [void](Registered '1.5.4') + Require ((FileSha $Gui) -ceq $PublicGui -and (FileSha $Svc) -ceq $PublicSvc) 'OFFICIAL_STABLE_GUI_AND_SERVICE' + New-Item -ItemType Directory -Path (Split-Path $Config -Parent) -Force|Out-Null + Require (!(Test-Path $Config)) 'NO_PREEXISTING_PRIVATE_CONFIG' + [IO.File]::WriteAllText($Config,'R535_PRIVATE_CUSTOM_SITE_SENTINEL') + $configSha=FileSha $Config + Require (Test-Path $Provider) 'PROVIDER_EXISTS' + [IO.File]::AppendAllText($Provider,[Environment]::NewLine+' ') + $providerSha=FileSha $Provider + New-Item -ItemType Directory -Path (Split-Path $Evidence -Parent) -Force|Out-Null + [IO.File]::WriteAllText($Evidence,'R535_USER_UNKNOWN_EVIDENCE_KEEP') + $evidenceSha=FileSha $Evidence + Install $New 'RC1_IN_PLACE' + [void](Registered '1.5.5-rc1') + Require ((FileSha $Gui) -ceq $build.guiSha -and (FileSha $Svc) -ceq $build.serviceSha) 'RC1_GUI_SERVICE_PIN' + Require ((FileSha (Join-Path $App 'manifest.json')) -ceq $build.manifestSha) 'RC1_MANIFEST_PIN' + Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.5-rc1') 'RC1_INSTALLED_VERSION' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' + $d=Registered '1.5.5-rc1' + $uninstaller=Join-Path $App 'unins000.exe' + Require ([string]$d.UninstallString -like ('*'+$uninstaller+'*')) 'EXACT_UNINSTALL_PATH_OWNED' + $u=Start-Process $uninstaller -ArgumentList @('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART') -Wait -PassThru -NoNewWindow + Require ($u.ExitCode -eq 0) 'RC1_UNINSTALL_ZERO' + Require (!(Test-Path $Key)) 'ONE_UNINSTALLER_REMOVED_REGISTRY' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'UNKNOWN_USER_DATA_SURVIVED_UNINSTALL' + Install $Old 'STABLE_ROLLBACK_REINSTALL' + [void](Registered '1.5.4') + Require ((FileSha $Gui) -ceq $PublicGui -and (FileSha $Svc) -ceq $PublicSvc) 'ORIGINAL_STABLE_BINARY_HASHES_RESTORED' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'PRIVATE_CONFIG_PRESERVED_ROLLBACK' + $after=Network;$report.networkAfter=$after + Require ($before.defaultRoutes -ceq $after.defaultRoutes -and $before.dns -ceq $after.dns) 'NETWORK_BASELINE_EXACT' + $report.status='PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154' + $report.singleUninstaller=$true;$report.customProviderUpgradePreserved=$true;$report.dataPreserved=$true +} catch { + $report.status='DEGRADED_UNRESOLVED' + $report.errors+=([string]$_.Exception.Message) +} finally { + [IO.File]::WriteAllText($Result,(ConvertTo-Json $report -Depth 9)+[Environment]::NewLine,[Text.UTF8Encoding]::new($false)) + Write-Output ('R535_NATIVE_STATUS='+$report.status) + Write-Output ('R535_RECEIPT_SHA256='+((Get-FileHash $Result -Algorithm SHA256).Hash)) +} +if($report.status -ne 'PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154'){throw ('R535_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} diff --git a/tests/release_metadata_audit.py b/tests/release_metadata_audit.py index 20c73ae1..79c8954d 100644 --- a/tests/release_metadata_audit.py +++ b/tests/release_metadata_audit.py @@ -128,16 +128,38 @@ def assert_unsigned_publication_acceptance(meta): raise SystemExit(f'README does not mention current version {version}') win_manifest = json.loads((ROOT / 'windows' / 'manifest.json').read_text(encoding='utf-8-sig')) -if str(win_manifest.get('version')) != version: - raise SystemExit(f"windows/manifest.json version {win_manifest.get('version')} != {version}") +win_release = json.loads((ROOT / 'windows' / 'RELEASE.json').read_text(encoding='utf-8-sig')) +win_version = str(win_release.get('version')) +# R535: an UNPUBLISHED, Windows-only private RC1 candidate is developed against +# the publicly accepted v1.5.4 cross-platform base. It cannot silently +# replace root/public release authority or satisfy public publication. +# The exception is branch- and metadata-exact; all original public sha/source +# checks above still run. Every other drift remains a hard failure. +branch = subprocess.run(['git','branch','--show-current'],cwd=ROOT, + capture_output=True,text=True,check=True).stdout.strip() +ref = __import__('os').environ.get('GITHUB_REF','') +private_win_rc1 = ( + version == '1.5.4' and win_version == '1.5.5-rc1' + and win_release.get('status') == 'PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN' + and win_release.get('previousAccepted',{}).get('version') == '1.5.4' + and win_release.get('previousAccepted',{}).get('tag') == 'v1.5.4' + and win_release.get('publication',{}).get('publishedAt') is None + and (branch == 'feature/r535-windows-155rc1' + or ref in ('refs/heads/feature/r535-windows-155rc1', + 'refs/heads/feature/r520-linux-155rc1-ci')) +) +if win_version != version and not private_win_rc1: + raise SystemExit('WINDOWS_PRIVATE_RC1_IDENTITY_NOT_AUTHORIZED') +if str(win_manifest.get('version')) != win_version: + raise SystemExit('WINDOWS_MANIFEST_NOT_MATCHING_PLATFORM_VERSION') linux_ui = (ROOT / 'linux' / 'app' / 'direct_internet_method.py').read_text(encoding='utf-8-sig') -if ('VERSION="' + version + '"') not in linux_ui and ('VERSION = "' + version + '"') not in linux_ui: - raise SystemExit('Linux UI version is inconsistent with RELEASE.json') - -win_release = json.loads((ROOT / 'windows' / 'RELEASE.json').read_text(encoding='utf-8-sig')) -if str(win_release.get('version')) != version: - raise SystemExit(f"windows/RELEASE.json version {win_release.get('version')} != {version}") +# The Linux 1.5.5-rc1 source was already present on this private draft branch. +# Its published metadata remains anchored to v1.5.4 until native acceptance. +# Require exact RC1 UI bytes for private candidate and exact stable otherwise. +linux_source_expected = '1.5.5-rc1' if private_win_rc1 else version +if ('VERSION="' + linux_source_expected + '"') not in linux_ui and ('VERSION = "' + linux_source_expected + '"') not in linux_ui: + raise SystemExit('Linux UI version is inconsistent with authorized source state') assert_unsigned_publication_acceptance(release) root_status = str(release.get('status','')) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index dd56a134..9c9593a2 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -227,7 +227,17 @@ def idx(s,x): return s.find(x) check("windows_ci_external_audits_fail_fast", ci_workflow.count("if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }") >= 7) files={(x["scope"],x["file"]) for x in manifest["files"]} -check("manifest_version", manifest.get("version")=="1.5.4") +release_meta=json.loads(txt("RELEASE.json")) +allowed_status={ + "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), + "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), +} +current=release_meta.get("version") +check("manifest_version", current in allowed_status + and manifest.get("version")==current + and release_meta.get("status")==allowed_status[current][0] + and release_meta.get("previousAccepted",{}).get("version")==allowed_status[current][1]) + check("manifest_direct_update", manifest.get("directUpdate",{}).get("userUacRequired") is False and "SHA256SUMS.txt" in manifest.get("directUpdate",{}).get("integrity","")) check("manifest_native_exe", ("user","app/DirectInternetMethod.exe") in files) check("manifest_router_gateway_data", ("user","app/router_gateway/providers.json") in files) diff --git a/windows/RELEASE.json b/windows/RELEASE.json index bff8d2a9..6d38b73e 100644 --- a/windows/RELEASE.json +++ b/windows/RELEASE.json @@ -1,147 +1,148 @@ -{ - "schema": 1, - "product": "Direct Internet Method", - "version": "1.5.4", - "status": "CANDIDATE_V154_UNPUBLISHED", - "date": "2026-10-13", - "previousAccepted": { - "version": "1.5.3", - "tag": "v1.5.3", - "tagCommit": "581a063836a20ca3c0adb941bfc77146e33d693a" - }, - "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", - "features": { - "directDnsDpi": true, - "directMethods": [ - "encrypted-dns-doh", - "http-host-split-tcp80", - "tls-sni-desync-tcp443", - "quic-desync-udp443", - "custom-hostlist", - "adult-catalog", - "strategy-profile" - ], - "noVpn": true, - "noProxy": true, - "noDefaultRouteTunnel": true, - "noPhysicalAdapterDnsMutation": true, - "targetCoverage": [ - "YouTube family", - "Gemini", - "Adult site/CDN", - "OpenAI/ChatGPT", - "GitHub", - "user Custom Sites" - ], - "liveChecks": [ - "YouTube", - "OpenAI", - "GitHub", - "Gemini", - "Adult coverage representative matrix" - ], - "routerGateway": true, - "routerGatewayProtocols": [ - "L2TP/IPsec", - "PPTP" - ], - "routerGatewayProfileCount": 9, - "routerGatewayIndependentProviders": 3, - "preferredStartupWindow": "1289x632 DIP", - "responsiveFooterWrap": true, - "secureDnsTransports": [ - "DoH" - ], - "secureDnsLeakToOsResolver": false, - "secureDnsPrimary": "DoH direct IP 76.76.10.11/p0", - "researchedButNotEnabledDnsTransports": [ - "DoH3", - "DoQ", - "DoT" - ], - "hostlistScopedByDefault": true, - "scopeModes": { - "default": "targeted", - "available": [ - "targeted", - "all-sites" - ], - "allSitesExperimental": true - }, - "strategyProfiles": { - "default": "balanced", - "available": [ - "balanced", - "compatibility", - "strong" - ] - }, - "customSites": { - "enabled": true, - "maxEntries": 256, - "normalizesUrlsToDomains": true, - "exactHostPrefix": "^" - }, - "adultCoverage": { - "enabledByDefault": false, - "optIn": true, - "toggleable": true, - "persistedPerUser": true, - "uiLabel": "Adult coverage", - "maintainedCatalog": true, - "catalogSources": [ - "HaGeZi NSFW only-domains raw GitHub", - "HaGeZi NSFW only-domains jsDelivr fallback" - ], - "offlineFallback": true, - "minimumValidatedDomains": 10000, - "maximumValidatedDomains": 100000, - "usablePageAcceptance": "2xx/3xx only" - } - }, - "usage": { - "chatgptRequired": false, - "offlineRuntime": true, - "adminRequiredAtInstallOrPrivilegedUpgrade": true, - "adminRequiredForNormalRun": false, - "controlPanel": "app/DirectInternetMethod.exe", - "privilegedService": "DirectInternetMethodSvc", - "externalPowerShellRequired": false, - "bundledPowerShell": "7.6.6" - }, - "acceptance": { - "windowsContract": "PENDING_V154_REVERIFICATION", - "serviceSelftest": "PENDING_V154_REVERIFICATION", - "guiCompile": "PENDING_V154_REVERIFICATION", - "startupLayout": "PENDING_V154_REVERIFICATION", - "targetHostlistContract": "PENDING_V154_REVERIFICATION", - "siteCoverageLive": "PENDING_V154_REVERIFICATION", - "publication": "NOT_PUBLISHED", - "strategyProfiles": "PENDING_V154_REVERIFICATION", - "allSitesScope": "PENDING_V154_REVERIFICATION", - "securityRejectedComponentGate": "PENDING_V154_REVERIFICATION", - "artifactIdentity": "PENDING_V154_REVERIFICATION", - "windowsInstalledLive": "PENDING_V154_REVERIFICATION", - "startupHealth": "PENDING_V154_REVERIFICATION", - "adultCoverage": "PENDING_V154_REVERIFICATION", - "gemini": "FAIL_HTTP_403_PREVIOUS_EGRESS_UNCHANGED" - }, - "validationBoundary": [ - "Direct Internet Method does not change public egress IP; remote country/account/product restrictions are outside direct DNS/DPI control.", - "Gemini returns HTTP 403 on the validated IR egress in both curl and real headless Chrome. A usable Gemini page requires a separate egress-changing architecture.", - "Adult coverage uses a maintained opt-in catalog plus bounded host-specific profiles. Windows stable-state representative probes return HTTP 200 for Pornhub, RedTube, XVideos, XNXX and XHamster.", - "XHamster can resolve to multiple Cloudflare edges. Windows exact-installed balanced/targeted normal-client representative testing is HTTP 200; release claims do not assert universal availability for every remote CDN edge.", - "Linux exact-installed normal-client representative matrix passes Pornhub, XVideos, XNXX, XHamster and Reddit; forced edge 104.16.3.81 is unusable on the validated Linux path, while normal clients demonstrated failover to 104.16.4.81 with 5/5 successful restart cycles.", - "HTTP 4xx/5xx is not accepted as usable-page PASS.", - "Targeted remains the default scope; All Sites is explicit opt-in.", - "Windows installer remains not Authenticode-signed." - ], - "exactNextAction": "Source-only v1.5.4 pending exact SHA native Windows/Linux acceptance; official v1.5.3 unaffected.", - "buildIdentityNote": "Installer and manifest hashes are intentionally not embedded here because this file is itself part of the packaged payload. Authoritative artifact identity lives in root RELEASE.json, SHA256SUMS.txt and evidence records.", - "publication": { - "tag": "v1.5.4", - "tagCommit": null, - "releaseUrl": null, - "publishedAt": null - } -} +{ + "schema": 1, + "product": "Direct Internet Method", + "version": "1.5.5-rc1", + "status": "PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", + "date": "2026-10-13", + "previousAccepted": { + "version": "1.5.4", + "tag": "v1.5.4", + "tagCommit": "51009372e3c2d069d7016b97773f35ee5e1e0631", + "status": "PUBLIC_STABLE_UNCHANGED" + }, + "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", + "features": { + "directDnsDpi": true, + "directMethods": [ + "encrypted-dns-doh", + "http-host-split-tcp80", + "tls-sni-desync-tcp443", + "quic-desync-udp443", + "custom-hostlist", + "adult-catalog", + "strategy-profile" + ], + "noVpn": true, + "noProxy": true, + "noDefaultRouteTunnel": true, + "noPhysicalAdapterDnsMutation": true, + "targetCoverage": [ + "YouTube family", + "Gemini", + "Adult site/CDN", + "OpenAI/ChatGPT", + "GitHub", + "user Custom Sites" + ], + "liveChecks": [ + "YouTube", + "OpenAI", + "GitHub", + "Gemini", + "Adult coverage representative matrix" + ], + "routerGateway": true, + "routerGatewayProtocols": [ + "L2TP/IPsec", + "PPTP" + ], + "routerGatewayProfileCount": 9, + "routerGatewayIndependentProviders": 3, + "preferredStartupWindow": "1289x632 DIP", + "responsiveFooterWrap": true, + "secureDnsTransports": [ + "DoH" + ], + "secureDnsLeakToOsResolver": false, + "secureDnsPrimary": "DoH direct IP 76.76.10.11/p0", + "researchedButNotEnabledDnsTransports": [ + "DoH3", + "DoQ", + "DoT" + ], + "hostlistScopedByDefault": true, + "scopeModes": { + "default": "targeted", + "available": [ + "targeted", + "all-sites" + ], + "allSitesExperimental": true + }, + "strategyProfiles": { + "default": "balanced", + "available": [ + "balanced", + "compatibility", + "strong" + ] + }, + "customSites": { + "enabled": true, + "maxEntries": 256, + "normalizesUrlsToDomains": true, + "exactHostPrefix": "^" + }, + "adultCoverage": { + "enabledByDefault": false, + "optIn": true, + "toggleable": true, + "persistedPerUser": true, + "uiLabel": "Adult coverage", + "maintainedCatalog": true, + "catalogSources": [ + "HaGeZi NSFW only-domains raw GitHub", + "HaGeZi NSFW only-domains jsDelivr fallback" + ], + "offlineFallback": true, + "minimumValidatedDomains": 10000, + "maximumValidatedDomains": 100000, + "usablePageAcceptance": "2xx/3xx only" + } + }, + "usage": { + "chatgptRequired": false, + "offlineRuntime": true, + "adminRequiredAtInstallOrPrivilegedUpgrade": true, + "adminRequiredForNormalRun": false, + "controlPanel": "app/DirectInternetMethod.exe", + "privilegedService": "DirectInternetMethodSvc", + "externalPowerShellRequired": false, + "bundledPowerShell": "7.6.6" + }, + "acceptance": { + "windowsContract": "PENDING_V154_REVERIFICATION", + "serviceSelftest": "PENDING_V154_REVERIFICATION", + "guiCompile": "PENDING_V154_REVERIFICATION", + "startupLayout": "PENDING_V154_REVERIFICATION", + "targetHostlistContract": "PENDING_V154_REVERIFICATION", + "siteCoverageLive": "PENDING_V154_REVERIFICATION", + "publication": "NOT_PUBLISHED", + "strategyProfiles": "PENDING_V154_REVERIFICATION", + "allSitesScope": "PENDING_V154_REVERIFICATION", + "securityRejectedComponentGate": "PENDING_V154_REVERIFICATION", + "artifactIdentity": "PENDING_V154_REVERIFICATION", + "windowsInstalledLive": "PENDING_V154_REVERIFICATION", + "startupHealth": "PENDING_V154_REVERIFICATION", + "adultCoverage": "PENDING_V154_REVERIFICATION", + "gemini": "FAIL_HTTP_403_PREVIOUS_EGRESS_UNCHANGED" + }, + "validationBoundary": [ + "Direct Internet Method does not change public egress IP; remote country/account/product restrictions are outside direct DNS/DPI control.", + "Gemini returns HTTP 403 on the validated IR egress in both curl and real headless Chrome. A usable Gemini page requires a separate egress-changing architecture.", + "Adult coverage uses a maintained opt-in catalog plus bounded host-specific profiles. Windows stable-state representative probes return HTTP 200 for Pornhub, RedTube, XVideos, XNXX and XHamster.", + "XHamster can resolve to multiple Cloudflare edges. Windows exact-installed balanced/targeted normal-client representative testing is HTTP 200; release claims do not assert universal availability for every remote CDN edge.", + "Linux exact-installed normal-client representative matrix passes Pornhub, XVideos, XNXX, XHamster and Reddit; forced edge 104.16.3.81 is unusable on the validated Linux path, while normal clients demonstrated failover to 104.16.4.81 with 5/5 successful restart cycles.", + "HTTP 4xx/5xx is not accepted as usable-page PASS.", + "Targeted remains the default scope; All Sites is explicit opt-in.", + "Windows installer remains not Authenticode-signed." + ], + "exactNextAction": "Windows 1.5.5-rc1 isolated build and staged-hosted install/upgrade rollback with byte-pinned public v1.5.4; no public promotion before native acceptance.", + "buildIdentityNote": "Installer and manifest hashes are intentionally not embedded here because this file is itself part of the packaged payload. Authoritative artifact identity lives in root RELEASE.json, SHA256SUMS.txt and evidence records.", + "publication": { + "tag": null, + "tagCommit": null, + "releaseUrl": null, + "publishedAt": null + } +} diff --git a/windows/gui/DirectInternetMethod.csproj b/windows/gui/DirectInternetMethod.csproj index 94a6495d..53db0d97 100644 --- a/windows/gui/DirectInternetMethod.csproj +++ b/windows/gui/DirectInternetMethod.csproj @@ -1 +1 @@ -WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.41.5.4.01.5.4.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file +WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.5-rc11.5.5.01.5.5.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index d6900617..32b67303 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -1,692 +1,692 @@ -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; - -namespace DirectInternetMethod; - -public partial class MainWindow : Window -{ - readonly string appDir = AppContext.BaseDirectory; - readonly string statePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectDnsDpiHarness", "state.json"); - readonly string actionStatusPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "action-status.json"); - readonly string bundledPwshPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "DirectInternetMethod", "Privileged", "runtime", "pwsh", "pwsh.exe"); - readonly Brush ok = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#68E8A6")); - readonly Brush warn = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFD166")); - readonly Brush bad = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF7A85")); - readonly Brush neutral = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9FB1C3")); - readonly string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DirectInternetMethod", "settings.json"); - readonly string customHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "custom-hosts.txt"); - readonly string adultEnabledPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-enabled.txt"); - readonly string adultHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.txt"); - readonly string adultMetaPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.meta.json"); - static readonly string[] AdultCatalogUrls = - { - "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/wildcard/nsfw-onlydomains.txt", - "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/nsfw-onlydomains.txt" - }; - static readonly Regex domainLabel = new("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", RegexOptions.CultureInvariant | RegexOptions.Compiled); - readonly string strategyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "strategy.txt"); - readonly string scopePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "scope.txt"); - UpdateInfo? availableUpdate; - bool adultCheckEnabled; - - public MainWindow() - { - InitializeComponent(); - adultCheckEnabled = LoadAdultSiteCheckSetting(); - AdultSiteCheckToggle.IsChecked = adultCheckEnabled; - AdultSiteCheckToggle.Checked += AdultSiteCheckToggle_Changed; - AdultSiteCheckToggle.Unchecked += AdultSiteCheckToggle_Changed; - Loaded += async (_, _) => - { - if (adultCheckEnabled) - { - try - { - SaveAdultSiteCheckSetting(); - await EnsureAdultCatalogAsync(false); - } - catch (Exception ex) - { - AdultSiteText.Text = "Coverage fallback active · catalog sync failed: " + ex.Message; - } - } - await RefreshStatusAsync(); - _ = CheckForUpdatesAsync(false); - }; - } - - Version CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 1, 0, 0); - - bool LoadAdultSiteCheckSetting() - { - try - { - if (File.Exists(adultEnabledPath)) - return File.ReadAllText(adultEnabledPath).Trim() == "1"; - if (!File.Exists(settingsPath)) return false; - using var doc = JsonDocument.Parse(File.ReadAllText(settingsPath)); - if (doc.RootElement.TryGetProperty("adultCoverageEnabled", out var coverage)) - return coverage.ValueKind == JsonValueKind.True; - return doc.RootElement.TryGetProperty("adultSiteLiveCheck", out var legacy) && legacy.ValueKind == JsonValueKind.True; - } - catch { return false; } - } - - void SaveAdultSiteCheckSetting() - { - var settingsDir = Path.GetDirectoryName(settingsPath)!; - Directory.CreateDirectory(settingsDir); - var settingsTemp = settingsPath + ".tmp"; - File.WriteAllText(settingsTemp, JsonSerializer.Serialize(new { adultCoverageEnabled = adultCheckEnabled, adultSiteLiveCheck = adultCheckEnabled })); - File.Move(settingsTemp, settingsPath, true); - - var configDir = Path.GetDirectoryName(adultEnabledPath)!; - Directory.CreateDirectory(configDir); - var enabledTemp = adultEnabledPath + ".tmp"; - File.WriteAllText(enabledTemp, adultCheckEnabled ? "1\r\n" : "0\r\n", new UTF8Encoding(false)); - File.Move(enabledTemp, adultEnabledPath, true); - if (!adultCheckEnabled) - { - try { if (File.Exists(adultHostsPath)) File.Delete(adultHostsPath); } catch { } - try { if (File.Exists(adultMetaPath)) File.Delete(adultMetaPath); } catch { } - } - } - - sealed record AdultCatalogInfo(int Count, string Sha256); - - static List ValidateAdultCatalog(string text) - { - var result = new List(); - var seen = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var raw in text.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None)) - { - var v = raw.Trim().ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(v) || v.StartsWith('#')) continue; - if (v.StartsWith('^')) v = v[1..]; - if (v.Length is < 3 or > 253 || !v.Contains('.')) continue; - if (v.Split('.').Any(x => !domainLabel.IsMatch(x))) continue; - if (seen.Add(v)) result.Add(v); - if (result.Count > 100000) throw new InvalidDataException("Adult catalog exceeds 100,000 validated domains."); - } - if (result.Count < 10000) throw new InvalidDataException("Adult catalog is unexpectedly small."); - foreach (var required in new[] { "xvideos.com", "xnxx.com", "xhamster.com", "pornhub.com", "redtube.com" }) - if (!seen.Contains(required)) throw new InvalidDataException("Adult catalog is missing a required coverage family."); - return result; - } - - async Task EnsureAdultCatalogAsync(bool force) - { - if (!force && File.Exists(adultHostsPath) && File.GetLastWriteTimeUtc(adultHostsPath) > DateTime.UtcNow.AddHours(-24)) - { - var existing = ValidateAdultCatalog(await File.ReadAllTextAsync(adultHostsPath)); - return new AdultCatalogInfo(existing.Count, Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(adultHostsPath)))); - } - - using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(20) }; - http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.4"); - Exception? lastError = null; - foreach (var sourceUrl in AdultCatalogUrls) - { - try - { - var bytes = await http.GetByteArrayAsync(sourceUrl); - if (bytes.Length is < 100000 or > 4 * 1024 * 1024) throw new InvalidDataException("Adult catalog size is outside the accepted range."); - var raw = Encoding.UTF8.GetString(bytes); - var domains = ValidateAdultCatalog(raw); - var dir = Path.GetDirectoryName(adultHostsPath)!; - Directory.CreateDirectory(dir); - var tmp = adultHostsPath + ".tmp"; - await File.WriteAllTextAsync(tmp, string.Join(Environment.NewLine, domains) + Environment.NewLine, new UTF8Encoding(false)); - File.Move(tmp, adultHostsPath, true); - var digest = Convert.ToHexString(SHA256.HashData(bytes)); - var metaTmp = adultMetaPath + ".tmp"; - await File.WriteAllTextAsync(metaTmp, JsonSerializer.Serialize(new - { - schema = 1, - source = sourceUrl, - fetchedUtc = DateTime.UtcNow.ToString("O"), - sourceSha256 = digest, - entries = domains.Count - }), new UTF8Encoding(false)); - File.Move(metaTmp, adultMetaPath, true); - return new AdultCatalogInfo(domains.Count, digest); - } - catch (Exception ex) - { - lastError = ex; - } - } - throw new InvalidOperationException("Adult catalog sync failed from all official upstream endpoints.", lastError); - } - - async Task RunPwshCaptureAsync(string script, string args = "") - { - if (!File.Exists(bundledPwshPath)) - throw new FileNotFoundException("Bundled PowerShell runtime is missing.", bundledPwshPath); - var psi = new ProcessStartInfo(bundledPwshPath, - $"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"{Path.Combine(appDir, script)}\" {args}") - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - WorkingDirectory = appDir - }; - using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start PowerShell 7."); - var output = await p.StandardOutput.ReadToEndAsync(); - var error = await p.StandardError.ReadToEndAsync(); - await p.WaitForExitAsync(); - if (p.ExitCode != 0) throw new InvalidOperationException(string.IsNullOrWhiteSpace(error) ? $"Status backend exited {p.ExitCode}." : error.Trim()); - return output; - } - - static string ReadStringList(JsonElement root, string property) - { - if (!root.TryGetProperty(property, out var v)) return "—"; - if (v.ValueKind == JsonValueKind.String) return v.GetString() ?? "—"; - if (v.ValueKind == JsonValueKind.Array) - { - var items = v.EnumerateArray().Select(x => x.ValueKind == JsonValueKind.String ? x.GetString() : x.ToString()) - .Where(x => !string.IsNullOrWhiteSpace(x)); - var text = string.Join(", ", items!); - return string.IsNullOrWhiteSpace(text) ? "—" : text; - } - return "—"; - } - - sealed record ProbeResult(bool Ok, bool Reached, string Code, string RemoteIp, long Ms); - - async Task ProbeAsync(string url, string localIp, string[] allowed) - { - var sw = Stopwatch.StartNew(); - try - { - var psi = new ProcessStartInfo("curl.exe", - $"-4 --interface {localIp} --noproxy * -A \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36\" -sS -o NUL -w \"%{{http_code}}|%{{remote_ip}}|%{{time_total}}\" --connect-timeout 5 --max-time 8 \"{url}\"") - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden - }; - using var p = Process.Start(psi)!; - var stdout = await p.StandardOutput.ReadToEndAsync(); - await p.WaitForExitAsync(); - sw.Stop(); - var parts = stdout.Trim().Split('|'); - var code = parts.Length > 0 ? parts[0] : ""; - var remote = parts.Length > 1 ? parts[1] : ""; - var reached = p.ExitCode == 0 && !string.IsNullOrWhiteSpace(code) && code != "000"; - return new ProbeResult(reached && allowed.Contains(code), reached, code, remote, sw.ElapsedMilliseconds); - } - catch - { - sw.Stop(); - return new ProbeResult(false, false, "", "", sw.ElapsedMilliseconds); - } - } - - static string FmtHttp(ProbeResult p) => - p.Ok ? $"PASS · HTTP {p.Code} · {p.Ms} ms" : - p.Reached ? $"FAIL · HTTP {p.Code} · {p.Ms} ms" : - "FAIL · transport"; - - // OpenAI API is an endpoint transport probe, never evidence of a signed-in ChatGPT browser. - static string FmtOpenAiApiProbe(ProbeResult p) => p.Code switch - { - "403" when p.Reached => "API HTTP 403 forbidden · login unverified", - "401" when p.Reached => "API HTTP 401 auth required · login unverified", - "200" when p.Reached => "API HTTP 200 reachable · login unverified", - _ when p.Reached => $"API HTTP {p.Code} unexpected · login unverified", - _ => "API transport unavailable · login unverified" - }; - - static string FmtAdult(IReadOnlyCollection probes) - { - var okCount = probes.Count(x => x.Ok); - return okCount == probes.Count - ? $"PASS · full pages {okCount}/{probes.Count}" - : $"FAIL · full pages {okCount}/{probes.Count}"; - } - - async Task RefreshStatusAsync() - { - try - { - HealthText.Text = "Checking…"; - var raw = await RunPwshCaptureAsync("Status.ps1", "-Json"); - using var d = JsonDocument.Parse(raw); - var r = d.RootElement; - var mode = r.TryGetProperty("mode", out var me) ? me.GetString() ?? "UNKNOWN" : "UNKNOWN"; - var reason = r.TryGetProperty("reason", out var re) ? re.GetString() ?? "" : ""; - - StatusBadge.Text = mode; - StatusBadge.Foreground = mode switch - { - "ACTIVE" => ok, - "OFF" => neutral, - "CONFLICT" => warn, - "BLOCKED" => warn, - _ => bad - }; - - InterfaceText.Text = r.TryGetProperty("interface", out var i) ? i.GetString() ?? "—" : "—"; - IpText.Text = r.TryGetProperty("localIp", out var ip) ? ip.GetString() ?? "—" : "—"; - DnsText.Text = ReadStringList(r, "adapterDns"); - - int ula = r.TryGetProperty("ula", out var ue) && ue.ValueKind == JsonValueKind.Array ? ue.GetArrayLength() : 0; - int nrpt = r.TryGetProperty("nrpt", out var ne) && ne.ValueKind == JsonValueKind.Array ? ne.GetArrayLength() : 0; - int wd = r.TryGetProperty("winDivertCount", out var we) && we.TryGetInt32(out var wdv) ? wdv : 0; - int broad = r.TryGetProperty("broadRouteCount", out var be) && be.TryGetInt32(out var bv) ? bv : 0; - - OwnedText.Text = $"ULA {ula} · NRPT {nrpt} · DPI {wd}"; - if (broad > 0 && r.TryGetProperty("broadRoutes", out var routes) && routes.ValueKind == JsonValueKind.Array) - { - var owners = routes.EnumerateArray() - .Select(x => - { - if (x.TryGetProperty("description", out var de) && !string.IsNullOrWhiteSpace(de.GetString())) return de.GetString(); - return x.TryGetProperty("interface", out var ie) ? ie.GetString() : null; - }) - .Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(); - var suffix = string.Join(", ", owners!); - RouteText.Text = string.IsNullOrWhiteSpace(suffix) ? $"External tunnel routes: {broad}" : $"External tunnel routes: {broad} · {suffix}"; - } - else RouteText.Text = "External tunnel routes: 0"; - - bool ownedHealthy = r.TryGetProperty("ownedResourcesHealthy", out var oh) && oh.GetBoolean(); - YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy"; - AdultSiteText.Text = adultCheckEnabled ? (mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy") : "Disabled"; - if (ownedHealthy && mode == "ACTIVE" && !string.IsNullOrWhiteSpace(IpText.Text) && IpText.Text != "—") - { - var ytTask = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "200", "204" }); - var oaTask = ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" }); - var ghTask = ProbeAsync("https://github.com/", IpText.Text, new[] { "200", "301", "302" }); - var geminiTask = ProbeAsync("https://gemini.google.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }); - Task[]? adultTasks = adultCheckEnabled - ? new[] - { - ProbeAsync("https://www.pornhub.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), - ProbeAsync("https://www.xvideos.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), - ProbeAsync("https://www.xnxx.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), - ProbeAsync("https://xhamster.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }) - } - : null; - var tasks = new List { ytTask, oaTask, ghTask, geminiTask }; - if (adultTasks is not null) tasks.AddRange(adultTasks); - await Task.WhenAll(tasks); - YoutubeText.Text = FmtHttp(await ytTask); - OpenAiText.Text = FmtOpenAiApiProbe(await oaTask); - GithubText.Text = FmtHttp(await ghTask); - GeminiText.Text = FmtHttp(await geminiTask); - AdultSiteText.Text = adultTasks is null ? "Disabled" : FmtAdult(await Task.WhenAll(adultTasks)); - } - - HealthText.Text = mode switch - { - "ACTIVE" => "Direct path healthy", - "CONFLICT" => "External VPN/tunnel conflict", - "BLOCKED" => "Disconnect external VPN/tunnel to Start", - "DEGRADED" => "Recovery required", - "OFF" => "Ready", - _ => "Unknown state" - }; - if (!string.IsNullOrWhiteSpace(reason)) ToolTip = reason; - - StartButton.IsEnabled = mode == "OFF"; - StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED"; - RecoveryButton.IsEnabled = mode is "DEGRADED" or "CONFLICT"; - RefreshButton.IsEnabled = true; - } - catch (Exception ex) - { - HealthText.Text = "Status error"; - StatusBadge.Text = "ERROR"; - StatusBadge.Foreground = bad; - YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = ex.Message; - AdultSiteText.Text = adultCheckEnabled ? ex.Message : "Disabled"; - StartButton.IsEnabled = false; - StopButton.IsEnabled = false; - RecoveryButton.IsEnabled = true; - RefreshButton.IsEnabled = true; - } - } - - sealed record ActionResult(string Phase, int? ExitCode, string Message); - - async Task WaitForActionAsync(string action, DateTime previousWriteUtc, int timeoutMs = 130000) - { - var until = DateTime.UtcNow.AddMilliseconds(timeoutMs); - while (DateTime.UtcNow < until) - { - try - { - if (File.Exists(actionStatusPath) && File.GetLastWriteTimeUtc(actionStatusPath) > previousWriteUtc) - { - using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(actionStatusPath)); - var root = doc.RootElement; - var actual = root.TryGetProperty("action", out var ae) ? ae.GetString() ?? "" : ""; - var phase = root.TryGetProperty("phase", out var pe) ? pe.GetString() ?? "" : ""; - if (actual.Equals(action, StringComparison.OrdinalIgnoreCase) && - phase is "done" or "error" or "busy" or "handoff") - { - int? exit = root.TryGetProperty("exitCode", out var ee) && ee.ValueKind == JsonValueKind.Number ? ee.GetInt32() : null; - var message = root.TryGetProperty("message", out var me) ? me.GetString() ?? "" : ""; - return new ActionResult(phase, exit, message); - } - } - } - catch (IOException) { } - catch (JsonException) { } - await Task.Delay(200); - } - throw new TimeoutException($"{action} did not complete within {timeoutMs / 1000} seconds."); - } - - async Task RunActionAsync(string action, string label) - { - SetBusy(true, label + "…"); - try - { - var previousWriteUtc = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; - ServiceClient.Send(action); - var result = await WaitForActionAsync(action, previousWriteUtc); - if (result.Phase != "done" || result.ExitCode != 0) - throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) - ? $"{label} failed ({result.ExitCode?.ToString() ?? result.Phase})." - : result.Message); - } - catch (Exception ex) - { - HealthText.Text = ex.Message; - } - finally - { - await RefreshStatusAsync(); - SetBusy(false, ""); - } - } - - void SetBusy(bool busy, string message) - { - if (busy) - { - StartButton.IsEnabled = StopButton.IsEnabled = RefreshButton.IsEnabled = RecoveryButton.IsEnabled = UpdateButton.IsEnabled = false; - HealthText.Text = message; - } - else - { - UpdateButton.IsEnabled = true; - } - } - - async Task CheckForUpdatesAsync(bool userInitiated) - { - try - { - UpdateButton.IsEnabled = false; - UpdateButton.Content = "Checking…"; - availableUpdate = await UpdateClient.CheckAsync(CurrentVersion); - if (availableUpdate is null) - { - UpdateButton.Content = "Up to date"; - if (userInitiated) HealthText.Text = "No newer release is available"; - } - else - { - UpdateButton.Content = $"Update v{availableUpdate.Version}"; - ToolTip = $"Verified update available: {availableUpdate.Tag}"; - } - } - catch (Exception ex) - { - UpdateButton.Content = "Check Update"; - if (userInitiated) HealthText.Text = "Update check failed: " + ex.Message; - } - finally - { - UpdateButton.IsEnabled = true; - } - } - - async void Update_Click(object sender, RoutedEventArgs e) - { - if (availableUpdate is null) - { - await CheckForUpdatesAsync(true); - if (availableUpdate is null) return; - } - - try - { - SetBusy(true, $"Preparing direct update to v{availableUpdate.Version}…"); - - var recoveryWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; - ServiceClient.Send("recovery"); - var recovery = await WaitForActionAsync("recovery", recoveryWrite); - if (recovery.Phase != "done" || recovery.ExitCode != 0) - throw new InvalidOperationException(string.IsNullOrWhiteSpace(recovery.Message) - ? "Unable to prepare a clean OFF state for update." - : recovery.Message); - - HealthText.Text = $"Downloading and verifying v{availableUpdate.Version}…"; - var updateWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; - ServiceClient.Send("update"); - var result = await WaitForActionAsync("update", updateWrite, 300000); - - if (result.Phase == "handoff" && result.ExitCode == 0) - { - HealthText.Text = result.Message; - await Task.Delay(600); - Close(); - return; - } - - if (result.Phase == "done" && result.ExitCode == 0) - { - availableUpdate = null; - HealthText.Text = result.Message; - UpdateButton.Content = "Up to date"; - SetBusy(false, ""); - return; - } - - throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) - ? $"Direct update failed ({result.ExitCode?.ToString() ?? result.Phase})." - : result.Message); - } - catch (Exception ex) - { - HealthText.Text = "Update failed: " + ex.Message; - SetBusy(false, ""); - } - } - - async void AdultSiteCheckToggle_Changed(object sender, RoutedEventArgs e) - { - adultCheckEnabled = AdultSiteCheckToggle.IsChecked == true; - try - { - SaveAdultSiteCheckSetting(); - if (adultCheckEnabled) - { - AdultSiteText.Text = "Syncing coverage catalog…"; - var info = await EnsureAdultCatalogAsync(true); - AdultSiteText.Text = $"Enabled · {info.Count:N0} domains · Stop/Start to apply"; - } - else - { - AdultSiteText.Text = "Disabled"; - } - } - catch (Exception ex) - { - AdultSiteText.Text = adultCheckEnabled - ? "Fallback coverage enabled · catalog sync failed: " + ex.Message - : "Settings error: " + ex.Message; - } - await RefreshStatusAsync(); - } - - static string? NormalizeCustomSite(string raw) - { - var value = (raw ?? "").Trim(); - if (string.IsNullOrWhiteSpace(value) || value.StartsWith('#')) return null; - var exact = value.StartsWith('^'); - if (exact) value = value[1..].Trim(); - string host; - if (Uri.TryCreate(value, UriKind.Absolute, out var absolute) && !string.IsNullOrWhiteSpace(absolute.Host)) - host = absolute.Host; - else if (Uri.TryCreate("https://" + value, UriKind.Absolute, out var implied) && !string.IsNullOrWhiteSpace(implied.Host)) - host = implied.Host; - else - return null; - host = host.Trim('.').ToLowerInvariant(); - if (host.Length is < 3 or > 253 || !host.Contains('.')) return null; - if (host.Split('.').Any(x => !domainLabel.IsMatch(x))) return null; - return exact ? "^" + host : host; - } - - void CustomSites_Click(object sender, RoutedEventArgs e) - { - var editor = new Window - { - Title = "Custom Sites", Owner = this, Width = 570, Height = 470, MinWidth = 470, MinHeight = 360, - WindowStartupLocation = WindowStartupLocation.CenterOwner, - Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) - }; - var root = new DockPanel { Margin = new Thickness(18) }; - var note = new TextBlock - { - Text = "One site or URL per line. The saved domain and its subdomains use the existing Direct Method on the next Start. Prefix ^ for exact-host only. Maximum 256 entries.", - TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,12) - }; - DockPanel.SetDock(note, Dock.Top); root.Children.Add(note); - var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0,12,0,0) }; - DockPanel.SetDock(buttons, Dock.Bottom); - var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; - var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; - buttons.Children.Add(save); buttons.Children.Add(cancel); root.Children.Add(buttons); - var box = new TextBox - { - AcceptsReturn = true, TextWrapping = TextWrapping.NoWrap, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, - HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, FontFamily = new FontFamily("Consolas") - }; - try { if (File.Exists(customHostsPath)) box.Text = File.ReadAllText(customHostsPath); } catch { } - root.Children.Add(box); editor.Content = root; - save.Click += (_, _) => - { - try - { - var values = box.Text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None) - .Select(NormalizeCustomSite).Where(x => !string.IsNullOrWhiteSpace(x)) - .Distinct(StringComparer.OrdinalIgnoreCase).Take(256).ToArray(); - var dir = Path.GetDirectoryName(customHostsPath)!; Directory.CreateDirectory(dir); - var tmp = customHostsPath + ".tmp"; - File.WriteAllText(tmp, string.Join(Environment.NewLine, values!) + (values.Length > 0 ? Environment.NewLine : ""), new System.Text.UTF8Encoding(false)); - File.Move(tmp, customHostsPath, true); - editor.DialogResult = true; HealthText.Text = $"Custom sites saved: {values.Length}. Stop/Start to apply."; editor.Close(); - } - catch (Exception ex) - { - MessageBox.Show(editor, "Unable to save custom sites: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); - } - }; - editor.ShowDialog(); - } - void Strategy_Click(object sender, RoutedEventArgs e) - { - var current = "balanced"; - var allSites = false; - try - { - if (File.Exists(strategyPath)) - { - var raw = File.ReadAllText(strategyPath).Trim().ToLowerInvariant(); - if (raw is "balanced" or "compatibility" or "strong") current = raw; - } - } - catch { } - try - { - if (File.Exists(scopePath)) allSites = File.ReadAllText(scopePath).Trim().Equals("all-sites", StringComparison.OrdinalIgnoreCase); - } - catch { } - - var editor = new Window - { - Title = "Direct Strategy", Owner = this, Width = 540, Height = 390, ResizeMode = ResizeMode.NoResize, - WindowStartupLocation = WindowStartupLocation.CenterOwner, - Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) - }; - var root = new StackPanel { Margin = new Thickness(20) }; - root.Children.Add(new TextBlock - { - Text = "Choose the packet strategy used for built-in and Custom Sites. Balanced is the default. Compatibility uses split-only TCP handling. Strong is more aggressive and should be used only when Balanced still fails.", - TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,14) - }); - var combo = new ComboBox { MinWidth = 230, HorizontalAlignment = HorizontalAlignment.Left }; - combo.Items.Add("Balanced"); combo.Items.Add("Compatibility"); combo.Items.Add("Strong"); - combo.SelectedIndex = current switch { "compatibility" => 1, "strong" => 2, _ => 0 }; - root.Children.Add(combo); - var allSitesCheck = new CheckBox - { - Content = "Apply DPI strategy to all web sites (experimental)", - IsChecked = allSites, Foreground = Brushes.White, Margin = new Thickness(0,14,0,0) - }; - root.Children.Add(allSitesCheck); - root.Children.Add(new TextBlock - { - Text = "Targeted mode affects only built-in and Custom Sites. All Sites mode can help unknown blocked domains, but may reduce compatibility or speed on some sites. Neither mode creates a VPN, proxy, or default-route tunnel.", - TextWrapping = TextWrapping.Wrap, Foreground = neutral, Margin = new Thickness(0,12,0,16) - }); - var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right }; - var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; - var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; - buttons.Children.Add(cancel); buttons.Children.Add(save); root.Children.Add(buttons); editor.Content = root; - save.Click += (_, _) => - { - try - { - var value = combo.SelectedIndex switch { 1 => "compatibility", 2 => "strong", _ => "balanced" }; - var dir = Path.GetDirectoryName(strategyPath)!; Directory.CreateDirectory(dir); - var tmp = strategyPath + ".tmp"; - File.WriteAllText(tmp, value + Environment.NewLine, new System.Text.UTF8Encoding(false)); - File.Move(tmp, strategyPath, true); - var scopeValue = allSitesCheck.IsChecked == true ? "all-sites" : "targeted"; - var scopeTmp = scopePath + ".tmp"; - File.WriteAllText(scopeTmp, scopeValue + Environment.NewLine, new System.Text.UTF8Encoding(false)); - File.Move(scopeTmp, scopePath, true); - HealthText.Text = $"Strategy saved: {value}; scope: {scopeValue}. Stop/Start to apply."; - editor.DialogResult = true; editor.Close(); - } - catch (Exception ex) - { - MessageBox.Show(editor, "Unable to save strategy: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); - } - }; - editor.ShowDialog(); - } - - void RouterGateway_Click(object sender, RoutedEventArgs e) - { - var w = new RouterGatewayWindow { Owner = this }; - w.ShowDialog(); - } - - async void Start_Click(object sender, RoutedEventArgs e) => await RunActionAsync("start", "Start"); - async void Stop_Click(object sender, RoutedEventArgs e) => await RunActionAsync("stop", "Stop"); - async void Recovery_Click(object sender, RoutedEventArgs e) => await RunActionAsync("recovery", "Recovery"); - async void Refresh_Click(object sender, RoutedEventArgs e) => await RefreshStatusAsync(); -} +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace DirectInternetMethod; + +public partial class MainWindow : Window +{ + readonly string appDir = AppContext.BaseDirectory; + readonly string statePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectDnsDpiHarness", "state.json"); + readonly string actionStatusPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "action-status.json"); + readonly string bundledPwshPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "DirectInternetMethod", "Privileged", "runtime", "pwsh", "pwsh.exe"); + readonly Brush ok = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#68E8A6")); + readonly Brush warn = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFD166")); + readonly Brush bad = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF7A85")); + readonly Brush neutral = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9FB1C3")); + readonly string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DirectInternetMethod", "settings.json"); + readonly string customHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "custom-hosts.txt"); + readonly string adultEnabledPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-enabled.txt"); + readonly string adultHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.txt"); + readonly string adultMetaPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.meta.json"); + static readonly string[] AdultCatalogUrls = + { + "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/wildcard/nsfw-onlydomains.txt", + "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/nsfw-onlydomains.txt" + }; + static readonly Regex domainLabel = new("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", RegexOptions.CultureInvariant | RegexOptions.Compiled); + readonly string strategyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "strategy.txt"); + readonly string scopePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "scope.txt"); + UpdateInfo? availableUpdate; + bool adultCheckEnabled; + + public MainWindow() + { + InitializeComponent(); + adultCheckEnabled = LoadAdultSiteCheckSetting(); + AdultSiteCheckToggle.IsChecked = adultCheckEnabled; + AdultSiteCheckToggle.Checked += AdultSiteCheckToggle_Changed; + AdultSiteCheckToggle.Unchecked += AdultSiteCheckToggle_Changed; + Loaded += async (_, _) => + { + if (adultCheckEnabled) + { + try + { + SaveAdultSiteCheckSetting(); + await EnsureAdultCatalogAsync(false); + } + catch (Exception ex) + { + AdultSiteText.Text = "Coverage fallback active · catalog sync failed: " + ex.Message; + } + } + await RefreshStatusAsync(); + _ = CheckForUpdatesAsync(false); + }; + } + + Version CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 1, 0, 0); + + bool LoadAdultSiteCheckSetting() + { + try + { + if (File.Exists(adultEnabledPath)) + return File.ReadAllText(adultEnabledPath).Trim() == "1"; + if (!File.Exists(settingsPath)) return false; + using var doc = JsonDocument.Parse(File.ReadAllText(settingsPath)); + if (doc.RootElement.TryGetProperty("adultCoverageEnabled", out var coverage)) + return coverage.ValueKind == JsonValueKind.True; + return doc.RootElement.TryGetProperty("adultSiteLiveCheck", out var legacy) && legacy.ValueKind == JsonValueKind.True; + } + catch { return false; } + } + + void SaveAdultSiteCheckSetting() + { + var settingsDir = Path.GetDirectoryName(settingsPath)!; + Directory.CreateDirectory(settingsDir); + var settingsTemp = settingsPath + ".tmp"; + File.WriteAllText(settingsTemp, JsonSerializer.Serialize(new { adultCoverageEnabled = adultCheckEnabled, adultSiteLiveCheck = adultCheckEnabled })); + File.Move(settingsTemp, settingsPath, true); + + var configDir = Path.GetDirectoryName(adultEnabledPath)!; + Directory.CreateDirectory(configDir); + var enabledTemp = adultEnabledPath + ".tmp"; + File.WriteAllText(enabledTemp, adultCheckEnabled ? "1\r\n" : "0\r\n", new UTF8Encoding(false)); + File.Move(enabledTemp, adultEnabledPath, true); + if (!adultCheckEnabled) + { + try { if (File.Exists(adultHostsPath)) File.Delete(adultHostsPath); } catch { } + try { if (File.Exists(adultMetaPath)) File.Delete(adultMetaPath); } catch { } + } + } + + sealed record AdultCatalogInfo(int Count, string Sha256); + + static List ValidateAdultCatalog(string text) + { + var result = new List(); + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var raw in text.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None)) + { + var v = raw.Trim().ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(v) || v.StartsWith('#')) continue; + if (v.StartsWith('^')) v = v[1..]; + if (v.Length is < 3 or > 253 || !v.Contains('.')) continue; + if (v.Split('.').Any(x => !domainLabel.IsMatch(x))) continue; + if (seen.Add(v)) result.Add(v); + if (result.Count > 100000) throw new InvalidDataException("Adult catalog exceeds 100,000 validated domains."); + } + if (result.Count < 10000) throw new InvalidDataException("Adult catalog is unexpectedly small."); + foreach (var required in new[] { "xvideos.com", "xnxx.com", "xhamster.com", "pornhub.com", "redtube.com" }) + if (!seen.Contains(required)) throw new InvalidDataException("Adult catalog is missing a required coverage family."); + return result; + } + + async Task EnsureAdultCatalogAsync(bool force) + { + if (!force && File.Exists(adultHostsPath) && File.GetLastWriteTimeUtc(adultHostsPath) > DateTime.UtcNow.AddHours(-24)) + { + var existing = ValidateAdultCatalog(await File.ReadAllTextAsync(adultHostsPath)); + return new AdultCatalogInfo(existing.Count, Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(adultHostsPath)))); + } + + using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(20) }; + http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.5-rc1"); + Exception? lastError = null; + foreach (var sourceUrl in AdultCatalogUrls) + { + try + { + var bytes = await http.GetByteArrayAsync(sourceUrl); + if (bytes.Length is < 100000 or > 4 * 1024 * 1024) throw new InvalidDataException("Adult catalog size is outside the accepted range."); + var raw = Encoding.UTF8.GetString(bytes); + var domains = ValidateAdultCatalog(raw); + var dir = Path.GetDirectoryName(adultHostsPath)!; + Directory.CreateDirectory(dir); + var tmp = adultHostsPath + ".tmp"; + await File.WriteAllTextAsync(tmp, string.Join(Environment.NewLine, domains) + Environment.NewLine, new UTF8Encoding(false)); + File.Move(tmp, adultHostsPath, true); + var digest = Convert.ToHexString(SHA256.HashData(bytes)); + var metaTmp = adultMetaPath + ".tmp"; + await File.WriteAllTextAsync(metaTmp, JsonSerializer.Serialize(new + { + schema = 1, + source = sourceUrl, + fetchedUtc = DateTime.UtcNow.ToString("O"), + sourceSha256 = digest, + entries = domains.Count + }), new UTF8Encoding(false)); + File.Move(metaTmp, adultMetaPath, true); + return new AdultCatalogInfo(domains.Count, digest); + } + catch (Exception ex) + { + lastError = ex; + } + } + throw new InvalidOperationException("Adult catalog sync failed from all official upstream endpoints.", lastError); + } + + async Task RunPwshCaptureAsync(string script, string args = "") + { + if (!File.Exists(bundledPwshPath)) + throw new FileNotFoundException("Bundled PowerShell runtime is missing.", bundledPwshPath); + var psi = new ProcessStartInfo(bundledPwshPath, + $"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"{Path.Combine(appDir, script)}\" {args}") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = appDir + }; + using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start PowerShell 7."); + var output = await p.StandardOutput.ReadToEndAsync(); + var error = await p.StandardError.ReadToEndAsync(); + await p.WaitForExitAsync(); + if (p.ExitCode != 0) throw new InvalidOperationException(string.IsNullOrWhiteSpace(error) ? $"Status backend exited {p.ExitCode}." : error.Trim()); + return output; + } + + static string ReadStringList(JsonElement root, string property) + { + if (!root.TryGetProperty(property, out var v)) return "—"; + if (v.ValueKind == JsonValueKind.String) return v.GetString() ?? "—"; + if (v.ValueKind == JsonValueKind.Array) + { + var items = v.EnumerateArray().Select(x => x.ValueKind == JsonValueKind.String ? x.GetString() : x.ToString()) + .Where(x => !string.IsNullOrWhiteSpace(x)); + var text = string.Join(", ", items!); + return string.IsNullOrWhiteSpace(text) ? "—" : text; + } + return "—"; + } + + sealed record ProbeResult(bool Ok, bool Reached, string Code, string RemoteIp, long Ms); + + async Task ProbeAsync(string url, string localIp, string[] allowed) + { + var sw = Stopwatch.StartNew(); + try + { + var psi = new ProcessStartInfo("curl.exe", + $"-4 --interface {localIp} --noproxy * -A \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36\" -sS -o NUL -w \"%{{http_code}}|%{{remote_ip}}|%{{time_total}}\" --connect-timeout 5 --max-time 8 \"{url}\"") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + using var p = Process.Start(psi)!; + var stdout = await p.StandardOutput.ReadToEndAsync(); + await p.WaitForExitAsync(); + sw.Stop(); + var parts = stdout.Trim().Split('|'); + var code = parts.Length > 0 ? parts[0] : ""; + var remote = parts.Length > 1 ? parts[1] : ""; + var reached = p.ExitCode == 0 && !string.IsNullOrWhiteSpace(code) && code != "000"; + return new ProbeResult(reached && allowed.Contains(code), reached, code, remote, sw.ElapsedMilliseconds); + } + catch + { + sw.Stop(); + return new ProbeResult(false, false, "", "", sw.ElapsedMilliseconds); + } + } + + static string FmtHttp(ProbeResult p) => + p.Ok ? $"PASS · HTTP {p.Code} · {p.Ms} ms" : + p.Reached ? $"FAIL · HTTP {p.Code} · {p.Ms} ms" : + "FAIL · transport"; + + // OpenAI API is an endpoint transport probe, never evidence of a signed-in ChatGPT browser. + static string FmtOpenAiApiProbe(ProbeResult p) => p.Code switch + { + "403" when p.Reached => "API HTTP 403 forbidden · login unverified", + "401" when p.Reached => "API HTTP 401 auth required · login unverified", + "200" when p.Reached => "API HTTP 200 reachable · login unverified", + _ when p.Reached => $"API HTTP {p.Code} unexpected · login unverified", + _ => "API transport unavailable · login unverified" + }; + + static string FmtAdult(IReadOnlyCollection probes) + { + var okCount = probes.Count(x => x.Ok); + return okCount == probes.Count + ? $"PASS · full pages {okCount}/{probes.Count}" + : $"FAIL · full pages {okCount}/{probes.Count}"; + } + + async Task RefreshStatusAsync() + { + try + { + HealthText.Text = "Checking…"; + var raw = await RunPwshCaptureAsync("Status.ps1", "-Json"); + using var d = JsonDocument.Parse(raw); + var r = d.RootElement; + var mode = r.TryGetProperty("mode", out var me) ? me.GetString() ?? "UNKNOWN" : "UNKNOWN"; + var reason = r.TryGetProperty("reason", out var re) ? re.GetString() ?? "" : ""; + + StatusBadge.Text = mode; + StatusBadge.Foreground = mode switch + { + "ACTIVE" => ok, + "OFF" => neutral, + "CONFLICT" => warn, + "BLOCKED" => warn, + _ => bad + }; + + InterfaceText.Text = r.TryGetProperty("interface", out var i) ? i.GetString() ?? "—" : "—"; + IpText.Text = r.TryGetProperty("localIp", out var ip) ? ip.GetString() ?? "—" : "—"; + DnsText.Text = ReadStringList(r, "adapterDns"); + + int ula = r.TryGetProperty("ula", out var ue) && ue.ValueKind == JsonValueKind.Array ? ue.GetArrayLength() : 0; + int nrpt = r.TryGetProperty("nrpt", out var ne) && ne.ValueKind == JsonValueKind.Array ? ne.GetArrayLength() : 0; + int wd = r.TryGetProperty("winDivertCount", out var we) && we.TryGetInt32(out var wdv) ? wdv : 0; + int broad = r.TryGetProperty("broadRouteCount", out var be) && be.TryGetInt32(out var bv) ? bv : 0; + + OwnedText.Text = $"ULA {ula} · NRPT {nrpt} · DPI {wd}"; + if (broad > 0 && r.TryGetProperty("broadRoutes", out var routes) && routes.ValueKind == JsonValueKind.Array) + { + var owners = routes.EnumerateArray() + .Select(x => + { + if (x.TryGetProperty("description", out var de) && !string.IsNullOrWhiteSpace(de.GetString())) return de.GetString(); + return x.TryGetProperty("interface", out var ie) ? ie.GetString() : null; + }) + .Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(); + var suffix = string.Join(", ", owners!); + RouteText.Text = string.IsNullOrWhiteSpace(suffix) ? $"External tunnel routes: {broad}" : $"External tunnel routes: {broad} · {suffix}"; + } + else RouteText.Text = "External tunnel routes: 0"; + + bool ownedHealthy = r.TryGetProperty("ownedResourcesHealthy", out var oh) && oh.GetBoolean(); + YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy"; + AdultSiteText.Text = adultCheckEnabled ? (mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy") : "Disabled"; + if (ownedHealthy && mode == "ACTIVE" && !string.IsNullOrWhiteSpace(IpText.Text) && IpText.Text != "—") + { + var ytTask = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "200", "204" }); + var oaTask = ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" }); + var ghTask = ProbeAsync("https://github.com/", IpText.Text, new[] { "200", "301", "302" }); + var geminiTask = ProbeAsync("https://gemini.google.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }); + Task[]? adultTasks = adultCheckEnabled + ? new[] + { + ProbeAsync("https://www.pornhub.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), + ProbeAsync("https://www.xvideos.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), + ProbeAsync("https://www.xnxx.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), + ProbeAsync("https://xhamster.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }) + } + : null; + var tasks = new List { ytTask, oaTask, ghTask, geminiTask }; + if (adultTasks is not null) tasks.AddRange(adultTasks); + await Task.WhenAll(tasks); + YoutubeText.Text = FmtHttp(await ytTask); + OpenAiText.Text = FmtOpenAiApiProbe(await oaTask); + GithubText.Text = FmtHttp(await ghTask); + GeminiText.Text = FmtHttp(await geminiTask); + AdultSiteText.Text = adultTasks is null ? "Disabled" : FmtAdult(await Task.WhenAll(adultTasks)); + } + + HealthText.Text = mode switch + { + "ACTIVE" => "Direct path healthy", + "CONFLICT" => "External VPN/tunnel conflict", + "BLOCKED" => "Disconnect external VPN/tunnel to Start", + "DEGRADED" => "Recovery required", + "OFF" => "Ready", + _ => "Unknown state" + }; + if (!string.IsNullOrWhiteSpace(reason)) ToolTip = reason; + + StartButton.IsEnabled = mode == "OFF"; + StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED"; + RecoveryButton.IsEnabled = mode is "DEGRADED" or "CONFLICT"; + RefreshButton.IsEnabled = true; + } + catch (Exception ex) + { + HealthText.Text = "Status error"; + StatusBadge.Text = "ERROR"; + StatusBadge.Foreground = bad; + YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = ex.Message; + AdultSiteText.Text = adultCheckEnabled ? ex.Message : "Disabled"; + StartButton.IsEnabled = false; + StopButton.IsEnabled = false; + RecoveryButton.IsEnabled = true; + RefreshButton.IsEnabled = true; + } + } + + sealed record ActionResult(string Phase, int? ExitCode, string Message); + + async Task WaitForActionAsync(string action, DateTime previousWriteUtc, int timeoutMs = 130000) + { + var until = DateTime.UtcNow.AddMilliseconds(timeoutMs); + while (DateTime.UtcNow < until) + { + try + { + if (File.Exists(actionStatusPath) && File.GetLastWriteTimeUtc(actionStatusPath) > previousWriteUtc) + { + using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(actionStatusPath)); + var root = doc.RootElement; + var actual = root.TryGetProperty("action", out var ae) ? ae.GetString() ?? "" : ""; + var phase = root.TryGetProperty("phase", out var pe) ? pe.GetString() ?? "" : ""; + if (actual.Equals(action, StringComparison.OrdinalIgnoreCase) && + phase is "done" or "error" or "busy" or "handoff") + { + int? exit = root.TryGetProperty("exitCode", out var ee) && ee.ValueKind == JsonValueKind.Number ? ee.GetInt32() : null; + var message = root.TryGetProperty("message", out var me) ? me.GetString() ?? "" : ""; + return new ActionResult(phase, exit, message); + } + } + } + catch (IOException) { } + catch (JsonException) { } + await Task.Delay(200); + } + throw new TimeoutException($"{action} did not complete within {timeoutMs / 1000} seconds."); + } + + async Task RunActionAsync(string action, string label) + { + SetBusy(true, label + "…"); + try + { + var previousWriteUtc = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; + ServiceClient.Send(action); + var result = await WaitForActionAsync(action, previousWriteUtc); + if (result.Phase != "done" || result.ExitCode != 0) + throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) + ? $"{label} failed ({result.ExitCode?.ToString() ?? result.Phase})." + : result.Message); + } + catch (Exception ex) + { + HealthText.Text = ex.Message; + } + finally + { + await RefreshStatusAsync(); + SetBusy(false, ""); + } + } + + void SetBusy(bool busy, string message) + { + if (busy) + { + StartButton.IsEnabled = StopButton.IsEnabled = RefreshButton.IsEnabled = RecoveryButton.IsEnabled = UpdateButton.IsEnabled = false; + HealthText.Text = message; + } + else + { + UpdateButton.IsEnabled = true; + } + } + + async Task CheckForUpdatesAsync(bool userInitiated) + { + try + { + UpdateButton.IsEnabled = false; + UpdateButton.Content = "Checking…"; + availableUpdate = await UpdateClient.CheckAsync(CurrentVersion); + if (availableUpdate is null) + { + UpdateButton.Content = "Up to date"; + if (userInitiated) HealthText.Text = "No newer release is available"; + } + else + { + UpdateButton.Content = $"Update v{availableUpdate.Version}"; + ToolTip = $"Verified update available: {availableUpdate.Tag}"; + } + } + catch (Exception ex) + { + UpdateButton.Content = "Check Update"; + if (userInitiated) HealthText.Text = "Update check failed: " + ex.Message; + } + finally + { + UpdateButton.IsEnabled = true; + } + } + + async void Update_Click(object sender, RoutedEventArgs e) + { + if (availableUpdate is null) + { + await CheckForUpdatesAsync(true); + if (availableUpdate is null) return; + } + + try + { + SetBusy(true, $"Preparing direct update to v{availableUpdate.Version}…"); + + var recoveryWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; + ServiceClient.Send("recovery"); + var recovery = await WaitForActionAsync("recovery", recoveryWrite); + if (recovery.Phase != "done" || recovery.ExitCode != 0) + throw new InvalidOperationException(string.IsNullOrWhiteSpace(recovery.Message) + ? "Unable to prepare a clean OFF state for update." + : recovery.Message); + + HealthText.Text = $"Downloading and verifying v{availableUpdate.Version}…"; + var updateWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; + ServiceClient.Send("update"); + var result = await WaitForActionAsync("update", updateWrite, 300000); + + if (result.Phase == "handoff" && result.ExitCode == 0) + { + HealthText.Text = result.Message; + await Task.Delay(600); + Close(); + return; + } + + if (result.Phase == "done" && result.ExitCode == 0) + { + availableUpdate = null; + HealthText.Text = result.Message; + UpdateButton.Content = "Up to date"; + SetBusy(false, ""); + return; + } + + throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) + ? $"Direct update failed ({result.ExitCode?.ToString() ?? result.Phase})." + : result.Message); + } + catch (Exception ex) + { + HealthText.Text = "Update failed: " + ex.Message; + SetBusy(false, ""); + } + } + + async void AdultSiteCheckToggle_Changed(object sender, RoutedEventArgs e) + { + adultCheckEnabled = AdultSiteCheckToggle.IsChecked == true; + try + { + SaveAdultSiteCheckSetting(); + if (adultCheckEnabled) + { + AdultSiteText.Text = "Syncing coverage catalog…"; + var info = await EnsureAdultCatalogAsync(true); + AdultSiteText.Text = $"Enabled · {info.Count:N0} domains · Stop/Start to apply"; + } + else + { + AdultSiteText.Text = "Disabled"; + } + } + catch (Exception ex) + { + AdultSiteText.Text = adultCheckEnabled + ? "Fallback coverage enabled · catalog sync failed: " + ex.Message + : "Settings error: " + ex.Message; + } + await RefreshStatusAsync(); + } + + static string? NormalizeCustomSite(string raw) + { + var value = (raw ?? "").Trim(); + if (string.IsNullOrWhiteSpace(value) || value.StartsWith('#')) return null; + var exact = value.StartsWith('^'); + if (exact) value = value[1..].Trim(); + string host; + if (Uri.TryCreate(value, UriKind.Absolute, out var absolute) && !string.IsNullOrWhiteSpace(absolute.Host)) + host = absolute.Host; + else if (Uri.TryCreate("https://" + value, UriKind.Absolute, out var implied) && !string.IsNullOrWhiteSpace(implied.Host)) + host = implied.Host; + else + return null; + host = host.Trim('.').ToLowerInvariant(); + if (host.Length is < 3 or > 253 || !host.Contains('.')) return null; + if (host.Split('.').Any(x => !domainLabel.IsMatch(x))) return null; + return exact ? "^" + host : host; + } + + void CustomSites_Click(object sender, RoutedEventArgs e) + { + var editor = new Window + { + Title = "Custom Sites", Owner = this, Width = 570, Height = 470, MinWidth = 470, MinHeight = 360, + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) + }; + var root = new DockPanel { Margin = new Thickness(18) }; + var note = new TextBlock + { + Text = "One site or URL per line. The saved domain and its subdomains use the existing Direct Method on the next Start. Prefix ^ for exact-host only. Maximum 256 entries.", + TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,12) + }; + DockPanel.SetDock(note, Dock.Top); root.Children.Add(note); + var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0,12,0,0) }; + DockPanel.SetDock(buttons, Dock.Bottom); + var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; + var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; + buttons.Children.Add(save); buttons.Children.Add(cancel); root.Children.Add(buttons); + var box = new TextBox + { + AcceptsReturn = true, TextWrapping = TextWrapping.NoWrap, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, FontFamily = new FontFamily("Consolas") + }; + try { if (File.Exists(customHostsPath)) box.Text = File.ReadAllText(customHostsPath); } catch { } + root.Children.Add(box); editor.Content = root; + save.Click += (_, _) => + { + try + { + var values = box.Text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None) + .Select(NormalizeCustomSite).Where(x => !string.IsNullOrWhiteSpace(x)) + .Distinct(StringComparer.OrdinalIgnoreCase).Take(256).ToArray(); + var dir = Path.GetDirectoryName(customHostsPath)!; Directory.CreateDirectory(dir); + var tmp = customHostsPath + ".tmp"; + File.WriteAllText(tmp, string.Join(Environment.NewLine, values!) + (values.Length > 0 ? Environment.NewLine : ""), new System.Text.UTF8Encoding(false)); + File.Move(tmp, customHostsPath, true); + editor.DialogResult = true; HealthText.Text = $"Custom sites saved: {values.Length}. Stop/Start to apply."; editor.Close(); + } + catch (Exception ex) + { + MessageBox.Show(editor, "Unable to save custom sites: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); + } + }; + editor.ShowDialog(); + } + void Strategy_Click(object sender, RoutedEventArgs e) + { + var current = "balanced"; + var allSites = false; + try + { + if (File.Exists(strategyPath)) + { + var raw = File.ReadAllText(strategyPath).Trim().ToLowerInvariant(); + if (raw is "balanced" or "compatibility" or "strong") current = raw; + } + } + catch { } + try + { + if (File.Exists(scopePath)) allSites = File.ReadAllText(scopePath).Trim().Equals("all-sites", StringComparison.OrdinalIgnoreCase); + } + catch { } + + var editor = new Window + { + Title = "Direct Strategy", Owner = this, Width = 540, Height = 390, ResizeMode = ResizeMode.NoResize, + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) + }; + var root = new StackPanel { Margin = new Thickness(20) }; + root.Children.Add(new TextBlock + { + Text = "Choose the packet strategy used for built-in and Custom Sites. Balanced is the default. Compatibility uses split-only TCP handling. Strong is more aggressive and should be used only when Balanced still fails.", + TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,14) + }); + var combo = new ComboBox { MinWidth = 230, HorizontalAlignment = HorizontalAlignment.Left }; + combo.Items.Add("Balanced"); combo.Items.Add("Compatibility"); combo.Items.Add("Strong"); + combo.SelectedIndex = current switch { "compatibility" => 1, "strong" => 2, _ => 0 }; + root.Children.Add(combo); + var allSitesCheck = new CheckBox + { + Content = "Apply DPI strategy to all web sites (experimental)", + IsChecked = allSites, Foreground = Brushes.White, Margin = new Thickness(0,14,0,0) + }; + root.Children.Add(allSitesCheck); + root.Children.Add(new TextBlock + { + Text = "Targeted mode affects only built-in and Custom Sites. All Sites mode can help unknown blocked domains, but may reduce compatibility or speed on some sites. Neither mode creates a VPN, proxy, or default-route tunnel.", + TextWrapping = TextWrapping.Wrap, Foreground = neutral, Margin = new Thickness(0,12,0,16) + }); + var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right }; + var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; + var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; + buttons.Children.Add(cancel); buttons.Children.Add(save); root.Children.Add(buttons); editor.Content = root; + save.Click += (_, _) => + { + try + { + var value = combo.SelectedIndex switch { 1 => "compatibility", 2 => "strong", _ => "balanced" }; + var dir = Path.GetDirectoryName(strategyPath)!; Directory.CreateDirectory(dir); + var tmp = strategyPath + ".tmp"; + File.WriteAllText(tmp, value + Environment.NewLine, new System.Text.UTF8Encoding(false)); + File.Move(tmp, strategyPath, true); + var scopeValue = allSitesCheck.IsChecked == true ? "all-sites" : "targeted"; + var scopeTmp = scopePath + ".tmp"; + File.WriteAllText(scopeTmp, scopeValue + Environment.NewLine, new System.Text.UTF8Encoding(false)); + File.Move(scopeTmp, scopePath, true); + HealthText.Text = $"Strategy saved: {value}; scope: {scopeValue}. Stop/Start to apply."; + editor.DialogResult = true; editor.Close(); + } + catch (Exception ex) + { + MessageBox.Show(editor, "Unable to save strategy: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); + } + }; + editor.ShowDialog(); + } + + void RouterGateway_Click(object sender, RoutedEventArgs e) + { + var w = new RouterGatewayWindow { Owner = this }; + w.ShowDialog(); + } + + async void Start_Click(object sender, RoutedEventArgs e) => await RunActionAsync("start", "Start"); + async void Stop_Click(object sender, RoutedEventArgs e) => await RunActionAsync("stop", "Stop"); + async void Recovery_Click(object sender, RoutedEventArgs e) => await RunActionAsync("recovery", "Recovery"); + async void Refresh_Click(object sender, RoutedEventArgs e) => await RefreshStatusAsync(); +} diff --git a/windows/installer/DirectInternetMethod.iss b/windows/installer/DirectInternetMethod.iss index f84fde8c..57288340 100644 --- a/windows/installer/DirectInternetMethod.iss +++ b/windows/installer/DirectInternetMethod.iss @@ -1,5 +1,5 @@ #define MyAppName "Direct Internet Method" -#define MyAppVersion "1.5.4" +#define MyAppVersion "1.5.5-rc1" #define MyAppPublisher "Direct Internet Method" #define MyAppExeName "DirectInternetMethod.exe" @@ -7,6 +7,7 @@ AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB} AppName={#MyAppName} AppVersion={#MyAppVersion} +VersionInfoVersion=1.5.5.0 AppPublisher={#MyAppPublisher} DefaultDirName={localappdata}\Programs\DirectInternetMethod UsePreviousAppDir=yes @@ -18,7 +19,7 @@ UninstallLogMode=overwrite DefaultGroupName=Direct Internet Method DisableProgramGroupPage=yes OutputDir=..\..\delivery -OutputBaseFilename=DirectInternetMethod_1.5.4_Windows_Setup +OutputBaseFilename=DirectInternetMethod_1.5.5-rc1_Windows_Setup Compression=lzma2/max SolidCompression=yes WizardStyle=modern diff --git a/windows/manifest.json b/windows/manifest.json index 8154cc08..35d6b7d1 100644 --- a/windows/manifest.json +++ b/windows/manifest.json @@ -1,4786 +1,4786 @@ -{ - "schema": 2, - "product": "Direct Internet Method", - "version": "1.5.4", - "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", - "offlineRuntime": true, - "normalRunRequiresAdmin": false, - "installOrPrivilegedUpgradeRequiresAdmin": true, - "directUpdate": { - "transport": "GitHub latest release HTTPS", - "privilegeModel": "DirectInternetMethodSvc performs download/verification/installer handoff", - "userUacRequired": false, - "integrity": "GitHub asset SHA-256 digest must equal SHA256SUMS.txt and downloaded installer SHA-256" - }, - "bundledPowerShellVersion": "7.6.6", - "bundledPowerShellArchiveSha256": "02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860", - "files": [ - { - "scope": "user", - "file": "app/DirectInternetMethod.exe", - "source": "app/DirectInternetMethod.exe", - "bytes": 139900022, - "sha256": "BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F" - }, - { - "scope": "user", - "file": "app/DirectInternetMethod.ico", - "source": "app/DirectInternetMethod.ico", - "bytes": 33478, - "sha256": "00D25A881AB5CA731437F5C631C031FD0FC0A8223F45181ACB6240EAA28AB01F" - }, - { - "scope": "user", - "file": "app/Status.ps1", - "source": "app/Status.ps1", - "bytes": 6394, - "sha256": "EC4B00233C1A0BD462D7AD674E54254F02BF462A6D3D53ACED47888CEE4A3447" - }, - { - "scope": "user", - "file": "app/router_gateway/providers.json", - "source": "../router_gateway/providers.json", - "bytes": 7111, - "sha256": "D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD" - }, - { - "scope": "user", - "file": "docs/README-FA.md", - "source": "docs/README-FA.md", - "bytes": 2191, - "sha256": "81C4242367FB6E15EBB2E03F0B4424EFD173091A46FE724AAEB1E9F22DD1B491" - }, - { - "scope": "user", - "file": "docs/ARCHITECTURE.md", - "source": "docs/ARCHITECTURE.md", - "bytes": 1863, - "sha256": "E4AC239D27A060B5CA0EF55C46B99FC9196725B44E07AB16F67C35E162C86BB8" - }, - { - "scope": "user", - "file": "RELEASE.json", - "source": "RELEASE.json", - "bytes": 5575, - "sha256": "51F54DC6761078C25A0913379B9B33315E317D96A992196C98DCA6DAF5B62B48" - }, - { - "scope": "privileged", - "file": "app/DirectInternetMethod.Service.exe", - "source": "app/DirectInternetMethod.Service.exe", - "bytes": 73627459, - "sha256": "2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41" - }, - { - "scope": "privileged", - "file": "app/Start-Direct.ps1", - "source": "app/Start-Direct.ps1", - "bytes": 26860, - "sha256": "B186872EDE733AE876FF1463E030988E9149A2CC88188F4B6C1FA5B564AEFC05" - }, - { - "scope": "privileged", - "file": "app/Stop-Direct.ps1", - "source": "app/Stop-Direct.ps1", - "bytes": 10426, - "sha256": "4E113237768506F940870CA7550EB3DD6A1B41C51879DD01D944A5E77BD96327" - }, - { - "scope": "privileged", - "file": "app/Recovery.ps1", - "source": "app/Recovery.ps1", - "bytes": 6157, - "sha256": "A406F1B23EF95C77C6B2FB4ECDB6337E582AB06D9A7927DEB31E916F06C870E1" - }, - { - "scope": "privileged", - "file": "app/SafeLegacyProviderRollback.ps1", - "source": "app/SafeLegacyProviderRollback.ps1", - "bytes": 13397, - "sha256": "522D78CB2A40A917B4E86DD7F98F4D3B076FD8A551B97C4B0C211FDFC7D9B322" - }, - { - "scope": "privileged", - "file": "bin/ctrld/ctrld.exe", - "source": "bin/ctrld/ctrld.exe", - "bytes": 7602136, - "sha256": "FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD" - }, - { - "scope": "privileged", - "file": "bin/ctrld/ctrld.toml", - "source": "bin/ctrld/ctrld.toml", - "bytes": 540, - "sha256": "B056AD8A51078208AED39744D48C4F4369AE5BA1E76F7D6F84B027C34CE1314B" - }, - { - "scope": "privileged", - "file": "bin/ctrld/LICENSE.txt", - "source": "bin/ctrld/LICENSE.txt", - "bytes": 1122, - "sha256": "5B166F4937F5D1ADED9C6D90675524ABAB6A6F0EBA1E226FA30119F37DBAB754" - }, - { - "scope": "privileged", - "file": "bin/zapret/winws.exe", - "source": "bin/zapret/winws.exe", - "bytes": 223232, - "sha256": "A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8" - }, - { - "scope": "privileged", - "file": "bin/zapret/WinDivert.dll", - "source": "bin/zapret/WinDivert.dll", - "bytes": 47616, - "sha256": "C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2" - }, - { - "scope": "privileged", - "file": "bin/zapret/WinDivert64.sys", - "source": "bin/zapret/WinDivert64.sys", - "bytes": 94144, - "sha256": "8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2" - }, - { - "scope": "privileged", - "file": "bin/zapret/cygwin1.dll", - "source": "bin/zapret/cygwin1.dll", - "bytes": 2954293, - "sha256": "103104A52E5293CE418944725DF19E2BF81AD9269B9A120D71D39028E821499B" - }, - { - "scope": "privileged", - "file": "bin/zapret/hosts.txt", - "source": "bin/zapret/hosts.txt", - "bytes": 287, - "sha256": "48EF43491901AB4336DDDFE6324C5820AC8B90F59511F138A7202DBCC8B033A3" - }, - { - "scope": "privileged", - "file": "bin/zapret/adult-fallback-hosts.txt", - "source": "bin/zapret/adult-fallback-hosts.txt", - "bytes": 448, - "sha256": "25BA72F6D56EF1C405115C7AA5D6ED1985E8F6D56F306A95D5AD800602A75288" - }, - { - "scope": "privileged", - "file": "bin/zapret/strong-override-hosts.txt", - "source": "bin/zapret/strong-override-hosts.txt", - "bytes": 99, - "sha256": "A371B754B201D1E87A9C0474BDEA1821539D7DAF20B5DA6C8F5105A1F85C8506" - }, - { - "scope": "privileged", - "file": "bin/zapret/LICENSE.txt", - "source": "bin/zapret/LICENSE.txt", - "bytes": 1090, - "sha256": "1FACB290FF6425CF6AB61FC882E71FFB0DF50FDA527FEF90D4098935097BD984" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Accessibility.dll", - "source": "vendor/pwsh/Accessibility.dll", - "bytes": 20816, - "sha256": "7E2CA83155D9F4B80909036B964461E1D020D25E107E378161AE7A16F52102A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/build.manifest", - "source": "vendor/pwsh/build.manifest", - "bytes": 3874, - "sha256": "58A354C02F0674CF951CEFF4E780F7532EAEE9C814B1FC75BDD3B943DD109F7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/build.manifest.sig", - "source": "vendor/pwsh/build.manifest.sig", - "bytes": 3277, - "sha256": "5E8F9EBCEE16A53E65BF204C4CFF40CF137D567229143126C9BA789E5B75FCA1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clretwrc.dll", - "source": "vendor/pwsh/clretwrc.dll", - "bytes": 317776, - "sha256": "557B5E6D5CC6C0BAECA172B987B023BEDB8B0C0C0ADF1352186768A926F671DD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clrgc.dll", - "source": "vendor/pwsh/clrgc.dll", - "bytes": 674088, - "sha256": "1AD35FC3FC5D8E26E007CFA21377579C748F1EB78BC7AC820813B505398E6403" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clrgcexp.dll", - "source": "vendor/pwsh/clrgcexp.dll", - "bytes": 711464, - "sha256": "2271063DED2A3A5A520380147300B483CA30403F0ACADCF03BAF8FBA0B53E23F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clrjit.dll", - "source": "vendor/pwsh/clrjit.dll", - "bytes": 2092328, - "sha256": "8B3E57D21CC9914A0F2DF5DF3B0FA5FC426D91282846EB88DA07736CFB7416AA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/coreclr.dll", - "source": "vendor/pwsh/coreclr.dll", - "bytes": 4614952, - "sha256": "20EA763D5AD8BE8656A3123C4AD5AFDB4C714A009BFC63BB8F4906D6678130DB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/createdump.exe", - "source": "vendor/pwsh/createdump.exe", - "bytes": 71512, - "sha256": "6A1B3A1AF1AFC2B756E2E45C36A3AC0D05D3134B14D8C66BCCFDBCC1F308B4E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/D3DCompiler_47_cor3.dll", - "source": "vendor/pwsh/D3DCompiler_47_cor3.dll", - "bytes": 4751568, - "sha256": "2D3B1B3057234778DCEBA837FF082355E6F6625AA5B68C4E69DCDF21106CEB4D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/DirectWriteForwarder.dll", - "source": "vendor/pwsh/DirectWriteForwarder.dll", - "bytes": 505640, - "sha256": "0384825B584E93D197040E7199E1C3D990190BBEDDBF65F327282D1CDA3FAE7F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/en-US/default.help.txt", - "source": "vendor/pwsh/en-US/default.help.txt", - "bytes": 3420, - "sha256": "5AD33AD117ECD78AD5E84AD745E69D45E6A6A10E68A0C7333C2CE95E102FD602" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/getfilesiginforedist.dll", - "source": "vendor/pwsh/getfilesiginforedist.dll", - "bytes": 67104, - "sha256": "153EEFB2EAFA8B2B909854CC1F941350EFB1170E179A299DE8836B8EC5CE6A7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/getfilesiginforedistwrapper.dll", - "source": "vendor/pwsh/getfilesiginforedistwrapper.dll", - "bytes": 30528, - "sha256": "7643100BB96726F8603975EAC7074174BF06F05AC3351CF8FCE0B8AAEF90ADD0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/hostfxr.dll", - "source": "vendor/pwsh/hostfxr.dll", - "bytes": 379688, - "sha256": "6D2D8B595846BFCBB5EF7B1CB1A3156AFF4930D2002B6AC813910B848C918855" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/hostpolicy.dll", - "source": "vendor/pwsh/hostpolicy.dll", - "bytes": 378664, - "sha256": "400E8547169E8D4405ADFD79510AE429F76F7A580932AC8A602EF5D88E4FEB2E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Humanizer.dll", - "source": "vendor/pwsh/Humanizer.dll", - "bytes": 845656, - "sha256": "E6AD30F638466744B2F64006AB48E8712B35D95269C08B64E9C9E6044150B612" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Install-PowerShellRemoting.ps1", - "source": "vendor/pwsh/Install-PowerShellRemoting.ps1", - "bytes": 23219, - "sha256": "A7BB6A752B186E225AB5080F0E5D26CD8598D827322D5927444508AEE937E285" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/InstallPSCorePolicyDefinitions.ps1", - "source": "vendor/pwsh/InstallPSCorePolicyDefinitions.ps1", - "bytes": 17206, - "sha256": "0B392011A5B57C05B7A8974406B20F8654CE1B32E11972DB6BABEB2765AE8EF8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Json.More.dll", - "source": "vendor/pwsh/Json.More.dll", - "bytes": 145224, - "sha256": "A34CF6FDCA5028DAC2D51F97951A13AEC724F172718568708CB9EBE2767813E6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/JsonPointer.Net.dll", - "source": "vendor/pwsh/JsonPointer.Net.dll", - "bytes": 75592, - "sha256": "D570D76B007F563490074D4BA738904F2886F9BD8F44D4C3F365D6AE51A90311" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/JsonSchema.Net.dll", - "source": "vendor/pwsh/JsonSchema.Net.dll", - "bytes": 812848, - "sha256": "262C0DFCFE5E1B2E99BA4D915E00B18C1094195643B19F04559DFDA1A8218A23" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/LICENSE.txt", - "source": "vendor/pwsh/LICENSE.txt", - "bytes": 1095, - "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Markdig.Signed.dll", - "source": "vendor/pwsh/Markdig.Signed.dll", - "bytes": 1361728, - "sha256": "17E508A3B13A1F52C463D0126004095032E4F0614CC275628F55BA267D281F56" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.ApplicationInsights.dll", - "source": "vendor/pwsh/Microsoft.ApplicationInsights.dll", - "bytes": 923480, - "sha256": "EA8B0774AFFEEC16ADFD24FC55CECCE39F778CA55E89A2FF6B2B457AFCCF44BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", - "source": "vendor/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", - "bytes": 30536, - "sha256": "53029EFFB9F6426939C5B702B4B2B322E4A5ACD35CF4A1ACE8E90F38B5BDEC15" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.CodeAnalysis.CSharp.dll", - "source": "vendor/pwsh/Microsoft.CodeAnalysis.CSharp.dll", - "bytes": 18536312, - "sha256": "78533CFBF2E43A374E58F05EF2C495ED56BF93EAA63BE0EC3F2B35FC621D008E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.CodeAnalysis.dll", - "source": "vendor/pwsh/Microsoft.CodeAnalysis.dll", - "bytes": 8128336, - "sha256": "14856FD1CDB7F425323C82D276A9ADD64454EDF0325A28B588C9825838938200" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.CSharp.dll", - "source": "vendor/pwsh/Microsoft.CSharp.dll", - "bytes": 980776, - "sha256": "C412EB8721B14AFC5BAE7C8D26B4239D1DB11486F0192FA39C3A8B4F6F23A1D2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Extensions.ObjectPool.dll", - "source": "vendor/pwsh/Microsoft.Extensions.ObjectPool.dll", - "bytes": 42824, - "sha256": "68AB071B3E6031888BD4944C2CED33B4973CB4C04877D9202781F6BD41F23EAE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", - "source": "vendor/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", - "bytes": 493376, - "sha256": "3C3FEDB396CFB166288A4ED6726F3F7E9CC4057A1052830573BE4B3313146965" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/microsoft.management.infrastructure.dll", - "source": "vendor/pwsh/microsoft.management.infrastructure.dll", - "bytes": 309040, - "sha256": "A5138F7B4EA083FA297E744D26AF2254EE3B593186CAA750A6E4963C00F3D778" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/microsoft.management.infrastructure.native.dll", - "source": "vendor/pwsh/microsoft.management.infrastructure.native.dll", - "bytes": 362288, - "sha256": "FFA5E12A8A10634F88796D20711CA0585C42BB22652157A2D776965AEBD34EAE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", - "source": "vendor/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", - "bytes": 28192, - "sha256": "9BEE4E35576355156F00E2E47EF57AA2C8CA64390C112F083A772F2219026293" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", - "bytes": 333624, - "sha256": "50F7A79D52316975A92AAC3996248C4B842EE6CE5EB41A6D0B25667B4791E336" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Management.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Management.dll", - "bytes": 1124152, - "sha256": "556085D8CDCF6D442F0F59776DE3D74535BBFA9FACF33D1358D90BB948F6BC92" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Utility.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Utility.dll", - "bytes": 1652536, - "sha256": "D156B47A70718A54D1B5691D0FF58663D11270BC94911D870AB2895648C1057D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.ConsoleHost.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.ConsoleHost.dll", - "bytes": 603960, - "sha256": "456BE117016C7B7E474C66E48439C1917D4E9717C2EE247B6DD4E19B4B7A674A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", - "bytes": 231224, - "sha256": "9EF54EB74EDA1C44BB1E7B71218DE2887A0490A526A3BAC967DC319ED3A74D3A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll", - "bytes": 993048, - "sha256": "D0CAC79C0CAFC557A981B1B7F3AB79544E764056B5CAF9EAC16A0095009E421E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", - "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", - "bytes": 4904, - "sha256": "04EF1B37822130AE6AC35D2CA9CB5224C87FBEB3AD2B0C48F953C87B083CC26C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.MarkdownRender.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.MarkdownRender.dll", - "bytes": 55104, - "sha256": "08A7A5C37E857A0197C710B8D9B52A4269AFE12F00374C077389E3C7DBD584E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.SDK.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.SDK.dll", - "bytes": 142624, - "sha256": "8637EC9E121C43B3280ECAB0AD12DDC76424AE08EE6A84A47F10548BDF076232" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Security.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Security.dll", - "bytes": 345904, - "sha256": "2944F4A3EA39EE33FA783A751037CCC7DA4B4A6AEC57DB7F652FEB544BE5D082" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.VisualBasic.Core.dll", - "source": "vendor/pwsh/Microsoft.VisualBasic.Core.dll", - "bytes": 1193808, - "sha256": "788558DDB1196D021FE17CCEF9DA03CC1C8C95174CC7A6CD56D0049AE65C2227" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.VisualBasic.dll", - "source": "vendor/pwsh/Microsoft.VisualBasic.dll", - "bytes": 18728, - "sha256": "4B8D9F34CC136D4F5FE156D3C0CCFA526231D042BE68DD0F412388EF7E0191CE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.VisualBasic.Forms.dll", - "source": "vendor/pwsh/Microsoft.VisualBasic.Forms.dll", - "bytes": 247592, - "sha256": "44A8EBEBEDC5746BBC22411BF34A5C869E3428908DB2D12B89270D1C762264FE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.Primitives.dll", - "source": "vendor/pwsh/Microsoft.Win32.Primitives.dll", - "bytes": 15656, - "sha256": "0327272BF4555EBD266751DFB3AC42463C8C0F266CDFD352263D4C7AA8FBAF04" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.Registry.AccessControl.dll", - "source": "vendor/pwsh/Microsoft.Win32.Registry.AccessControl.dll", - "bytes": 34600, - "sha256": "EA85FD3A01751393A2C4EB8D816EE50A0443C96C91CA2AEAD1F2AA15C52939B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.Registry.dll", - "source": "vendor/pwsh/Microsoft.Win32.Registry.dll", - "bytes": 116520, - "sha256": "FAD9AF3A76A4B89BEE820381FDE9CE06DFAADC3D90859953503F4BE81C74EE31" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.SystemEvents.dll", - "source": "vendor/pwsh/Microsoft.Win32.SystemEvents.dll", - "bytes": 116520, - "sha256": "75DA025453C94EE0D959A8077ADC2A69B05E28B987BC5EDF93887DF6C1CB393E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.WSMan.Management.dll", - "source": "vendor/pwsh/Microsoft.WSMan.Management.dll", - "bytes": 542512, - "sha256": "B95BB3EBAA7F562BA3EEA65B78429E267819D65A71F5839ADFD498EA695BB09F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.WSMan.Runtime.dll", - "source": "vendor/pwsh/Microsoft.WSMan.Runtime.dll", - "bytes": 157472, - "sha256": "D51427C022C1CEB37DE10991BD712F491B15D9BF8E0D42A3270ABAC050DFA0E3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", - "source": "vendor/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", - "bytes": 15219, - "sha256": "5417424A8746E0024526C21D09DD309F8B04B25BE711F99DF4F1507E8F335D70" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", - "bytes": 12873, - "sha256": "1C4EE42E1FA99A2768FA8FEDDAADF70A84F8EA8F273234061A6781D41DA0FAD4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", - "bytes": 17109, - "sha256": "15C2C0874890E5C715AA513C744CA99803A2629466CBB3C62E41AE3CFC857D66" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", - "bytes": 14859, - "sha256": "95E193E52B66E19C8ACD007AB2958B5020B3FDFF52227162B09C5110F01E2685" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", - "bytes": 77022, - "sha256": "48F095C3A48F95AAB0714C06A6EFDB798C77F957948AAF02CAA16CF50692D2F7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", - "bytes": 18243, - "sha256": "01EE5A86A4E774DBBACD911B7CF9070C72C6EF47699C1B94D70C8DF5FA9F0FA9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", - "bytes": 20849, - "sha256": "BD36F3F1242E75599579D6E3F805AF77663767C4DBF3750413FC6794B2EA5D12" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", - "bytes": 20298, - "sha256": "DD35B80DA89FF364BBA431A26223DE85252B3320C06D6489C5ECAE33A4EA8320" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", - "bytes": 14887, - "sha256": "F511D03F0A312CB4C74D1FFAC6E777190A37FDF057388F0DA3AC0DE8CE9CCEFF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", - "bytes": 14721, - "sha256": "BB209A688D755B76351364FE9B4ABB224F5BF881D9281383C7D0A62ACCFFCE63" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", - "bytes": 16092, - "sha256": "4BFCB08F3F388A74BE6DA9B765585974BE9AA37EE4EEBFFE52FC1BF762C25DB8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", - "bytes": 12873, - "sha256": "882CEF83D74AD2226EC51479280913B0B6C919DA35BC7365438D6EC540FB1018" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", - "bytes": 429624, - "sha256": "0C85C8CC3856D5BEB91895D1A8E00F785453F2AA538D85EE9ACE030B537F0FC2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", - "bytes": 357944, - "sha256": "B960AB88C55A7EDF0FF7A659CBC88FDA2DC8219221D46B7FCB92245B8E8EEFB4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", - "bytes": 26696, - "sha256": "FD587EF95461251B8D4B3D493D77102120622B1FD32B87BB98E032E1365480A1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", - "bytes": 22048, - "sha256": "FA1FECF3C081A0EF366100169CD539AD03EE10CD18BCF89DE56E9CB8D3659D4F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", - "bytes": 45088, - "sha256": "152BEBA2D6F82210F17696B2BE9498ADC9C38D32B9E2FD03BA87810B838F811D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", - "bytes": 44064, - "sha256": "7C6BC24A7D6A1C3C1FB4FC521174CF43E931A19F8BB88754FB738A6402848E20" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", - "bytes": 1664544, - "sha256": "924340C47CBC74B1B811059860DEFA3B136500BB97EF8B6DB95631F2391741D0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", - "bytes": 65568, - "sha256": "B084586684B03456481A6AA0AC0B149F87EBBC6FB01735D8005F987031BD0343" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", - "bytes": 18976, - "sha256": "7A77F7894D7F925C2E806737B8F4F036CD0D8B7E7843F824BA1139C10A0B3E25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", - "bytes": 732328, - "sha256": "1DC3D1DB10368906573D443667CED170FD7A01F3349D93906A8B60F1D914B6BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", - "bytes": 613824, - "sha256": "762988B531247A8AC282B85E0BC81004FA9D02B9118A24C3B18B3A97F1DADE48" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", - "bytes": 122800, - "sha256": "E8932AC0C5D7E306551BC4B585F582BAEF7651F21C88D4976445C0BFDF29F227" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", - "bytes": 170424, - "sha256": "0BB37C45575CBCCCC279A4250FB7F0D87DE5C0A3DDC16565542A6ED0E5682C6B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", - "bytes": 67616, - "sha256": "E5F44DDC086A8F40989C70460E22E25095D75AC402FAF6FF436E6A08C9CEC71F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", - "bytes": 93728, - "sha256": "077951FEA107ED42180AA5EAAA0BE047070A0EFF32A62999A8280535CB5D4F3D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", - "bytes": 131104, - "sha256": "C79E07959989FE7E99F31171EF712F9665474E652B4C16BC729160114697F8D4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", - "bytes": 56760, - "sha256": "EAF71D1D5DD771DAC1510C8779C2FDC98D539C8C07934EE114679D453FA60C21" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", - "bytes": 689088, - "sha256": "877E82582D7802ACA0B2FD53D34175B42D01BCF713A77D6D0A9E8A73C47EE2A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", - "bytes": 219056, - "sha256": "275A531DE109D62F71D745DB656D295511DD53CF0FD141CC0153AD357BFC8163" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", - "bytes": 786368, - "sha256": "AB3FCF87ABA83AC429FB040D5C36A4B7E9AF334C923F8F27041C93046D2AF9CB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", - "bytes": 65984, - "sha256": "77B3FAD9D3D369538F765035DD142D3D91785CE1308C40C5CA13BF84C9E78ECD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", - "bytes": 41240, - "sha256": "11568C79856612F9B5AF07FA8EF0A38E524F912D98C188E2510FD7E5ED520EC3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", - "bytes": 189104, - "sha256": "1887592615FC08BBC91AD62307C8C11DFC14C45BF120D271D66BE4A0D1A149A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", - "bytes": 28552, - "sha256": "E6026501AD4056FF2F1655B0AFDFE8923BC6E8FBAD67E1E9EF56E3002F49FBB9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", - "bytes": 31016, - "sha256": "6CF06482F4FD82AF659C2EB4B7BDEAE51EC171AB1CA3B3CBF0328C6A4B882242" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", - "bytes": 162624, - "sha256": "2728C8F0D153C72D3F50363FA1762536675C303F4CF4E7502EE3F099B3474554" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", - "bytes": 126152, - "sha256": "E5B1E3DEFA97A3AA29CC20BE0C9D525C86BB9C716F3B85F9A82CE1F30FF6310E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", - "bytes": 38424, - "sha256": "2154F3A7C647BD58C9DA47650E10CC42F906B7F7F1080211023917CA9684839F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", - "bytes": 33672, - "sha256": "1FDA491EEBDB19EA0A83CF6C16AB5DD004A1BFDFC845EDE017EBE0945BEB927F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", - "bytes": 23696, - "sha256": "A79AD1E9E7205BD790F40D6C1900BAF1A9C8CAA06089878D24ED3599AC3228A9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", - "bytes": 18312, - "sha256": "B4D8E15ADC235D0E858E39B5133E5D00A4BAA8C94F4F39E3B5E791B0F9C0C806" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", - "bytes": 99408, - "sha256": "A58026527BED9F3098CA3D016A7B6C93BD6F0134092F484B099970EC3D839AB9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", - "bytes": 644904, - "sha256": "0CF5A9763C98F09E94DFCDAA4437E9508EABE2E50B408BA421D5FAF734C5556A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", - "bytes": 25984, - "sha256": "4F81FFD0DC7204DB75AFC35EA4291769B07C440592F28894260EEA76626A23C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", - "bytes": 25232, - "sha256": "E905D102585B22C6DF04F219AF5CBDBFA7BC165979E9788B62DF6DCC165E10F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", - "bytes": 17567, - "sha256": "7429201A7A4BE49FEB0456DEDBD47E9E753DDA4EE871A8E6B977A0917F96745D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", - "bytes": 1095, - "sha256": "BE5723395260D4F07283D596F4B375810EB695653D48D52A5062C0F7070573A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", - "bytes": 433184, - "sha256": "C9ECCB0E5313CED30E7DA3903C27F1A97C6E8B0793A0A91160C4BEE346F3EE02" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", - "bytes": 138856, - "sha256": "A8890484E98D618F366A5AADCF34F9AB8D9D2C51AC08E7E4789E7C733D62B653" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", - "bytes": 20644, - "sha256": "13A0C3EA11F35E08DD1EE06A15EE9EE793062752EF5DA63B43E7C4BC64976328" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", - "bytes": 17978, - "sha256": "303A5B8DD3749DA6A7530E2F040DDC1ECFCCA280120DCEDA473EBB2285661CE8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", - "bytes": 158819, - "sha256": "CB3714C1F7FC609FEE9264C5F7E87DF89121D09242A4DCCEFE4815E9D784AA65" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", - "bytes": 23783, - "sha256": "097FE2BBE4C6BC7829C64114E46E51479574B5B9E4B2F84D148F29B2FAE0BE9A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", - "bytes": 1364, - "sha256": "1C8163F8163797C4B91466A94207E356256B8F770A0C591C68CD1FADD015A79A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", - "bytes": 1839, - "sha256": "37BCB0428C05343483E7B5AA2F76AD3F804BA87830526EC42E8F8A91736D4AA9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "bytes": 15415, - "sha256": "9CBBDDBC38465378D4FDF3529B74C39161B728EF295974B5B9D90FD5A87806C3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", - "bytes": 20141, - "sha256": "B226CC25C17CDD19CE028EA22B3B596EF9A3A8C8E3C58BF3A9E76E45D9F38514" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", - "bytes": 12812, - "sha256": "3DB2F058A8F27F3E7BE0E22E8258944F9A6412A379DFFC66F99F3E592899DEC4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", - "bytes": 19596, - "sha256": "6E5AA422BFDE0133D9FC12CAB32C404557CA7CDDDA1FF888BF69395595928003" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", - "bytes": 1095, - "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", - "bytes": 34840, - "sha256": "F89AB468EC1329474288FB0FAAD81600005BE3DA88DC2B8A2631CFBDD64F394A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", - "bytes": 18088, - "sha256": "4E9CB7A6D8A94560F0025B807162BAAA9C4A6AF4254D3FA7E71B23E0DCC1FB25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", - "bytes": 1979, - "sha256": "296FF3E4A93BC8C20CA5C20D0E1A70B8C4622FADF0351A37845A63ED52585279" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", - "bytes": 16830, - "sha256": "BB9BD6F3EAB1CF227E1AFAFEC89934E6EB10C6C8530908C6436849120F631D9C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", - "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", - "bytes": 15033, - "sha256": "8E06AA0AB008337C59AEBB32FF672BCD1646FA927DDF4E4EFE82AC454AAB30B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", - "bytes": 24000, - "sha256": "6A47415133889D1D676C6170DD14DC43C2C983DC6ADAFE73DC0F66891E5150FE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", - "bytes": 89504, - "sha256": "5A140EB9BFB2EBB42F02E75DA9A408A86BA6034EAA27AC57A10A534011EFD509" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", - "bytes": 72080, - "sha256": "50CB7269866EC2E3663221662FD1649A0C1B2736525D65231E29B39B25BB2467" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", - "bytes": 318352, - "sha256": "23689DD357312F0924305C612AA41BE0BD592AD9712FED70A3DA72A9C40EB712" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", - "bytes": 83864, - "sha256": "8F5BFD7FFFFE4930AD035194447B8FB24257C8C799302F04879CD5D5211A38C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", - "bytes": 290192, - "sha256": "C0579B5BCA0ADFE99DC17C253F4F00D669AD008A60854D2508BF6D79A886B4B2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", - "bytes": 189328, - "sha256": "10D3F4E3AA47CFC3AAC0E94C3B20000EA73CB0062E5037A79FCBE932112EB0A1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", - "bytes": 28469, - "sha256": "B7CD11228923E8747B04AE8A03F246F1490B4D51C8A54B41D26CCB20164FE558" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", - "bytes": 31676, - "sha256": "8D9E6819AF5265B47DC514F82EEEF657449C2F3DC72CE4F4F8531FC2FCCDECF0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", - "bytes": 31412, - "sha256": "50EE9087694A8325E0BC6FF4E5AE008984D46FF84CB9BCC6ED655CFF1A8547A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", - "bytes": 53524, - "sha256": "56BD33242E6D59F02514EB3B902F65B83A3EA8EA7E44099F57892AB640823C25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", - "bytes": 31566, - "sha256": "ACD658401A6A84F29E091F6549F4AD6BC6E16B71005F542E4BE558C40B50EAFA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", - "bytes": 32494, - "sha256": "AB2DD00EE1AB028EF4C506CAC98E5407FB866DA40720C232A3426ADBB57EA67A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", - "bytes": 24136, - "sha256": "CA8EA0F3E2208FDEDD51C2F282EA9E9A50BE7D8A38C56EEF0141191F7E484776" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", - "bytes": 31104, - "sha256": "24E1182E6F6E3EDDF2528EC6D335E0D584CE37995B0D849B5DD5F5EE75A558BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", - "bytes": 32443, - "sha256": "6CC6DA35A87357E213FA7A2CD0FAF9B1DB1BB83BDAAF9F73C952FE9AD2BB4596" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psd1", - "bytes": 17889, - "sha256": "EC633E756164C26D1154FAB722648E2737F3650399856D29C089F0C25B190D33" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psm1", - "bytes": 16422, - "sha256": "D484E31593FE26C8E670F8A0D72D4400A3E8EB75F587DF4B11554B8C000E128A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", - "bytes": 14984, - "sha256": "734BFFF81999A08B4145399A6284B00E46F070A7038BB4C82AB79165FF15F543" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", - "bytes": 25115, - "sha256": "481FDED09BF8A376EC4BD53CF34C0D66C8D8B1D31AB2012A768227F92B099156" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", - "bytes": 15939, - "sha256": "4081ABD182F805AB3B9705279478A2A8AF17A0E43F07887E3C68962BCD09C9E8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", - "bytes": 38882, - "sha256": "6C724F3B86D5D5233C2906FE41206D6EB2279B6D053164F51AAF83B4891619EB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", - "bytes": 1766, - "sha256": "CF126CC6FD7AFAEC54F07942F346F379725795625B80EC5FC32F547AB7B49BFC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", - "bytes": 14446, - "sha256": "2107051FAB99F1E8545CC30F72FB089899B19A5BD4D33A6891FBF0DF794F573A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", - "bytes": 14171, - "sha256": "7520000BE81F281384AF99B787A4629E55269EF73D8FA162CF29390DC273A017" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", - "bytes": 23645, - "sha256": "627EDB8150EC8C52B33E363652D27E4FD71C1374D03C5B56D98AF244D3C1CC10" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", - "bytes": 1578, - "sha256": "B9F9A62A3D2D0497BF81CC69A456B9E0BDBF9D1F34909A0283F088940EFFFC34" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", - "bytes": 14325, - "sha256": "6AA312F0A73494723FE4CAA98BD74CCF3724226BEE4BFE4EC0A7229BFAD7141C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", - "bytes": 59328, - "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", - "bytes": 20118, - "sha256": "14A35134461ABCFAA7DFE393C7B3A16605032719F5EA0EE062B4655A181C0C76" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", - "bytes": 15108, - "sha256": "1302F09514E514B4A2C5A2F8D4DFCFC5087070E211357319FB516E27994C88B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", - "bytes": 27387, - "sha256": "640240A2632C22E75AE372414BEE7136C2ADC932F92A0F656C436BE616B19CDA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", - "bytes": 23683, - "sha256": "B1315712ADE77AF6C5115D8C9AE2F9A5D8CCBFA5E9A5F00C19AB283A52057F2B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", - "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", - "bytes": 23680, - "sha256": "4F12DD41E0FAB7B1A97AE83A84BDDB567CE9876F9E6C3496D833CFBB8E52BD7F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", - "bytes": 59328, - "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PSModule.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/PSModule.psm1", - "bytes": 1368306, - "sha256": "573D4A3861CB967E52EAF6155A41F930FCD4168368A6946F65D985F30DC357EC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", - "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", - "bytes": 14978, - "sha256": "1DCFF05CD03BEC2F6117BB7267DADDB9770CB10FA6FE4AB9D892E87DA15349F7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", - "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", - "bytes": 26145, - "sha256": "2697A9EC7792940DA5F3D91CCAC24B1A3948CFF4E5901D5B98B7689A1C4A9DBF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/.signature.p7s", - "source": "vendor/pwsh/Modules/PSReadLine/.signature.p7s", - "bytes": 12873, - "sha256": "ECDF2C3BC44D32AF003FD81BEF96E83F19D7EFC9658C07F7D78687F105F721A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/Changes.txt", - "source": "vendor/pwsh/Modules/PSReadLine/Changes.txt", - "bytes": 41347, - "sha256": "E18C4D35A40A544877378B36B7D86B5C79DAED0C6E7A55C3027C4F0F998426C0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/License.txt", - "source": "vendor/pwsh/Modules/PSReadLine/License.txt", - "bytes": 1324, - "sha256": "25C2FDFCDC653F65629233F5EF217A83287733D3C0299C207B0BCA865508C463" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", - "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", - "bytes": 16784, - "sha256": "451994C3D3E38D939B4FA5F8594D0207840F45E7A210AA75F81AEF85AA17C592" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", - "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", - "bytes": 339528, - "sha256": "F8E3A5B7E3E8CAD2130CE10647564A2A0EA15D98DB8A0CC8D589F80154C108E2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "source": "vendor/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "bytes": 14920, - "sha256": "6D33EA289B405C64BF5DAA9C59E459596CAC59ECE399D0D215EE9F3D46AFD675" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "source": "vendor/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "bytes": 16928, - "sha256": "2E63DC86D9240243CCFB07DA5D0B4FCE2BC5CE5F9952658A7CE68C26BCD3E14C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", - "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", - "bytes": 26820, - "sha256": "1CA887463598D38AA18236A97A4193B2228D1D8434FA73CC8C7932F42A0565CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psd1", - "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psd1", - "bytes": 15471, - "sha256": "DD8766BD4DB0C1D17111B81D145D32448F61F48451D51C780A79BE9CA98D837E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psm1", - "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psm1", - "bytes": 15076, - "sha256": "9BC0252F616067CA5EF1D0328EC0B97C7D31C757F9FC80ADEE234EA6132A9C7E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", - "source": "vendor/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", - "bytes": 41594, - "sha256": "53B518B3D72AFEB6C8A3F4ABF079C93CEBAF7100A12C93668B83A4D4BCD45795" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscordaccore.dll", - "source": "vendor/pwsh/mscordaccore.dll", - "bytes": 1366744, - "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", - "source": "vendor/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", - "bytes": 1366744, - "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscordbi.dll", - "source": "vendor/pwsh/mscordbi.dll", - "bytes": 1247992, - "sha256": "1875AB683E30F79B39FF861F440A246561B72247C22366615B97CC14537B4808" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscorlib.dll", - "source": "vendor/pwsh/mscorlib.dll", - "bytes": 59688, - "sha256": "E7BE6C67FA82F5F0C10C6553D82CCC2854DF3B49CF8DA706629F80A34A1AE309" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscorrc.dll", - "source": "vendor/pwsh/mscorrc.dll", - "bytes": 133928, - "sha256": "11917AEAEC22F310A26A466E7D57CDCAB40F6ED1233B286E39AC675E881A7DAE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/msquic.dll", - "source": "vendor/pwsh/msquic.dll", - "bytes": 536344, - "sha256": "B7158D427B01C70483BB3D80C5211E387DD98E9471C8A70E4484D449DE9157E8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/netstandard.dll", - "source": "vendor/pwsh/netstandard.dll", - "bytes": 100648, - "sha256": "42388B4A19485ADC2A7D384F5257BB9D806F3BAC6F8871DDE42AFD944BBFC0C5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Newtonsoft.Json.dll", - "source": "vendor/pwsh/Newtonsoft.Json.dll", - "bytes": 1824584, - "sha256": "2B6FDDE54CC2D2E9277D458A42FDF9FCA30FC4299652E97144A31C90FEC91829" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PenImc_cor3.dll", - "source": "vendor/pwsh/PenImc_cor3.dll", - "bytes": 157992, - "sha256": "962CC2EB9DBBC45680649AC084685FB0E57DA4DE09D3DD5762188D10B76F598C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/powershell.config.json", - "source": "vendor/pwsh/powershell.config.json", - "bytes": 190, - "sha256": "98C0E5B6EE17EB8B8F4E4940C2B2528689CEC8470DB4BDE427AD16D90D6A52D4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShell.Core.Instrumentation.dll", - "source": "vendor/pwsh/PowerShell.Core.Instrumentation.dll", - "bytes": 106808, - "sha256": "AEEB8062AAFF1057E90E9485F610B508B1179AAC4DF7FC465090BDEE49E3CFF3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShell.Core.Instrumentation.man", - "source": "vendor/pwsh/PowerShell.Core.Instrumentation.man", - "bytes": 227126, - "sha256": "8B2585E50EDE697905DFDB8E3878DA2AB42039135BA23E377207CC8D96E8125F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.adml", - "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.adml", - "bytes": 9675, - "sha256": "2B8C4B7D5BD585431FBF918F223A9B932D80EB9703A1976A1CE3492D4DD26966" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.admx", - "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.admx", - "bytes": 6198, - "sha256": "EE8EB99C1E66009D95AF46E4F6C8F5C13563C9A0AD827E631149176A1B8D34D2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationCore.dll", - "source": "vendor/pwsh/PresentationCore.dll", - "bytes": 8324904, - "sha256": "7A610FD470016E5C74E2BFC403D2E08DDFF10F1EEFCBBF3AA7B07577855EE746" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemCore.dll", - "source": "vendor/pwsh/PresentationFramework-SystemCore.dll", - "bytes": 38696, - "sha256": "12DDD4029181D0AA27D32A69777760037A85F06BC57D56478669E098417E9990" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemData.dll", - "source": "vendor/pwsh/PresentationFramework-SystemData.dll", - "bytes": 34600, - "sha256": "0075922DB11FDE4BE1E8668BB9ABF5596DA8A40367E1D7264FE44B30B83F2228" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemDrawing.dll", - "source": "vendor/pwsh/PresentationFramework-SystemDrawing.dll", - "bytes": 34640, - "sha256": "953D322E29718C113DB240EB73396E65BA5B9AA55AD25C8A796B7BEB2E11573E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemXml.dll", - "source": "vendor/pwsh/PresentationFramework-SystemXml.dll", - "bytes": 34600, - "sha256": "EBDDF88BDA059E766772C7F3BB073C4660A48FE7CAB1238B971FE6DDBA4C01CA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemXmlLinq.dll", - "source": "vendor/pwsh/PresentationFramework-SystemXmlLinq.dll", - "bytes": 30504, - "sha256": "8D5290BF6787A161D2689C03C1936BC9D28CB22292197B2EED3A57ADD05DC81A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Aero.dll", - "source": "vendor/pwsh/PresentationFramework.Aero.dll", - "bytes": 440104, - "sha256": "D64069A4E059D5F74EFAE734541A5BC54B13E00A087B7D7A0CE224B4F9F7B3C3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Aero2.dll", - "source": "vendor/pwsh/PresentationFramework.Aero2.dll", - "bytes": 444200, - "sha256": "092E068D4F0117CF5020A072A53A3010EEB54F125DA3BC8D4EA1913A3370E4E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.AeroLite.dll", - "source": "vendor/pwsh/PresentationFramework.AeroLite.dll", - "bytes": 239440, - "sha256": "B7B8BF2214F9653CA2257D1A5E1DD0D978FAF15BFDE826F0ED3EE57B6A1A9A90" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Classic.dll", - "source": "vendor/pwsh/PresentationFramework.Classic.dll", - "bytes": 272168, - "sha256": "CB733EA566618B52415EB32FC32C86A3FE6BC915D471D63DED3A46AAF3071AEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.dll", - "source": "vendor/pwsh/PresentationFramework.dll", - "bytes": 15816528, - "sha256": "6A3C5B98ED693C577FF25827315D7EEE89F73DEDF0088B80B43B4AFB337018C4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Fluent.dll", - "source": "vendor/pwsh/PresentationFramework.Fluent.dll", - "bytes": 980816, - "sha256": "E33301B7F7A38B9A5065127F79EC3860A6827B30BD1C85A669E3C3AE191C4258" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Luna.dll", - "source": "vendor/pwsh/PresentationFramework.Luna.dll", - "bytes": 669480, - "sha256": "F9030C4D064B7F097B6F73FAA9C7683BEFB7A1A4C18C2D6DD741EB34A2325658" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Royale.dll", - "source": "vendor/pwsh/PresentationFramework.Royale.dll", - "bytes": 333648, - "sha256": "118F34B2213AFE8075BE78D66FDB5B5B0C099A89F70AEE9CA09A9DC09BF07102" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationNative_cor3.dll", - "source": "vendor/pwsh/PresentationNative_cor3.dll", - "bytes": 1235240, - "sha256": "42E2E543C17E1B37499BAF00D956433B8AFB655348EC0FAE89C926FB7D149B6E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationUI.dll", - "source": "vendor/pwsh/PresentationUI.dll", - "bytes": 1259304, - "sha256": "5DE46036ACAB98C8B970BFB4234F66438EE102039F9A59A72053A9818C3ED1F0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/preview/pwsh-preview.cmd", - "source": "vendor/pwsh/preview/pwsh-preview.cmd", - "bytes": 25, - "sha256": "7A24CB96B62009CFFB38C85B6ACBF48A5DB5746459B85FF3E727BD952FFD8832" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwrshplugin.dll", - "source": "vendor/pwsh/pwrshplugin.dll", - "bytes": 159032, - "sha256": "FBF2C8FC0D8A3CD5F2DEE9E0F15315F92862B6DF26BF1B02209588A07CC0D429" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.deps.json", - "source": "vendor/pwsh/pwsh.deps.json", - "bytes": 99499, - "sha256": "DFC3A3FB00F53720610F389E2525FB268928B381E55F899B84B478502D5736A2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.dll", - "source": "vendor/pwsh/pwsh.dll", - "bytes": 157472, - "sha256": "91F9892F15A7E2DCF440AFC3A497A7D839F9D38D1AB4A50BD36080D982A01449" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.exe", - "source": "vendor/pwsh/pwsh.exe", - "bytes": 301368, - "sha256": "BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.profile.dsc.resource.json", - "source": "vendor/pwsh/pwsh.profile.dsc.resource.json", - "bytes": 5024, - "sha256": "378030311FCDD7A8D6C401142275BAEB6C031307EC2E12F51399B84851906380" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.profile.resource.ps1", - "source": "vendor/pwsh/pwsh.profile.resource.ps1", - "bytes": 20443, - "sha256": "BA90CDBF60C48C05343A7C4A451BEC2A5A133DEB10F1F7CD763E76F5D2BA7944" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.runtimeconfig.json", - "source": "vendor/pwsh/pwsh.runtimeconfig.json", - "bytes": 790, - "sha256": "6C1779B71C67B5F40B2D0041BACAE0873320DD55F0DC3D5DF76D88C8D486275D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ReachFramework.dll", - "source": "vendor/pwsh/ReachFramework.dll", - "bytes": 1570600, - "sha256": "E0EE50A412ABA6DC2FAAD0655D5AEBCFF33587A113984CAC2777ED7B763BD193" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.CSharp.dll", - "source": "vendor/pwsh/ref/Microsoft.CSharp.dll", - "bytes": 18216, - "sha256": "AE0ECD8F42EA3EFACC2612ADE56F29FEEA9A123FCE9FAA455126EC9F4A4540CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.VisualBasic.Core.dll", - "source": "vendor/pwsh/ref/Microsoft.VisualBasic.Core.dll", - "bytes": 59216, - "sha256": "7A7E44D7AC1F870E0B7D8F3F7598CCA79E35CAB12E77C58ED46E9B6B3C78BCFB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.VisualBasic.dll", - "source": "vendor/pwsh/ref/Microsoft.VisualBasic.dll", - "bytes": 17192, - "sha256": "E192508ADDE6A758138A5A8E28998DCCB64E7F45EAF30D8AB3564F87749028B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.Win32.Primitives.dll", - "source": "vendor/pwsh/ref/Microsoft.Win32.Primitives.dll", - "bytes": 16168, - "sha256": "AB197DEC717C52F5A72C8F2366C33E2FC5149BFA7CF49ACD59BBFB563DEFCD05" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.Win32.Registry.dll", - "source": "vendor/pwsh/ref/Microsoft.Win32.Registry.dll", - "bytes": 21288, - "sha256": "E8EB7F780B9CECA6DAE35E74A11FA85E26A1B8B4A7EDD5237CCCC6B63B4E4B7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/mscorlib.dll", - "source": "vendor/pwsh/ref/mscorlib.dll", - "bytes": 55592, - "sha256": "894D95D82126559637417B4F14C48ED20BB1168B13A0E65BD1337CDC2A981D1B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/netstandard.dll", - "source": "vendor/pwsh/ref/netstandard.dll", - "bytes": 100648, - "sha256": "3DC81DBC9DACD28261632C94410859EDA24C2DB50CD6C14309CBB2A434B50C57" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.AppContext.dll", - "source": "vendor/pwsh/ref/System.AppContext.dll", - "bytes": 15144, - "sha256": "D9DD9FA44E019F27172D6B2662E2B6038F48C270912AC8AAE53D4D909DA7E9BE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Buffers.dll", - "source": "vendor/pwsh/ref/System.Buffers.dll", - "bytes": 15144, - "sha256": "A1A35F5524E2F831D08CA55F702FBDDCB8C6DFECD264F0F563D81A1C8B8280BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.Concurrent.dll", - "source": "vendor/pwsh/ref/System.Collections.Concurrent.dll", - "bytes": 27432, - "sha256": "27B446A401163BAA325F9CD82F787DFD32D0E205011093E0AB3057E5DA609555" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.dll", - "source": "vendor/pwsh/ref/System.Collections.dll", - "bytes": 55080, - "sha256": "130FCDD915C3FF8D8279FEFD9A198B784D37693F4C1E96D1F2CFF3EDC96B638D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.Immutable.dll", - "source": "vendor/pwsh/ref/System.Collections.Immutable.dll", - "bytes": 75048, - "sha256": "D4145735395753187965A6F0A8BF6BB6F301563C4D4080249614B8487B0604AC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.NonGeneric.dll", - "source": "vendor/pwsh/ref/System.Collections.NonGeneric.dll", - "bytes": 22864, - "sha256": "99ADF08652A16E5AF30797B98742851B94604AA44BAB677DDC38C7826BD529A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.Specialized.dll", - "source": "vendor/pwsh/ref/System.Collections.Specialized.dll", - "bytes": 25896, - "sha256": "17EF2B9433A1F1E9FC956786067F8D60672437D3FEB76AF84175F2B3BEE4DED1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.Annotations.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.Annotations.dll", - "bytes": 31568, - "sha256": "43C467051FEAFAA27144CC672836F38108377439245833FC8D99DE97D3667C37" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.DataAnnotations.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.DataAnnotations.dll", - "bytes": 16720, - "sha256": "D40E8C7062E6B9CE11BA83EAB3D26BE0EEC8066B99970EA88B8E87FB75F411BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.dll", - "bytes": 15656, - "sha256": "8811E7CC4288C435F54B0350AFBFFCBB13F77618CF9C0D3436CAD89A83D29B23" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", - "bytes": 19240, - "sha256": "B96D6D2F159EE5E192B2406A0EC993C8AD470A847F2E7ACD2AA92CBC0A4C6A58" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.Primitives.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.Primitives.dll", - "bytes": 25936, - "sha256": "09A575C38BD010F3131704E9294941FF9740A0BA70BE0FD6AB8FA54C44DBFCA7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.TypeConverter.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.TypeConverter.dll", - "bytes": 104744, - "sha256": "98A3E34140052C828AB7BDB81040421616FA4E6356C27F7970DF141EF5681B42" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Configuration.dll", - "source": "vendor/pwsh/ref/System.Configuration.dll", - "bytes": 19240, - "sha256": "3952A72E1F18774F16952B46C20E80ED2BC601A06ECA01F33FA924C738022A9C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Console.dll", - "source": "vendor/pwsh/ref/System.Console.dll", - "bytes": 26408, - "sha256": "783E5EA6D049218EC30316598654E031415F0D1EC6CE6932C2D1A9CBD97A77E0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Core.dll", - "source": "vendor/pwsh/ref/System.Core.dll", - "bytes": 23336, - "sha256": "AC23FAF1B37FA49F1B117BAAD951A65609E799A6E669A6DF06D3552677E0C086" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Data.Common.dll", - "source": "vendor/pwsh/ref/System.Data.Common.dll", - "bytes": 154448, - "sha256": "E563226DCE5E8D2F1ECF24B13A37D8948E42208D7B93BC9812475074382F2303" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Data.DataSetExtensions.dll", - "source": "vendor/pwsh/ref/System.Data.DataSetExtensions.dll", - "bytes": 15656, - "sha256": "AEEAE0755C8C7F6747765CDF82FA43182E320E97F2035E3A0A19F21905E6C113" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Data.dll", - "source": "vendor/pwsh/ref/System.Data.dll", - "bytes": 23848, - "sha256": "8BEE1D4675FEE975B5454E7407827AA3C6D4A2E53B9EFB40BE5409AC50900EDB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Contracts.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Contracts.dll", - "bytes": 19752, - "sha256": "4366CCD79A875FB64E38813A81BDF2F61915B4C838CB74572D87919C2E7DEE41" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Debug.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Debug.dll", - "bytes": 15656, - "sha256": "8B160FFE7518588BF64E13C01C6DCB2EF504D64F38BBF0F198D31E7B698DB92C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", - "bytes": 43304, - "sha256": "6CAA24C3FE3819A6F78AEDB0E8F718A6BD83EAB4D9A786C609A228E330557BB0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", - "bytes": 16720, - "sha256": "AE73589F3288FC3648A718797361F0AB37606CC18667C8AAEBEA2787F62736A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Process.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Process.dll", - "bytes": 31568, - "sha256": "CD121308CB25B34EBC2549D72DA361E56A093B29EF5AB0A85870D13FB91DE052" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.StackTrace.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.StackTrace.dll", - "bytes": 22824, - "sha256": "E287630ABE5753B658E64E947616C8BC8291BCD27BFCADC745A33818A43AD30D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", - "bytes": 17704, - "sha256": "2C567AD9A2EFE67D171E0AB06D0C7035383508588CAE62B3C3D08F579A064595" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Tools.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Tools.dll", - "bytes": 15144, - "sha256": "841C5E2B6C171FA9AAFF0FC6295F3FBC93DC5DCAB44118B62F1C942BB674AFF2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.TraceSource.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.TraceSource.dll", - "bytes": 27984, - "sha256": "ECFE5B354EA6B7BF3736D8C4B02AE322C766F2BCCF260182632A492F3309D883" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Tracing.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Tracing.dll", - "bytes": 28968, - "sha256": "E0F72905EB1B1CFD6DB130E6B2F18424788769D79FAE486054A242615D713A50" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.dll", - "source": "vendor/pwsh/ref/System.dll", - "bytes": 49448, - "sha256": "FF11E2AB9009EF141E315187254F8327DD1E2591E44B8BF158730869028F1F0E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Drawing.dll", - "source": "vendor/pwsh/ref/System.Drawing.dll", - "bytes": 20304, - "sha256": "585A8DA9952D715F8DE7A7B1879B59D68FF8608B92F6ED1C5F0E1D251180D3C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Drawing.Primitives.dll", - "source": "vendor/pwsh/ref/System.Drawing.Primitives.dll", - "bytes": 35616, - "sha256": "8FFDA23FAFB73BDAC7B6C8DFCAC1C06894C729F65157E218980DB9A4B74C7427" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Dynamic.Runtime.dll", - "source": "vendor/pwsh/ref/System.Dynamic.Runtime.dll", - "bytes": 16168, - "sha256": "C0EAC30C791DB8C1A2B50B61C50D029F4DDD0EADFDD35433745001688A3E7466" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Formats.Asn1.dll", - "source": "vendor/pwsh/ref/System.Formats.Asn1.dll", - "bytes": 26408, - "sha256": "C6F8CB70A20321AC4C93219CCB5264F50D21D7F7CDD0BD61C02360FF87CD38F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Formats.Tar.dll", - "source": "vendor/pwsh/ref/System.Formats.Tar.dll", - "bytes": 20264, - "sha256": "C6D0AA4D2E3ED3AB6C1949669D79BFC763FC9E7B04830AB7174D4CC55569757C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Globalization.Calendars.dll", - "source": "vendor/pwsh/ref/System.Globalization.Calendars.dll", - "bytes": 15656, - "sha256": "E0F3F3F6E4ACC7B6755782FED5CB075ECF70BEEE4FC5D404579AE99BC0883EF2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Globalization.dll", - "source": "vendor/pwsh/ref/System.Globalization.dll", - "bytes": 15696, - "sha256": "66902CFFD057673B4B77AA68FAF80D33FFA4C94510E61DB2A067943CC53B7702" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Globalization.Extensions.dll", - "source": "vendor/pwsh/ref/System.Globalization.Extensions.dll", - "bytes": 15144, - "sha256": "BE0390557CC72078A7217CF94FD44B9AB66AA30838C00A92AA78C62469C44AA0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.Brotli.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.Brotli.dll", - "bytes": 18256, - "sha256": "1401375A39B7BA5E65E0B49274C0168B6C8B7E5455AFEFD269C1418FD019E997" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.dll", - "bytes": 21800, - "sha256": "AC573888BA94E958243674D82E51E88692E5BEA6FD94BB68D87D30C28CDAECF5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.FileSystem.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.FileSystem.dll", - "bytes": 15184, - "sha256": "4AEEC2C0189F73BA8122F05C0D759008DDAE1A8873B167CC8A558D53F2AF80E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.ZipFile.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.ZipFile.dll", - "bytes": 18728, - "sha256": "C34EF5004F535F10ADCEB66B3F20F743B15656ED4C7DCF5946A8B83A6CDEE329" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.dll", - "source": "vendor/pwsh/ref/System.IO.dll", - "bytes": 15656, - "sha256": "388400CCB189B819D6EC5F1EF70710A39641D93ED5B870FC9324866B75DFB78B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.AccessControl.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.AccessControl.dll", - "bytes": 20264, - "sha256": "38EB787FD20B30F36E207364387FD34A5C68207BAFFDDF318986216F4B6CC6B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.dll", - "bytes": 15656, - "sha256": "C251F33B29382C47B773D907463EDD9FE978A06E39EFD5670EF3B3B1D4F8C2B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", - "bytes": 17192, - "sha256": "5CE6349A752189B3BCAA553FEF38F9C0CDD1FB2E39E7CABA67C966F52EF66391" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.Primitives.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.Primitives.dll", - "bytes": 15144, - "sha256": "D3B21B8B38E48C6B4D1CB5CFE9ADFE4ABAD7B3353601FEBD3C7E4027F9EF9C6B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.Watcher.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.Watcher.dll", - "bytes": 20776, - "sha256": "07FC2B07B7D53173C695E60E93F2DA4325B68C797C4351FFF571923F01F8054C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.IsolatedStorage.dll", - "source": "vendor/pwsh/ref/System.IO.IsolatedStorage.dll", - "bytes": 22312, - "sha256": "6720027218D12697D30640FB97ACDB02438649CCCB038FFDB3AD80DC27F6F736" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.MemoryMappedFiles.dll", - "source": "vendor/pwsh/ref/System.IO.MemoryMappedFiles.dll", - "bytes": 18256, - "sha256": "028850F2CEBBCC3CCB48FCEF97751522EA6455C05FF9234686A16CC088FAC5A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Pipelines.dll", - "source": "vendor/pwsh/ref/System.IO.Pipelines.dll", - "bytes": 20304, - "sha256": "08344A31693477A61B45DFF21739BF576C7482B394FAB2FA26D2D8BA87B2CB91" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Pipes.AccessControl.dll", - "source": "vendor/pwsh/ref/System.IO.Pipes.AccessControl.dll", - "bytes": 18216, - "sha256": "80FDA558926F7D7A085A6A210596000D281E4AF2FB235E4C8DC9CBEF8F1A87D0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Pipes.dll", - "source": "vendor/pwsh/ref/System.IO.Pipes.dll", - "bytes": 22312, - "sha256": "0B7AAA8FEA2A9483857B2AC918814AD1D8FF3303C98D6EF71D4593B16ADEE9BA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", - "source": "vendor/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", - "bytes": 15696, - "sha256": "AD6A5F859589E1951103A00F45566DA079CF87BD8B03F0C6AD985A6F39F19C5A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.AsyncEnumerable.dll", - "source": "vendor/pwsh/ref/System.Linq.AsyncEnumerable.dll", - "bytes": 36176, - "sha256": "3367B7D6D2BE9D171E158B544979131E8B97328B293D6E77917234A2A7279507" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.dll", - "source": "vendor/pwsh/ref/System.Linq.dll", - "bytes": 33616, - "sha256": "9CB73D57C035503BDC70FDC422DEE5BF4F9DE97B812CD0DCB379D652C6F91205" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.Expressions.dll", - "source": "vendor/pwsh/ref/System.Linq.Expressions.dll", - "bytes": 63312, - "sha256": "772112C3F1C10FB713CCC3AD62CC313C2CAD1CBAF122EF6D25120AA7B65B0447" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.Parallel.dll", - "source": "vendor/pwsh/ref/System.Linq.Parallel.dll", - "bytes": 31016, - "sha256": "B4B597374FFEBCCF8A46793F03D5E6D5E477DFC5047157217D79449F9729E761" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.Queryable.dll", - "source": "vendor/pwsh/ref/System.Linq.Queryable.dll", - "bytes": 32040, - "sha256": "5BAF83B98F103D9304EAC427C4296C9DD501D320B7B5495ED8EDE892AAF1076D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Memory.dll", - "source": "vendor/pwsh/ref/System.Memory.dll", - "bytes": 57168, - "sha256": "D7E674A1FEFF0546C9932B0FCFFA9D055976736342A4B1904F1F27894F3C0ED6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.dll", - "source": "vendor/pwsh/ref/System.Net.dll", - "bytes": 17192, - "sha256": "481B38C912F8779B3510CD00F079E45096EA22B6DEFDAAB66780D5AC3AE6986A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Http.dll", - "source": "vendor/pwsh/ref/System.Net.Http.dll", - "bytes": 60200, - "sha256": "3FF081221D0ADEB99B5CF40788E20DAB1D0D706618044B34E68A47529C761601" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Http.Json.dll", - "source": "vendor/pwsh/ref/System.Net.Http.Json.dll", - "bytes": 23336, - "sha256": "BF97FCFA0D20ADB597878F902B761480FB02FA0F9F81521F16F2455DDC6A1ED2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.HttpListener.dll", - "source": "vendor/pwsh/ref/System.Net.HttpListener.dll", - "bytes": 25384, - "sha256": "03CA98ABEB9EA2C7E372C31AA460D2A9E526617C22FA97C9D8750977167A9634" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Mail.dll", - "source": "vendor/pwsh/ref/System.Net.Mail.dll", - "bytes": 32040, - "sha256": "8193E3652B1ECB1AA46258087BB5F44BECD72693552DA120E1274EF1AD443E53" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.NameResolution.dll", - "source": "vendor/pwsh/ref/System.Net.NameResolution.dll", - "bytes": 17704, - "sha256": "97E0CC175BF13E4861E1508820F08561B38F28E98EBC9C5ADDB28C58FA7C344D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.NetworkInformation.dll", - "source": "vendor/pwsh/ref/System.Net.NetworkInformation.dll", - "bytes": 33576, - "sha256": "B90B729446F4ED3FA470B5C96E9C5ADECBF4E438EAA91D0548832C58673A4C18" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Ping.dll", - "source": "vendor/pwsh/ref/System.Net.Ping.dll", - "bytes": 20264, - "sha256": "F623594E3309D40D36A7DD63DCB5251F72EB94EB2E74246805B4A0D23FC8C6B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Primitives.dll", - "source": "vendor/pwsh/ref/System.Net.Primitives.dll", - "bytes": 37160, - "sha256": "129E7B324FBC12F8C54B89E6D6706B08455532FB64A8913038ADD92BE2924244" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Quic.dll", - "source": "vendor/pwsh/ref/System.Net.Quic.dll", - "bytes": 23848, - "sha256": "F9651DCDE17F5823C7D42BE080059FD9F88AED81D91C64E58F95AFCA44A2E24A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Requests.dll", - "source": "vendor/pwsh/ref/System.Net.Requests.dll", - "bytes": 41256, - "sha256": "D45910C06D2C2FCD6039E8B4076B5878FD195D0FC09D27C4CEB4009AD9491672" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Security.dll", - "source": "vendor/pwsh/ref/System.Net.Security.dll", - "bytes": 53544, - "sha256": "C4AF7099D2077E34C7074E94EFEB57AC5707AF128EE6E5E0E75D398B8E5843BA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.ServerSentEvents.dll", - "source": "vendor/pwsh/ref/System.Net.ServerSentEvents.dll", - "bytes": 17192, - "sha256": "87BA81CD8FD6AB3A9A3F4DCE922849AEA832928F52495788D371DDA66E57DA01" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.ServicePoint.dll", - "source": "vendor/pwsh/ref/System.Net.ServicePoint.dll", - "bytes": 15144, - "sha256": "84FA83CD96E62FDA02991B8A8E99FA1C7B3AC08807B04F7486A896CFE6B4A760" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Sockets.dll", - "source": "vendor/pwsh/ref/System.Net.Sockets.dll", - "bytes": 47440, - "sha256": "8B7CEBAFD550083A8D242DBCFB7501384D17142AD6C2BBB047DD37A524762179" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebClient.dll", - "source": "vendor/pwsh/ref/System.Net.WebClient.dll", - "bytes": 27944, - "sha256": "E4E6CC79E23892C8E67A4CB594BB304A26A0328507C45DC2D1998BC6836311A0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebHeaderCollection.dll", - "source": "vendor/pwsh/ref/System.Net.WebHeaderCollection.dll", - "bytes": 19240, - "sha256": "C4EE735A52B966F3805CFBD5C172F151FFE013BD70C080F15C2F68BE628CD357" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebProxy.dll", - "source": "vendor/pwsh/ref/System.Net.WebProxy.dll", - "bytes": 18216, - "sha256": "42C8EB8BF1563806069E1DD1C6C651159DAA5589B7F69FF817CDEAB3BD02915F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebSockets.Client.dll", - "source": "vendor/pwsh/ref/System.Net.WebSockets.Client.dll", - "bytes": 19240, - "sha256": "5AC14D45C1B92E9C831B37591941EA01F3097A39D130BC292506DD099AEB7260" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebSockets.dll", - "source": "vendor/pwsh/ref/System.Net.WebSockets.dll", - "bytes": 23848, - "sha256": "C16043D3FFA90A8FDAF193F0DF91F99D6C23518D8432B4CAA2D70A92D4C3CFA7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Numerics.dll", - "source": "vendor/pwsh/ref/System.Numerics.dll", - "bytes": 15184, - "sha256": "35ADC9360421B3B7A3D50399CB2C651C160158D7E8C302907ACBCE5F61F4A4F7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Numerics.Vectors.dll", - "source": "vendor/pwsh/ref/System.Numerics.Vectors.dll", - "bytes": 58152, - "sha256": "CC06324F279C581D8F76C0C8F6B011CF8D05EFD0C95E998D68F9243DA124EB52" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ObjectModel.dll", - "source": "vendor/pwsh/ref/System.ObjectModel.dll", - "bytes": 23336, - "sha256": "3A591E043CD443761537580084F714392BB3278176C9A19F1BE8700A29EF6F56" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.DispatchProxy.dll", - "source": "vendor/pwsh/ref/System.Reflection.DispatchProxy.dll", - "bytes": 15656, - "sha256": "0D46DFB9C4FFC27D05A1C089D8F60A1CB365855140A9B30972133208270DD08B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.dll", - "source": "vendor/pwsh/ref/System.Reflection.dll", - "bytes": 16168, - "sha256": "95059346D6271DD6D0322CC321E5FB8CCEAB662F550C67AEE7615059E41739BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Emit.dll", - "source": "vendor/pwsh/ref/System.Reflection.Emit.dll", - "bytes": 44368, - "sha256": "29021699E4128964365A86C22AFB3B0A8368DA7A3F7463C9C1A8FC0A1402A39F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", - "source": "vendor/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", - "bytes": 20776, - "sha256": "0201069B706D397C573B9342902613A71879A2701994A48E2821C32D43101AE4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Emit.Lightweight.dll", - "source": "vendor/pwsh/ref/System.Reflection.Emit.Lightweight.dll", - "bytes": 19240, - "sha256": "EEF97E1049D8DD7ECC695080DD22C199EE8CA82B1CFCB3E33866CF7125DBD1A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Extensions.dll", - "source": "vendor/pwsh/ref/System.Reflection.Extensions.dll", - "bytes": 15144, - "sha256": "CEB1B81E1EB488D87BD43D16DC54450B2A78E0B653C44C988AAE2A20ABC162BC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Metadata.dll", - "source": "vendor/pwsh/ref/System.Reflection.Metadata.dll", - "bytes": 122152, - "sha256": "92EC811516B768220CC41FB6A7197F7BA735F97B694716DFD1BFDF274078A90A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Primitives.dll", - "source": "vendor/pwsh/ref/System.Reflection.Primitives.dll", - "bytes": 21840, - "sha256": "FA52402A7B4096EF109853F6BF03ADEF0E1DE67E3652BB31F413D197F19F9B22" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.TypeExtensions.dll", - "source": "vendor/pwsh/ref/System.Reflection.TypeExtensions.dll", - "bytes": 19752, - "sha256": "78178074BDB0F7714C634E4A1B425570264B13B2CA5B9AD6880622BFF0BDA58E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Resources.Reader.dll", - "source": "vendor/pwsh/ref/System.Resources.Reader.dll", - "bytes": 15144, - "sha256": "20BDB5D302B5E28F6042C9F281CFA3AE2F2358544CC40783052FE5ABF6AA859B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Resources.ResourceManager.dll", - "source": "vendor/pwsh/ref/System.Resources.ResourceManager.dll", - "bytes": 15696, - "sha256": "F709EB5CF16E26679782784C428825019785FCC7085562092425D4999BECE03D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Resources.Writer.dll", - "source": "vendor/pwsh/ref/System.Resources.Writer.dll", - "bytes": 16208, - "sha256": "7E772363E799C78D8D1D37FE1D412723DA9350496A6D7BACD37491F2BB777CC4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", - "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", - "bytes": 15184, - "sha256": "6E685EC0477B21A688CD38BFCAFC673888CDF186C99DF3C96FAB352E96D6FDC8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", - "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", - "bytes": 17192, - "sha256": "1E5EB23824055D6AF5BABB6A7CE47E314F78B0B464466DB32B60F543304A9140" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.dll", - "source": "vendor/pwsh/ref/System.Runtime.dll", - "bytes": 862504, - "sha256": "10CEAA9B398F512B46DEF6FD16FB5374C82AC03A7495859ADBAAA70868FE70AA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Extensions.dll", - "source": "vendor/pwsh/ref/System.Runtime.Extensions.dll", - "bytes": 17704, - "sha256": "3D7B517D4FD89EE58C53E0F642CC9BE42E7A8669B51A85E861DAFE118067CBFD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Handles.dll", - "source": "vendor/pwsh/ref/System.Runtime.Handles.dll", - "bytes": 15656, - "sha256": "985457A3E6A67E96A445EE0FB69B2D58BCDADC4C964253FB63E2D5061A36B050" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.InteropServices.dll", - "source": "vendor/pwsh/ref/System.Runtime.InteropServices.dll", - "bytes": 101160, - "sha256": "8CED47E6A2A717C459C3FA13F76A521B35917E123A9B1EE7ED1B5DE13DB80709" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", - "source": "vendor/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", - "bytes": 25896, - "sha256": "CB551CC19DF1067DD8897D14BF02ECFA63D8ECF96067A21902406791D68CE226" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", - "source": "vendor/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", - "bytes": 15656, - "sha256": "C26D9F94329422BD000F28F6C65FDC162BF9397A6D29E418A189D4CA49C5C3EC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Intrinsics.dll", - "source": "vendor/pwsh/ref/System.Runtime.Intrinsics.dll", - "bytes": 466216, - "sha256": "DF8F497F64B6A3AC02941FAE92E057FADDF93F3F135F2F6444F725617C38B619" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Loader.dll", - "source": "vendor/pwsh/ref/System.Runtime.Loader.dll", - "bytes": 19752, - "sha256": "42884BCDC89EBD4EE509D5FCB4A679B1D7EEBD6DDD9B66CB9E118063DDD30482" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Numerics.dll", - "source": "vendor/pwsh/ref/System.Runtime.Numerics.dll", - "bytes": 36136, - "sha256": "4AFC84F3E73B63FF6683941FCFDA44E8A6AA5227B6AFC128DCB979865E23D183" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.dll", - "bytes": 17192, - "sha256": "D565FC261CA98CBD14833821F777860291E8A0F775DE7AEE91778A9F6647F6F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Formatters.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Formatters.dll", - "bytes": 23848, - "sha256": "EF24BB5669B54B31DA46EB13238FB2B7F880DD780DC9B34A224A69DD59D93AF8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Json.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Json.dll", - "bytes": 20776, - "sha256": "DE7EECA7DF9F6BB664383FBEAD141719BD08D88B6AEF43C0605528719F607E62" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Primitives.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Primitives.dll", - "bytes": 19752, - "sha256": "A547B0D205CEBE84508C2DDEFD56D063C48CDFEAE5B0C44BDE6BAE9F3D72F98A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Xml.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Xml.dll", - "bytes": 39760, - "sha256": "117BCB0DD828EAF6BFAD06D8380BA32D32729B45879D907D7023FE73E9655BD9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.AccessControl.dll", - "source": "vendor/pwsh/ref/System.Security.AccessControl.dll", - "bytes": 37160, - "sha256": "2BD23D6AA92B920DAFD374E1AB47ECB597EF3B1CF1652A4EC4944A0DEFB1A4AC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Claims.dll", - "source": "vendor/pwsh/ref/System.Security.Claims.dll", - "bytes": 32552, - "sha256": "693A61DC317AB5A651EF598578A4B55912AC39342A0842FEA003761F861B0CAF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Algorithms.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Algorithms.dll", - "bytes": 17232, - "sha256": "EB99AE7DBE058A1B015ABA6769CE1F65155B7FF51FF9A7203FCF6D7CDEA5FEF7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Cng.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Cng.dll", - "bytes": 16168, - "sha256": "7319F97896D92A7D0729CF28DC6C4D1F797C1520B656EE01A780EEC726EC2C16" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Csp.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Csp.dll", - "bytes": 16208, - "sha256": "4002AF141E9CB29875B677841ADF64A9681E5672C2595492DCBC161460D4E121" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.dll", - "bytes": 163112, - "sha256": "A700A7F6C32169E62BE879088AA2218BDD5450C3945ED6C6D0ACB3D25DB0B1BF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Encoding.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Encoding.dll", - "bytes": 15656, - "sha256": "994494AEC7760F40FADE2264AA7C13EEBAE36360F0B1399A7134BB19C9D58FDA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", - "bytes": 15656, - "sha256": "1A8902BBAD1B577A98BEC62A8472105DC1E9A253E390F69FBE3D167E93206968" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Primitives.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Primitives.dll", - "bytes": 15656, - "sha256": "194CCCEA9354BB248E44C954B5373F9069163F8F21524B3EE4B3185FE436BC15" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", - "bytes": 16680, - "sha256": "7439C3B90BC2EE49A9275B28ED29052B4D48B45999AC1B7D25593033B1BB8DD6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.dll", - "source": "vendor/pwsh/ref/System.Security.dll", - "bytes": 18256, - "sha256": "D8EFD304765E859369698EFEF9B3CA707DEFA8E142721FE5605D7A2DE7D38375" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Principal.dll", - "source": "vendor/pwsh/ref/System.Security.Principal.dll", - "bytes": 15144, - "sha256": "1D3E351058BE3A33214B23B308FE57F78CD6FD4D8FAD448FC84CBBE7376129F9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Principal.Windows.dll", - "source": "vendor/pwsh/ref/System.Security.Principal.Windows.dll", - "bytes": 26920, - "sha256": "4B8EAB6E832E99E8C08DB2B9649F95AE8298E1105DEEA45A62E7D3B68C6593B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.SecureString.dll", - "source": "vendor/pwsh/ref/System.Security.SecureString.dll", - "bytes": 15696, - "sha256": "73AAC97072B7DEEFA4DE9647BF822162224F1452AD467841B989FF89D4D94595" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ServiceModel.Web.dll", - "source": "vendor/pwsh/ref/System.ServiceModel.Web.dll", - "bytes": 16680, - "sha256": "CE91013EE3A9A9D69A85E258F7749E6ECE488295D7CB66F7C1079F2E8678FC5E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ServiceProcess.dll", - "source": "vendor/pwsh/ref/System.ServiceProcess.dll", - "bytes": 15696, - "sha256": "63FC498C9D04E3C4345DFDB8016AD0639D54562DD7D8C707C072792C854DF944" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encoding.CodePages.dll", - "source": "vendor/pwsh/ref/System.Text.Encoding.CodePages.dll", - "bytes": 15656, - "sha256": "2D66F9AE5E7439756EBA063C0DF86D6D3A0DC571F4BB07ADD18C59D6F10138CE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encoding.dll", - "source": "vendor/pwsh/ref/System.Text.Encoding.dll", - "bytes": 15696, - "sha256": "5E347DB6B4F9B2746D50E1AC85BE2D9CC7DC4038FDE98AC4295BC7030C83CDA9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encoding.Extensions.dll", - "source": "vendor/pwsh/ref/System.Text.Encoding.Extensions.dll", - "bytes": 20776, - "sha256": "D6224360B0E91DFE544EC3BAF0D5DED78A13349F6378493C96344A310C8CAF08" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encodings.Web.dll", - "source": "vendor/pwsh/ref/System.Text.Encodings.Web.dll", - "bytes": 26408, - "sha256": "F9F6120753FC9FAEAA5B09E10EF309594DBD4C20C8CDF1717EB910455549793A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Json.dll", - "source": "vendor/pwsh/ref/System.Text.Json.dll", - "bytes": 82216, - "sha256": "4133A8597A10EEB99D56A962FF581DB9FD3AC85506EE3A20A2D23119C73A4346" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.RegularExpressions.dll", - "source": "vendor/pwsh/ref/System.Text.RegularExpressions.dll", - "bytes": 36648, - "sha256": "347C497CF2BD60A298EFB59FE47156909CA854FDAB4DABB944AF7D6F8C270CEF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.AccessControl.dll", - "source": "vendor/pwsh/ref/System.Threading.AccessControl.dll", - "bytes": 20776, - "sha256": "8449A1EE09EDF8BDC69C61605BEBEC602C7B28A9CA401CA463512E4D369A3B7B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Channels.dll", - "source": "vendor/pwsh/ref/System.Threading.Channels.dll", - "bytes": 19240, - "sha256": "C72028E4423FB04F3A644A76E1807E3651191AEB606B8F7B15B3650E4714CDCA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.dll", - "source": "vendor/pwsh/ref/System.Threading.dll", - "bytes": 33576, - "sha256": "0C2A3B4FDDB2D732B8EBD0BCA9AC57A8C66C9D4F5FBCB55FCD17E3CBCF678A4B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Overlapped.dll", - "source": "vendor/pwsh/ref/System.Threading.Overlapped.dll", - "bytes": 18768, - "sha256": "7837D3BD05E28F95749029C832FB1D0CE5BF403A17BEF7BF95DF522BA93DAC3A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.Dataflow.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.Dataflow.dll", - "bytes": 32040, - "sha256": "5D605D4C470C38BC37A04D58B4023A9C60179172790A29F4F7CB92ABA6251648" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.dll", - "bytes": 16680, - "sha256": "F80B21F7DAE04377B73945456CABE25E2AB1F4B564095AED546C2AA571644567" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.Extensions.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.Extensions.dll", - "bytes": 15696, - "sha256": "F148E3A1414B7C2763AE21529AD79AD759F7CFD3C885256B64B0D2209DEC2613" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.Parallel.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.Parallel.dll", - "bytes": 20264, - "sha256": "97F9CCB69D9F1D4D86DF2869E1EF667C4C68285B04F336B1188E633C8F814D05" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Thread.dll", - "source": "vendor/pwsh/ref/System.Threading.Thread.dll", - "bytes": 23888, - "sha256": "FFF2281F039C07E564BCB198E041627082702BEDA24DCCC8DF39683701EA25A0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.ThreadPool.dll", - "source": "vendor/pwsh/ref/System.Threading.ThreadPool.dll", - "bytes": 18216, - "sha256": "12FEDF2DE6449BFEC4C1A08D762493BC0188E1B9B0A58028289105ED81B983F5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Timer.dll", - "source": "vendor/pwsh/ref/System.Threading.Timer.dll", - "bytes": 15144, - "sha256": "9E4620846572F49AEB476AA99405071B4BBD0DF4323E4E232787ED73642B1EE5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Transactions.dll", - "source": "vendor/pwsh/ref/System.Transactions.dll", - "bytes": 16720, - "sha256": "56ABFCA0350C111D077B3D86A2469EA3DA4FCCC1871F7C51A8B4FDB664BB0EDE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Transactions.Local.dll", - "source": "vendor/pwsh/ref/System.Transactions.Local.dll", - "bytes": 25896, - "sha256": "C9FAFFC186D677DE7800FF7D4D1B0D204AE7614E6B8518ADD582C840D7532A3B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ValueTuple.dll", - "source": "vendor/pwsh/ref/System.ValueTuple.dll", - "bytes": 15696, - "sha256": "07A5347F6A597E5E0D72109EFA1A525697E8C3F74FB0029005500F8EBBF9187A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Web.dll", - "source": "vendor/pwsh/ref/System.Web.dll", - "bytes": 15144, - "sha256": "BC260B3DB247AD9A6E4A82A275DA85C3691A41272CC745CB7EAEFCF1F31CC817" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Web.HttpUtility.dll", - "source": "vendor/pwsh/ref/System.Web.HttpUtility.dll", - "bytes": 17192, - "sha256": "F26B0586048FDF472E0FB33CA9435B163C91325DE4281FCE8684251CACC304CC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Windows.dll", - "source": "vendor/pwsh/ref/System.Windows.dll", - "bytes": 15696, - "sha256": "716EEE98CEEE87D38F0B301224462F8442AA793F4B1B9C3FFAC315753320AB37" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.dll", - "source": "vendor/pwsh/ref/System.Xml.dll", - "bytes": 23376, - "sha256": "277A84BA155FE7808A514F32694A8157EC9F0EEEEF00EF9643E5250004054FF0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.Linq.dll", - "source": "vendor/pwsh/ref/System.Xml.Linq.dll", - "bytes": 15696, - "sha256": "AE04903B25717FA4695E7E63489091F0D461E791E0BD264C6C85250D668F540D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.ReaderWriter.dll", - "source": "vendor/pwsh/ref/System.Xml.ReaderWriter.dll", - "bytes": 116008, - "sha256": "942964D32F72BA10288593F212E3A52C4811B65CA4D5F1A3979C92D2D526AA4C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.Serialization.dll", - "source": "vendor/pwsh/ref/System.Xml.Serialization.dll", - "bytes": 16208, - "sha256": "489694401C60905CB6DCA8EF0BF738CB609CD45B337126A01C825B913086BA43" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XDocument.dll", - "source": "vendor/pwsh/ref/System.Xml.XDocument.dll", - "bytes": 34088, - "sha256": "05F6380411A81245960714A9FD81F6134F7BD279CD70B248A1E1586977798F1A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XmlDocument.dll", - "source": "vendor/pwsh/ref/System.Xml.XmlDocument.dll", - "bytes": 15656, - "sha256": "A50B9B3A03596E36E73E7A7D54F4D018E8DCFAA7FE79E8CF900AF8838EF3EC36" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XmlSerializer.dll", - "source": "vendor/pwsh/ref/System.Xml.XmlSerializer.dll", - "bytes": 50984, - "sha256": "F957611B80E082A453264A13BF27B5B251FAF598F377F948195123A374B2E8F6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XPath.dll", - "source": "vendor/pwsh/ref/System.Xml.XPath.dll", - "bytes": 16680, - "sha256": "0E68260B4C36D00C9A77C30A270100461123C0E7E1A37402BB7F67174051B8F3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XPath.XDocument.dll", - "source": "vendor/pwsh/ref/System.Xml.XPath.XDocument.dll", - "bytes": 16168, - "sha256": "B8F74E198C3C2CD00CCC90549579F1B69C6AE1655F8B348ACE414FD9075C921F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/WindowsBase.dll", - "source": "vendor/pwsh/ref/WindowsBase.dll", - "bytes": 16208, - "sha256": "0EC46A38F29A234B9A4D0AE9364C8322F661A47C831C5B948F2787B7CA12DBBD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/RegisterManifest.ps1", - "source": "vendor/pwsh/RegisterManifest.ps1", - "bytes": 17502, - "sha256": "13D50727D810BABE2A8FCBC77A0A4EA967A6C78DE4960469116DF9D8EEF53EC0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/RegisterMicrosoftUpdate.ps1", - "source": "vendor/pwsh/RegisterMicrosoftUpdate.ps1", - "bytes": 16907, - "sha256": "8DD5BF144D53B976B3F414973FB0F6C886F754E515427A2E8FD58B9C3B3E1B52" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/base.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/base.xsd", - "bytes": 6434, - "sha256": "B860EF30B01C57486A56948800E9B2DA116EC473CBECBB33FA9F71B8CEAB90AE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/baseConditional.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/baseConditional.xsd", - "bytes": 5297, - "sha256": "D26D4EF29124B2F9107B167D73E611F649FB2021D30C48A8616C6CC96230DE81" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/block.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/block.xsd", - "bytes": 917, - "sha256": "9A8B3C184394711480C38D71DB4850659B87729DF9EAC5A2235BDCF37A2020CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/blockCommon.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/blockCommon.xsd", - "bytes": 3872, - "sha256": "928717B190B014F271C09C2FFB7633C1E660B26F0ECBDD926662121914795789" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/blockSoftware.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/blockSoftware.xsd", - "bytes": 1282, - "sha256": "88AA3462598B7C394350F83EF366E5F83885C50A9C6966F02179228277358334" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/command.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/command.xsd", - "bytes": 13061, - "sha256": "A3DA7F8F2260A826CE253C33BDA8576246B4298794765EDC4F80A433357C6B87" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/conditionSet.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/conditionSet.xsd", - "bytes": 4279, - "sha256": "506415CD77EDE25B33C3E28FB89012B282E90B5D01F266323A83FEA9625F8EAB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developer.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developer.xsd", - "bytes": 11403, - "sha256": "AB9A7DDB85084F331ED79CB34374509767D9A64C510B0264682D5E682DC59497" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.rld", - "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.rld", - "bytes": 115907, - "sha256": "CD8A9A6B113DB5005F2A5C8321E52368411691B5F0C5EB90595E2A97B0FDEE89" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.xsd", - "bytes": 14520, - "sha256": "BBC9B2DD6D4473F09CE95A4E44B7F654A8D34A70197BEABACD1C9FF5233C3DEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerDscResource.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerDscResource.xsd", - "bytes": 2896, - "sha256": "7C22D9A1393062734E4923657C48D9117F6AC824D4E3F85EF88D8DE23F8C7BDF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManaged.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManaged.xsd", - "bytes": 10238, - "sha256": "5585FD25ED4FA8D39B75E9A83082EAB1D305B00839E7BCF05A24530E3D91D6EA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedClass.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedClass.xsd", - "bytes": 3693, - "sha256": "4CB1C23D86D22B4BCC5ACA98CD309C37F54A30358D673115D460FEEACB78DF9A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", - "bytes": 2557, - "sha256": "0CA55A744AD968B537742ED37CD62852DF01CD96533D4E17874AD4F75271C8DC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", - "bytes": 2558, - "sha256": "961DAA1C4B3513B83CF2E37FDB1361BE0D123E8B2CFE7F32510AD534FEA98E3D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", - "bytes": 2306, - "sha256": "3AB7329914D7A9A6D711A62405089E736CE714EE4DE72F3493222887A72FDD39" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", - "bytes": 3108, - "sha256": "93EBD3AF5A078830839B10C9DB753CD445DC2D1A78A5449C90AC605E7FFC397C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedField.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedField.xsd", - "bytes": 2803, - "sha256": "B17F73ADD6678C4BC259065ADD380A9070531BC24A66AF3C4CAD3CF3D811F3A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", - "bytes": 2882, - "sha256": "B851D8BE146F1F51E5857045C28726585CC96A29C5A5A57DFC86CF7C509D8234" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", - "bytes": 2544, - "sha256": "36EB2BC76B2CDBFD908DF62E16CA915F7490CE75CF7BC386974EF89AA77B32FF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", - "bytes": 3359, - "sha256": "68D68D8CEC8181C0C3046B419426EE1BD4D0371ABFDBF9A6789755B0F5764D91" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", - "bytes": 1896, - "sha256": "053C18557BE353860222E14D7CE5E5844F5A746BA45347A9E08DA1D5A2B29F3B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", - "bytes": 2573, - "sha256": "7D2695D2F6AB6EB3C0BAB6125EEE415545C3EABE0923B1B37519F2D4A8B9251D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", - "bytes": 2843, - "sha256": "832A25485EAA9060868E9258AA1F93626804ED439EE2B43B2D74F9CB0DC438C4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", - "bytes": 2951, - "sha256": "B7D1F7DDBCFEF43BD171FD78D5A255089A97762AD67C116B6A6998815905901F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerReference.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerReference.xsd", - "bytes": 12604, - "sha256": "42C585D48C418C7EB47CBA298A147923597B967E236997BB815301B57E31CB92" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerStructure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerStructure.xsd", - "bytes": 4112, - "sha256": "D1C0E02BB7207095E49EFC216A574B596A82E97B88E686E1C7A9717362623C44" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerXaml.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerXaml.xsd", - "bytes": 6746, - "sha256": "83ED2EE26CF6AB4C95A880F1424F7D5E09385B71239B84B3CFFB58288EEFF3B2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/endUser.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/endUser.xsd", - "bytes": 2144, - "sha256": "24CAEE5D0536ED81EB68D6FD836CF08849E056497B6064971A145397A04EB853" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/hierarchy.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/hierarchy.xsd", - "bytes": 10070, - "sha256": "D50D8096A11350A8E3B6873CF9CBC173B8A4322324B16F949538B1B98857C5B4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inline.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inline.xsd", - "bytes": 7644, - "sha256": "5A1A2D4586203BB3D6A4E6EC93E4D09D0AB66D33539B88A2550A36100D1E16FD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inlineCommon.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inlineCommon.xsd", - "bytes": 8860, - "sha256": "02071402B963966AD574AB09BB42719BDB1CE6CD7E9AF260A13FAF9EBB580C55" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inlineSoftware.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inlineSoftware.xsd", - "bytes": 8016, - "sha256": "C444CD1121A8B83B0FFE5A8467CC10D2D810C5A7765F4B4406D1EB329CD8901D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inlineUi.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inlineUi.xsd", - "bytes": 6400, - "sha256": "E7FF025576E482632878709E208B95916CCDCA2AF108C1C7B9218FB8472C1AE3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ITPro.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ITPro.xsd", - "bytes": 1208, - "sha256": "767328AD7B14EC90731E52D04EF27873EA674D34DD30297CB0FBA2A11242E96E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.rld", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.rld", - "bytes": 115835, - "sha256": "A711A691FDFDBEEEF09CB23543A8AC3D6BBF92AABEEC05939FCA51665229E710" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.tbr", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.tbr", - "bytes": 30575, - "sha256": "0FFD2E7097B51E0C6E1C1A18867658312D39AC68D8179049521F9ECAEB2CBF08" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsd", - "bytes": 1571, - "sha256": "FDA94A92FE41A7280D7F5ECE13CFC0042D4E1049E8FF961717628412317F6AE2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsx", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsx", - "bytes": 211, - "sha256": "639E5ED55BB9FD20672C74C20020C6C96B5FF721E45E9A337133AF858998B08C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML.xsl", - "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML.xsl", - "bytes": 363, - "sha256": "BDA0F195C928E6C18E226A83C77A1477DD88B70025D78F12D92605BBD10723BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", - "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", - "bytes": 14422, - "sha256": "99E1AD569C30396EFC7F37ACD08A8F0F41B20FF82E513D738E331C0EAC5DDC1C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", - "bytes": 14517, - "sha256": "3B89CB9A72549C46A56D9EA2EA12AC53CBC7DDD3392012FDD9D69EF261961AD2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", - "bytes": 3391, - "sha256": "0FA8EACF615F4A726F846D2938E18E84BB641BB28579BD9E2AB83F3D46D505FA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ProviderHelp.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ProviderHelp.xsd", - "bytes": 6766, - "sha256": "0E6ADBE5B896F075BF55AE6F2C1966E0E5596F79BC282C2D17CC36CCF41C30CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/shellExecute.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/shellExecute.xsd", - "bytes": 2250, - "sha256": "52F48B0830346E30C782AA5C379DC547D0F40265FF1504276C904B819446409E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structure.xsd", - "bytes": 5973, - "sha256": "F35E24830E9E1A6AC79DEDFBC3F2EF03EFF8D41EF8C2414E8BA1C1A52E527391" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureGlossary.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureGlossary.xsd", - "bytes": 4186, - "sha256": "4425E365BA3B646B11E5625644D5F98B0151F60ED2BA0A28316570D7D68F6FA5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureList.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureList.xsd", - "bytes": 4173, - "sha256": "176D55D7715DDECCCBE51330E9C012E09972AE3E4A9C9AC8D1C5869B9510540A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureProcedure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureProcedure.xsd", - "bytes": 1994, - "sha256": "D323AEB14F98A01583E538DD92EF445E1094DB861DC7205D5D2062976DE539B3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureTable.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureTable.xsd", - "bytes": 6137, - "sha256": "63D397B018E5D3783AE3057658D7ADCB44F968BFD81329D2FCEA1ABB91F2C7B8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", - "bytes": 6794, - "sha256": "C37308C8CDB9AC988C94A850A848FC79707FB5D268F7E17072D6D2FB8E83E852" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/task.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/task.xsd", - "bytes": 22391, - "sha256": "808A13CEEEBDA94C0152B93A52A7825E20B5C01780A3EC6C2AE0C8AC0230ED74" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/troubleshooting.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/troubleshooting.xsd", - "bytes": 3239, - "sha256": "FBA82FDA979C33074968ED01AD95970FCF1151F3E7C298955BD501D5CA554633" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/sni.dll", - "source": "vendor/pwsh/sni.dll", - "bytes": 160040, - "sha256": "68705C4EF9AB818F2956A78E05F3FEFCE501A1448793B073B46110BEB49B47D6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.AppContext.dll", - "source": "vendor/pwsh/System.AppContext.dll", - "bytes": 15184, - "sha256": "6CD5D7FAE26815702D087EADBCE804B157CFDE3A57A8D897016822183FA2190E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Buffers.dll", - "source": "vendor/pwsh/System.Buffers.dll", - "bytes": 15184, - "sha256": "91F7DD9DBBEF1A7A2BD7C83EAB8110F8C1915DEB38723B05E7F65A5FFA491220" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.CodeDom.dll", - "source": "vendor/pwsh/System.CodeDom.dll", - "bytes": 476968, - "sha256": "28B49CE532AF31CA14A28C9B823E628457D2F3D62F96FDF0E54F206E5B1B7169" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.Concurrent.dll", - "source": "vendor/pwsh/System.Collections.Concurrent.dll", - "bytes": 231208, - "sha256": "EFD70AF76FE0182D974DDC1520F335E9764ECFDD6A517168944B76C07A31E7A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.dll", - "source": "vendor/pwsh/System.Collections.dll", - "bytes": 296744, - "sha256": "DC868C8736277084D26726D726C6CE07FFF9EAB3ACA2D3476CFACE8FC3B2403F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.Immutable.dll", - "source": "vendor/pwsh/System.Collections.Immutable.dll", - "bytes": 976680, - "sha256": "CDEFF36E2129E3A1CBF08698C4D692A13F2C25CD1C04CED0120CAEF982461354" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.NonGeneric.dll", - "source": "vendor/pwsh/System.Collections.NonGeneric.dll", - "bytes": 104272, - "sha256": "D84C613DA29BAF7D8DFD7E8AB7CD806E1086EB16B350C8377947B5EDB77B6EEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.Specialized.dll", - "source": "vendor/pwsh/System.Collections.Specialized.dll", - "bytes": 104232, - "sha256": "E203E92897552D5109F4F096EE7019443F83DAC9DC7190217359255CEED1E3A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Annotations.dll", - "source": "vendor/pwsh/System.ComponentModel.Annotations.dll", - "bytes": 198480, - "sha256": "62C92AA7C62EB1CF7B506F0F3A9A1D63A4F6941EAAF9F6D8180A6C68E05BCD9B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Composition.dll", - "source": "vendor/pwsh/System.ComponentModel.Composition.dll", - "bytes": 718656, - "sha256": "7294AB56FC43F92EBB60700463262EFFE7CCD5BF36777CC78648E5D57F683240" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Composition.Registration.dll", - "source": "vendor/pwsh/System.ComponentModel.Composition.Registration.dll", - "bytes": 108336, - "sha256": "0CB63A3465A5C2A21C2B63B37586E9E61CA6A5454363905F3283707286F2354E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.DataAnnotations.dll", - "source": "vendor/pwsh/System.ComponentModel.DataAnnotations.dll", - "bytes": 16680, - "sha256": "6D8DD0135A19828A0B4F8F62FB79AEE094ADD47EBFFA6D2F3BCDC8CC2CC2425D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.dll", - "source": "vendor/pwsh/System.ComponentModel.dll", - "bytes": 30544, - "sha256": "283C355A88E039F5A49F791F89F1AF721FB82576447E23859A4CB269085A69D1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.EventBasedAsync.dll", - "source": "vendor/pwsh/System.ComponentModel.EventBasedAsync.dll", - "bytes": 46888, - "sha256": "6B5D96FB4CF007AA303FE4EFA2AEEB345A9AD2E93DABD668EB234DC793453CD4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Primitives.dll", - "source": "vendor/pwsh/System.ComponentModel.Primitives.dll", - "bytes": 79696, - "sha256": "A6268FE9416484303A678B72837B461AAA686D4CD4509430C1E4C5203397E63B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.TypeConverter.dll", - "source": "vendor/pwsh/System.ComponentModel.TypeConverter.dll", - "bytes": 767784, - "sha256": "9FC839CC0C4138DEEEE0080C72D29069D4EA22A4385FB78CDB4FEA6A911C30C0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Configuration.ConfigurationManager.dll", - "source": "vendor/pwsh/System.Configuration.ConfigurationManager.dll", - "bytes": 1046312, - "sha256": "68265C15B59229C842C7239D9456926096DDAB61AA39C2C29F90D98B12ADC609" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Configuration.dll", - "source": "vendor/pwsh/System.Configuration.dll", - "bytes": 19280, - "sha256": "87876603DABB331C85986D8AC56F0B317C4769051F369FB37409158B924F3FD0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Console.dll", - "source": "vendor/pwsh/System.Console.dll", - "bytes": 169768, - "sha256": "53B599B15B7EB4DB618946B943B00229D998D9BF5450CF23274B0F66BCDCB4EA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Core.dll", - "source": "vendor/pwsh/System.Core.dll", - "bytes": 23336, - "sha256": "FFFC038ABE34BB1BCDC13B57C146CD121B91D8BB2F8C80D2292F50C2563D8FAA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.Common.dll", - "source": "vendor/pwsh/System.Data.Common.dll", - "bytes": 2770728, - "sha256": "1CBEF4B075D6F36E914666010CE93FC831858622E775ABE9F73966B24C816AEE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.DataSetExtensions.dll", - "source": "vendor/pwsh/System.Data.DataSetExtensions.dll", - "bytes": 15656, - "sha256": "3FECFE3CAA4A21FF3BADC04354C036FDBE20636AD40A899B0F3CCF30471D39DE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.dll", - "source": "vendor/pwsh/System.Data.dll", - "bytes": 25384, - "sha256": "B801A07AD2B340F4808F1B683997A9D1AF9A70622FF104B1E89E37405403AC49" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.Odbc.dll", - "source": "vendor/pwsh/System.Data.Odbc.dll", - "bytes": 612144, - "sha256": "F609F522D9668A3761C21FD74D4C5EF8B1C76CF21E67377E550630110B5C66AB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.OleDb.dll", - "source": "vendor/pwsh/System.Data.OleDb.dll", - "bytes": 853808, - "sha256": "C6524C47A9584C10B7359A88D6ED5462E9BDFE4B1085129296E82F8C9A2F7BB0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.SqlClient.dll", - "source": "vendor/pwsh/System.Data.SqlClient.dll", - "bytes": 2434864, - "sha256": "C37A712C5E0B4F2D388611B0CEB9BCECA902FE8751075ABA4F5DCDCC291EDA03" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Design.dll", - "source": "vendor/pwsh/System.Design.dll", - "bytes": 23376, - "sha256": "3ED1B23FBA9A8E1B61F5036CC84058335F34BE254EACDFAE83F20FF484A422D8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Contracts.dll", - "source": "vendor/pwsh/System.Diagnostics.Contracts.dll", - "bytes": 16168, - "sha256": "E529CEFF3E677722C0779EFB90F464325C37F4598D2275684B6F3E44475DCA54" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Debug.dll", - "source": "vendor/pwsh/System.Diagnostics.Debug.dll", - "bytes": 15656, - "sha256": "97A2FAD35E9C07C9BB37B627FF5517A969421A0CA6C5D31DE665B9B72FB7F1CA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.DiagnosticSource.dll", - "source": "vendor/pwsh/System.Diagnostics.DiagnosticSource.dll", - "bytes": 501544, - "sha256": "1F3297FFFDFD0C97EC4C3AB7B628839B1F43FCB44193E44E96D4480631C5F07B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.EventLog.dll", - "source": "vendor/pwsh/System.Diagnostics.EventLog.dll", - "bytes": 366376, - "sha256": "47100510AA1489B538D0D8F58FF6D97AC79DBE3F09C541CB3E49B2CB8168E3F1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.EventLog.Messages.dll", - "source": "vendor/pwsh/System.Diagnostics.EventLog.Messages.dll", - "bytes": 800552, - "sha256": "5BF5DE8C874F06129A9A016F25A2532F323098F669EC766BEE212354F05ADB13" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.FileVersionInfo.dll", - "source": "vendor/pwsh/System.Diagnostics.FileVersionInfo.dll", - "bytes": 46888, - "sha256": "1A0073C382851890010F74B559F27A033EB783EEA1E38205782E6C8DACB5D10E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.PerformanceCounter.dll", - "source": "vendor/pwsh/System.Diagnostics.PerformanceCounter.dll", - "bytes": 300880, - "sha256": "B4E97299CB6BA71B5F4AB79774F1D5AFB72061CE7866188486770C9DC7D4F10B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Process.dll", - "source": "vendor/pwsh/System.Diagnostics.Process.dll", - "bytes": 329512, - "sha256": "7F70EA57A7868DFEEEA304D5DA3843B9B52D98767BC41A81162031BAA9361405" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.StackTrace.dll", - "source": "vendor/pwsh/System.Diagnostics.StackTrace.dll", - "bytes": 46888, - "sha256": "D696C411324CF024A89D830C57CB713C487950DB5FCD7A3F8790559675A02CE4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.TextWriterTraceListener.dll", - "source": "vendor/pwsh/System.Diagnostics.TextWriterTraceListener.dll", - "bytes": 67408, - "sha256": "308FA04540B284354DC0DB843B88EFCC562B43CB3EC5ABCE2ACE437521A31AEB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Tools.dll", - "source": "vendor/pwsh/System.Diagnostics.Tools.dll", - "bytes": 15144, - "sha256": "3EF95CD1BE21128929FF5A59EB656B79E2F1CF620E7EE8518AD54EF15642ABCC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.TraceSource.dll", - "source": "vendor/pwsh/System.Diagnostics.TraceSource.dll", - "bytes": 141136, - "sha256": "C707B27E877FB0C7D38848828AB08637915E2B7A05563AA1A08327DC69EEA911" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Tracing.dll", - "source": "vendor/pwsh/System.Diagnostics.Tracing.dll", - "bytes": 16168, - "sha256": "72F5F814B5E142C87F514A38AF6C08D103FF14DAA22160867F987151C7F26C39" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.DirectoryServices.AccountManagement.dll", - "source": "vendor/pwsh/System.DirectoryServices.AccountManagement.dll", - "bytes": 710472, - "sha256": "809BF2787752D82E9B75760FB742E32901470013066E193E5A3C5D21654CD0C7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.DirectoryServices.dll", - "source": "vendor/pwsh/System.DirectoryServices.dll", - "bytes": 1021736, - "sha256": "18A53AF12ED4CDFC3F6FCBE0787E53101ACA6B5C28B730ABEC789FBBE040801A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.DirectoryServices.Protocols.dll", - "source": "vendor/pwsh/System.DirectoryServices.Protocols.dll", - "bytes": 366408, - "sha256": "B5AE31C1B36D24149561B5DD47F3D6E4124CCD3CA7DC94DB21405D945F84BD00" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.dll", - "source": "vendor/pwsh/System.dll", - "bytes": 50472, - "sha256": "2F5F3CCB0BB4E730F8EEDE5585E64AA4164CFDA1EF0C07B3E6C20DD4036F11B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.Common.dll", - "source": "vendor/pwsh/System.Drawing.Common.dll", - "bytes": 923432, - "sha256": "924F083442617CADED6A137E8926791461D8735F2ECDF4821C56B5F836283155" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.Design.dll", - "source": "vendor/pwsh/System.Drawing.Design.dll", - "bytes": 15656, - "sha256": "3ECF8AD363EBA3E9DEDF01C63698BA257C0F743F734FF92B8FD03C5E7A2F5F26" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.dll", - "source": "vendor/pwsh/System.Drawing.dll", - "bytes": 20816, - "sha256": "E3FEC0493ED82D0F8BA9AB365428EA2008C5344184634D737DAD005F5FC4F1CE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.Primitives.dll", - "source": "vendor/pwsh/System.Drawing.Primitives.dll", - "bytes": 132944, - "sha256": "00CB49E97C49A30C2282904EB851B5C8D3F3E1B240C7DFF1BB850AC8E424BDAA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Dynamic.Runtime.dll", - "source": "vendor/pwsh/System.Dynamic.Runtime.dll", - "bytes": 16168, - "sha256": "D2A82A14EE1DFCE78D7B4F1F3D161935C56CB4C07EBC8A7DAFA8260745FA3771" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Formats.Asn1.dll", - "source": "vendor/pwsh/System.Formats.Asn1.dll", - "bytes": 239440, - "sha256": "027EC42F8A81D9999425FD9B75FDA1D20F610F2FC2F57C2B55D4D37B2A1FE75C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Formats.Nrbf.dll", - "source": "vendor/pwsh/System.Formats.Nrbf.dll", - "bytes": 137040, - "sha256": "9852CE3FD4EAE4B7638F910860E382BA8EDE1CF51628C3619691B7F24CD4DACA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Formats.Tar.dll", - "source": "vendor/pwsh/System.Formats.Tar.dll", - "bytes": 272168, - "sha256": "D4E9806746831BDE8670F8AF3A0DF7AA370678CDDB7950B28F4FAD0BA3353727" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Globalization.Calendars.dll", - "source": "vendor/pwsh/System.Globalization.Calendars.dll", - "bytes": 15656, - "sha256": "48F3B8DB0B6AF61E000C70C29C977B9CEE6D4C6FA0CF794418DFF29D1BA4E709" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Globalization.dll", - "source": "vendor/pwsh/System.Globalization.dll", - "bytes": 15656, - "sha256": "769DDE445009C78BE8D4CDB28DCC6603DF2218DAFE9491F5F1B915B1945A3F6E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Globalization.Extensions.dll", - "source": "vendor/pwsh/System.Globalization.Extensions.dll", - "bytes": 15144, - "sha256": "D3497DFC648BF299FCE08FC992918C542D1ED66EBD19EF726595369B0CD513B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.Brotli.dll", - "source": "vendor/pwsh/System.IO.Compression.Brotli.dll", - "bytes": 83752, - "sha256": "A14E6E6B390BF8F96A6EF0AA4450C2B0A8455202309C589D1BA8995C0D586611" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.dll", - "source": "vendor/pwsh/System.IO.Compression.dll", - "bytes": 419664, - "sha256": "9362497FA6D4ABE5B8D3DD9F2256564E9A5C84BFB8E9C709A741503DC602EBAA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.FileSystem.dll", - "source": "vendor/pwsh/System.IO.Compression.FileSystem.dll", - "bytes": 15184, - "sha256": "C2635E40545F18B1D0B15887C1AE5DE5504BB21A407E9371BEE83CE8974EA84D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.Native.dll", - "source": "vendor/pwsh/System.IO.Compression.Native.dll", - "bytes": 937296, - "sha256": "9FBB2CE965AE5F0AFB8601F9190326C6FC7A345F4B6E3FD4AF6740A4F6CF2548" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.ZipFile.dll", - "source": "vendor/pwsh/System.IO.Compression.ZipFile.dll", - "bytes": 100136, - "sha256": "79409DEBB2680FF86B7513FBDF622CAAEB791328358F4290EC6A78B403FD4F48" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.dll", - "source": "vendor/pwsh/System.IO.dll", - "bytes": 15656, - "sha256": "5329ADEC3B4B1C869E9814912B80E50E0094F795EFF2A7E360FCCB81A1BC55C9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.AccessControl.dll", - "source": "vendor/pwsh/System.IO.FileSystem.AccessControl.dll", - "bytes": 104232, - "sha256": "1C54898A3EC1EB02A9DFF3BA726C6328D1D0F306926A9A370C0C468E065BAB17" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.dll", - "source": "vendor/pwsh/System.IO.FileSystem.dll", - "bytes": 15696, - "sha256": "CB1CF391EF106BEECB0C7D835E2B24D03101AA7B6D4B279B98C235654ED521C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.DriveInfo.dll", - "source": "vendor/pwsh/System.IO.FileSystem.DriveInfo.dll", - "bytes": 55120, - "sha256": "AD0FA0F61602BABDB65280CFB5295D7F3905E7739431CA64D531919703729D2E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.Primitives.dll", - "source": "vendor/pwsh/System.IO.FileSystem.Primitives.dll", - "bytes": 15184, - "sha256": "2673EA5084171A31D826A8D93C91C7F34798385EAC41155932192A3C9066C20B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.Watcher.dll", - "source": "vendor/pwsh/System.IO.FileSystem.Watcher.dll", - "bytes": 87888, - "sha256": "7B5E2B5E413C7A234F971FE2C1E0A04063380018336F0C22F0C52955B6E7578F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.IsolatedStorage.dll", - "source": "vendor/pwsh/System.IO.IsolatedStorage.dll", - "bytes": 87848, - "sha256": "0494E0FC71AECF0AC89EA062E0887BCB7075DFEA986B2D87E88DEEF2ADDEB5B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.MemoryMappedFiles.dll", - "source": "vendor/pwsh/System.IO.MemoryMappedFiles.dll", - "bytes": 83752, - "sha256": "8D17D7A790D463C15C3D683A92D449A1D02C423DA680D2DC5B809C9CDE1B1C2F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Packaging.dll", - "source": "vendor/pwsh/System.IO.Packaging.dll", - "bytes": 321320, - "sha256": "6B178F0D551444A502A66055EBDF3FBB65FCDE25056C43866055C2B5B759F2AA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Pipelines.dll", - "source": "vendor/pwsh/System.IO.Pipelines.dll", - "bytes": 190248, - "sha256": "40A3244F2A4EBA94430C8813F7FD4778E15E1D957DCE9034D8DFA17725A0FCF0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Pipes.AccessControl.dll", - "source": "vendor/pwsh/System.IO.Pipes.AccessControl.dll", - "bytes": 15656, - "sha256": "1FA1F01D59F34DD66398931BBC51A4D1C981CE422FC149E7EEC914991A8588F6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Pipes.dll", - "source": "vendor/pwsh/System.IO.Pipes.dll", - "bytes": 165672, - "sha256": "A1858A82360321B757B41F8F7C33CB793D626A712CC29409E5E8D77493F62062" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Ports.dll", - "source": "vendor/pwsh/System.IO.Ports.dll", - "bytes": 165680, - "sha256": "378B0F569EAF4BA9E6B8DE64186A9CEAEE127FE0BFB13B7D3E832050F0A8D233" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.UnmanagedMemoryStream.dll", - "source": "vendor/pwsh/System.IO.UnmanagedMemoryStream.dll", - "bytes": 15656, - "sha256": "A18AC4B8B1DE6DFC0FA4F9CDA77EF9763E1721A52731B7AD8000BF733FA9FE8E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.AsyncEnumerable.dll", - "source": "vendor/pwsh/System.Linq.AsyncEnumerable.dll", - "bytes": 1292112, - "sha256": "7648125B74F53ABFE7E6F627E4D56D555BCF1AB1A0BFE4912C21969D47110C78" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.dll", - "source": "vendor/pwsh/System.Linq.dll", - "bytes": 698152, - "sha256": "F170005154FE77B1A5AF72179A454CA1D24FF3F805E6DC1A629EF1CF82D3E7AD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.Expressions.dll", - "source": "vendor/pwsh/System.Linq.Expressions.dll", - "bytes": 3639080, - "sha256": "E2627EA364EC3868E20B9F84D2C2A3ECF5FAAD4DC548F53A9D0C6A2D6F9FB282" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.Parallel.dll", - "source": "vendor/pwsh/System.Linq.Parallel.dll", - "bytes": 776016, - "sha256": "75459D9290969BC7E0BE2A352F7E4BC910FAD343F51C01359570CDACE014755D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.Queryable.dll", - "source": "vendor/pwsh/System.Linq.Queryable.dll", - "bytes": 182056, - "sha256": "DFCCADFD6219CE61D1C1263191C919FF2D095395E1B796B82ECF15C486A2B70C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Management.Automation.dll", - "source": "vendor/pwsh/System.Management.Automation.dll", - "bytes": 19658592, - "sha256": "ED8D39663E05435AAB59F1C7FB0AD20292E22B0CB12CF47AAD4B62B31E4D4126" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Management.dll", - "source": "vendor/pwsh/System.Management.dll", - "bytes": 833352, - "sha256": "AD07E0C250FE08CE6B23D63B7A564DA19E128D0A5A7ABA9CE49810ED7752A3DF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Memory.dll", - "source": "vendor/pwsh/System.Memory.dll", - "bytes": 161576, - "sha256": "A714F1A2B4B8D2695BEA4E5D2A85E62621CED26A7E4EBC5FCD61E85EE4838B77" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.dll", - "source": "vendor/pwsh/System.Net.dll", - "bytes": 17192, - "sha256": "D16A0494E92F84355FB5526A4E2F0587AB4F3C296E9971903E17523169E58E7E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Http.dll", - "source": "vendor/pwsh/System.Net.Http.dll", - "bytes": 1746728, - "sha256": "ED96D8A3995CF09E84981DB6EE8A5199E41FC2A2599749F70CF84ADA7D4CCE53" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Http.Json.dll", - "source": "vendor/pwsh/System.Net.Http.Json.dll", - "bytes": 128848, - "sha256": "DC58C398BAEF3AFFB78A9F2440E153D66BD8DC5F78079ECA71AD1BF18C726F19" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Http.WinHttpHandler.dll", - "source": "vendor/pwsh/System.Net.Http.WinHttpHandler.dll", - "bytes": 304960, - "sha256": "EA7676CB7C3FBA9167FD3B4308D422DBEC961282319267F924306744C2BC12F2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.HttpListener.dll", - "source": "vendor/pwsh/System.Net.HttpListener.dll", - "bytes": 534312, - "sha256": "5EF8C9B43FEDE96449D0AE2C99423658C1549121F31A260078E00173A330CAE4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Mail.dll", - "source": "vendor/pwsh/System.Net.Mail.dll", - "bytes": 481064, - "sha256": "B0C2C70BA2035E683E2232699E0547DF7AB4F361DAF4A5418C8F3B45EA147C13" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.NameResolution.dll", - "source": "vendor/pwsh/System.Net.NameResolution.dll", - "bytes": 124712, - "sha256": "EB7AC4D71D4DDD3A05129221F35C4C02926DAC2A9A664EBBC442B1307D6A74E4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.NetworkInformation.dll", - "source": "vendor/pwsh/System.Net.NetworkInformation.dll", - "bytes": 157480, - "sha256": "2A85A3E70D84F237E10A9BEAE7D4B1FDFE05CE0DE135D5CA45B46DE48491CDE3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Ping.dll", - "source": "vendor/pwsh/System.Net.Ping.dll", - "bytes": 91944, - "sha256": "1269783428EFC8C8A430C20ECEE9F931AA40D790B953E1D6C97FAAA71AC660B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Primitives.dll", - "source": "vendor/pwsh/System.Net.Primitives.dll", - "bytes": 210768, - "sha256": "6CA45C7299B6B76A5650F01E63A3290AC50432C6301CEA135C89FFC54AF72E25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Quic.dll", - "source": "vendor/pwsh/System.Net.Quic.dll", - "bytes": 358184, - "sha256": "11F53F8A10F14C789953C82CBEE8BEF8A9F0DDF3F86527125FBC6797E8E3860D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Requests.dll", - "source": "vendor/pwsh/System.Net.Requests.dll", - "bytes": 374568, - "sha256": "663A98F7F7A8082FA47A7197F362041E1479BF0A50D57A00872A2F128BD97121" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Security.dll", - "source": "vendor/pwsh/System.Net.Security.dll", - "bytes": 665424, - "sha256": "2CA204D66FE544FEF6D92E0EA1CDD2DF2F80160E6FBA6D04E3BA80382C69694D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.ServerSentEvents.dll", - "source": "vendor/pwsh/System.Net.ServerSentEvents.dll", - "bytes": 83792, - "sha256": "BF227E04A1D642D83211A4A1136FB31946B8B7148BBA5DDE2310D7ABCBCADE71" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.ServicePoint.dll", - "source": "vendor/pwsh/System.Net.ServicePoint.dll", - "bytes": 15144, - "sha256": "C77F8868279BF5F0D7129FDF5240ABC2527437B8928E8ABF06BC774438416C64" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Sockets.dll", - "source": "vendor/pwsh/System.Net.Sockets.dll", - "bytes": 530216, - "sha256": "B0386EBC2A62CDF0BA4D36973B3347C4372D9BFBCEFABD8E3CDD1B0194753653" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebClient.dll", - "source": "vendor/pwsh/System.Net.WebClient.dll", - "bytes": 165712, - "sha256": "52A615FD624996B513848D1CA1BDA1D02D49991AF0A000909779DDBF35FB17EC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebHeaderCollection.dll", - "source": "vendor/pwsh/System.Net.WebHeaderCollection.dll", - "bytes": 63272, - "sha256": "59E2FF9EE6AC647929BD689486F141645C2412B5B01CBF8A397D2888ADF0E764" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebProxy.dll", - "source": "vendor/pwsh/System.Net.WebProxy.dll", - "bytes": 42832, - "sha256": "41526195B63CD77C37DF1A99F621E2E5A8F40666E9613D974E9935D99F662905" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebSockets.Client.dll", - "source": "vendor/pwsh/System.Net.WebSockets.Client.dll", - "bytes": 104232, - "sha256": "45996C74C3C6C2E144EC006DEB5C70781F2E3C333782B815BB44B612D41EA27E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebSockets.dll", - "source": "vendor/pwsh/System.Net.WebSockets.dll", - "bytes": 243536, - "sha256": "7EF764FC11991DB23A90920DC3D49BBDDC6D2F766529D19463B17EAEE720CA71" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Numerics.dll", - "source": "vendor/pwsh/System.Numerics.dll", - "bytes": 15144, - "sha256": "49351764F0118538F1E531D91F416F325F362F6638C55896AF3D0FF631296B05" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Numerics.Vectors.dll", - "source": "vendor/pwsh/System.Numerics.Vectors.dll", - "bytes": 15656, - "sha256": "F7D2DBA3D30B09FA8CA802CDA5D0EE0827CB3AA44E1E19F790F62C390DDF5DE8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ObjectModel.dll", - "source": "vendor/pwsh/System.ObjectModel.dll", - "bytes": 79656, - "sha256": "9A7D09EF51C648C4CE51A0E21CDC64DEDE904420CCEC224D62C09FCEEE6B150A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Printing.dll", - "source": "vendor/pwsh/System.Printing.dll", - "bytes": 968488, - "sha256": "1CB55A01A300A2EBC0C8E14BD498005F4B34A0F4FC5F5CF7A88D50EC8AB8DCE6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.CoreLib.dll", - "source": "vendor/pwsh/System.Private.CoreLib.dll", - "bytes": 16033616, - "sha256": "63D83DD8169F23820F5D3BF0F0399F60E5E26200361787DFCB1C24CABC76E423" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.DataContractSerialization.dll", - "source": "vendor/pwsh/System.Private.DataContractSerialization.dll", - "bytes": 2062120, - "sha256": "A34745F8431BA8D38EAC37A65BCBD644C0F50AB88AFD66AF236A9A49C693B9F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Uri.dll", - "source": "vendor/pwsh/System.Private.Uri.dll", - "bytes": 255784, - "sha256": "D14188A1352F793F862726FA2C956DC270FB0E2578D46C6561F682FF2B4E110E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Windows.Core.dll", - "source": "vendor/pwsh/System.Private.Windows.Core.dll", - "bytes": 1906472, - "sha256": "0341B9E7466D143656E804D694A3C0B0BDB040FD44DE709047873661EC638884" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Windows.GdiPlus.dll", - "source": "vendor/pwsh/System.Private.Windows.GdiPlus.dll", - "bytes": 427816, - "sha256": "4ED46AD143DB84BDFA38C0973F1525296264FCCCA14AAB7D8C177982390C8260" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Xml.dll", - "source": "vendor/pwsh/System.Private.Xml.dll", - "bytes": 7788328, - "sha256": "65F1252BAD1A18D985A55D6434608E4A66DBA8A426310B2C6FDAE12B09E3FAF6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Xml.Linq.dll", - "source": "vendor/pwsh/System.Private.Xml.Linq.dll", - "bytes": 390992, - "sha256": "8A82630530FB3AD2E29A3D755BCE1F50497EC764804DDC7B014428A88E6DF16C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Context.dll", - "source": "vendor/pwsh/System.Reflection.Context.dll", - "bytes": 190256, - "sha256": "B7B226B5E08F84B6D6DDF77EED92A112ECA3D61087EF689F5BCCEB8996A736AE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.DispatchProxy.dll", - "source": "vendor/pwsh/System.Reflection.DispatchProxy.dll", - "bytes": 75560, - "sha256": "827499A045D2E71731BEB0391D392436D911E9D3195663E14F3CCA0F6B13691A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.dll", - "source": "vendor/pwsh/System.Reflection.dll", - "bytes": 16168, - "sha256": "2D0A26F77691CAC4D6522D9212FD70116DC183E1035B3AE4E6087C1EB9255F89" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Emit.dll", - "source": "vendor/pwsh/System.Reflection.Emit.dll", - "bytes": 309032, - "sha256": "F3CAEDCFF578946F80ECE81947A5ABB5D905BF1567D77D05C0725E0FF1FA1782" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Emit.ILGeneration.dll", - "source": "vendor/pwsh/System.Reflection.Emit.ILGeneration.dll", - "bytes": 15696, - "sha256": "9D003F3B5DF43C3062FA1E363E5F6B3ECC5D4B84213FC7397DA53E583494F390" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Emit.Lightweight.dll", - "source": "vendor/pwsh/System.Reflection.Emit.Lightweight.dll", - "bytes": 15656, - "sha256": "0461AC768E7F2E3BCD20A016C1E06E207092DF250458C868FF628C2E7766B748" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Extensions.dll", - "source": "vendor/pwsh/System.Reflection.Extensions.dll", - "bytes": 15144, - "sha256": "28B889CEA13DCE1ECFECF62CF887C1C9587255596333680C8441120E4EDA25C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Metadata.dll", - "source": "vendor/pwsh/System.Reflection.Metadata.dll", - "bytes": 1156904, - "sha256": "B453AC5B26229328268803037B95EBDFD9EA3C7AABD830513BD6A39CF73F7076" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Primitives.dll", - "source": "vendor/pwsh/System.Reflection.Primitives.dll", - "bytes": 15656, - "sha256": "B20C388943B74385F712CDAF74CDE691F6EEE14CB5320D8136C3D40B57C02D42" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.TypeExtensions.dll", - "source": "vendor/pwsh/System.Reflection.TypeExtensions.dll", - "bytes": 42832, - "sha256": "7CEDCBAB51CACAEEECA6A766F92A2B93FD74CA88D326BADFAD090067D9D19150" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.Extensions.dll", - "source": "vendor/pwsh/System.Resources.Extensions.dll", - "bytes": 194344, - "sha256": "CFBD1384747751A7BA4CE8A16F191501CDB762567029FF57F6E768AD4D4C58FD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.Reader.dll", - "source": "vendor/pwsh/System.Resources.Reader.dll", - "bytes": 15144, - "sha256": "3E83796FDDDA54188EBE691C4B45641BB2FE7780F8E4D1928D5F4B435BB6C6F8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.ResourceManager.dll", - "source": "vendor/pwsh/System.Resources.ResourceManager.dll", - "bytes": 15656, - "sha256": "DA9AD363EE8A17881D33768D75A3D9689605C626415AF780D0FF5BA743C3A3F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.Writer.dll", - "source": "vendor/pwsh/System.Resources.Writer.dll", - "bytes": 50984, - "sha256": "E089809FA22829491D90FB8BCBA9210EE7F37A7C9D07D0FA2E333C15016B87A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Caching.dll", - "source": "vendor/pwsh/System.Runtime.Caching.dll", - "bytes": 202560, - "sha256": "DB5D7363FFAF4616FD9FF0225DEFFE1644418D792AED29C90E63924A852A6838" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.CompilerServices.Unsafe.dll", - "source": "vendor/pwsh/System.Runtime.CompilerServices.Unsafe.dll", - "bytes": 15144, - "sha256": "3D00BF59EE665068C013BBF7258A0BF46A86CDACEC9FC5BD727ABFF9D190D3E5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.CompilerServices.VisualC.dll", - "source": "vendor/pwsh/System.Runtime.CompilerServices.VisualC.dll", - "bytes": 30504, - "sha256": "84820BF55EDAEEBCFC2F8AA5ED64ECDCB8EDA8A6ED1D44AA9DFD28C8AE362FD1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.dll", - "source": "vendor/pwsh/System.Runtime.dll", - "bytes": 44840, - "sha256": "2C733C433C3D26C0EDB1EB89C9ED7A53CB3194092D1A56A79BF14AB71F4A3425" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Extensions.dll", - "source": "vendor/pwsh/System.Runtime.Extensions.dll", - "bytes": 17704, - "sha256": "71CAB63C02F634D86377BB06C481E733347ABC22B56106B39228A1A01305DE10" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Handles.dll", - "source": "vendor/pwsh/System.Runtime.Handles.dll", - "bytes": 15656, - "sha256": "784E5ADCC91FB6C3154C3F4E9C7C4E311892A2182686205DC4C2B3118C8355BA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.InteropServices.dll", - "source": "vendor/pwsh/System.Runtime.InteropServices.dll", - "bytes": 112464, - "sha256": "F3587B04D04F05C5A22E129EBE3DABE34243315E035AB6BBD91A4A3350FBAD18" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.InteropServices.JavaScript.dll", - "source": "vendor/pwsh/System.Runtime.InteropServices.JavaScript.dll", - "bytes": 50984, - "sha256": "A865BF2368D23E2328C68DAC87B136E71E00BA8136A0CF4B7EC10F71CC3525A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", - "source": "vendor/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", - "bytes": 15656, - "sha256": "4E38FBC3D52D0EA22A13D8569D24F40C3C6043DAA28DDF242CF77288FBBE2DEA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Intrinsics.dll", - "source": "vendor/pwsh/System.Runtime.Intrinsics.dll", - "bytes": 17192, - "sha256": "58A88BA0B94E29F26C3846697A6A251FE7677CBCC7C2B8950090D4AF59109720" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Loader.dll", - "source": "vendor/pwsh/System.Runtime.Loader.dll", - "bytes": 15656, - "sha256": "B3A9500ED9E7F4A698FB46513B8C73B5AA752331597A544E2722B2C5DB262B09" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Numerics.dll", - "source": "vendor/pwsh/System.Runtime.Numerics.dll", - "bytes": 354088, - "sha256": "9F067C312E0C0C097D56BB3487933DCDA758D547480AB76E5A2987AEADD8FD7E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.dll", - "bytes": 17192, - "sha256": "CA3289757E8F3BC2BCCBC0B02205CD0F7A21AD9845417FB9496A7C9FA4117DF5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Formatters.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Formatters.dll", - "bytes": 124712, - "sha256": "7832E9DCC1A36276B2DF00427C5C926F1A48DFF2D03977632A83AD138D853013" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Json.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Json.dll", - "bytes": 15656, - "sha256": "3E23CF2C3DABB7F3D58A88838884FFA68E0A73E40EB0AFCB316BA29DDD567A38" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Primitives.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Primitives.dll", - "bytes": 38736, - "sha256": "1C2DED997862CD07FD93BD8450A98517875F7BB52A4C4E651A4D3B8832D7DB9C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Xml.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Xml.dll", - "bytes": 16680, - "sha256": "6E9DFAA42EEF9F406696E5D2372C100143BD47E0E95EC21158FE5FA7DD2EE428" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.AccessControl.dll", - "source": "vendor/pwsh/System.Security.AccessControl.dll", - "bytes": 227112, - "sha256": "AA6BF70A513CB43DAAD41F91BDE35740EC28456481639A57508BE02B08F1969B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Claims.dll", - "source": "vendor/pwsh/System.Security.Claims.dll", - "bytes": 100136, - "sha256": "6AE287447243764B203F58EABBC8E5B9EBCA135F6174AD73DD06AE572936DCC4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Algorithms.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Algorithms.dll", - "bytes": 17192, - "sha256": "D679F5CAF8BCF0007E4003D5CE728EF3DE03EB88F5DC90528AEEE1D266436318" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Cng.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Cng.dll", - "bytes": 16168, - "sha256": "E6D327C11059988B2C12584040C588CAEC73334E9745FFE85C3522BC85756F49" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Csp.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Csp.dll", - "bytes": 16168, - "sha256": "629D0E04F3605F8CABCB04105EB7CA369D3AF023FBE171CBACB60476395509A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.dll", - "source": "vendor/pwsh/System.Security.Cryptography.dll", - "bytes": 2549584, - "sha256": "ACB3E95912A1B3D2CC96BFD42346328462A930AC77858DFC62D7D712CEED4F5E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Encoding.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Encoding.dll", - "bytes": 15656, - "sha256": "7D824AC50A0D6A75B8F85BA007BD84BEC6714CCFFDEBEAD00A01EDB9CCD3918E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.OpenSsl.dll", - "source": "vendor/pwsh/System.Security.Cryptography.OpenSsl.dll", - "bytes": 15656, - "sha256": "4F6747F232BF9C3AC7B5352D818E31131DCC299503806A8190E9FBEF95D4A50A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Pkcs.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Pkcs.dll", - "bytes": 767784, - "sha256": "AC3A317E89EF612F3DC6169887C8A1B0A692121E1FF48D402F75F4A82DA4899A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Primitives.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Primitives.dll", - "bytes": 15656, - "sha256": "E17D719732B43BA6BEB751A7EF55390B9E9B9B659826FABF1288CD03AF0241C2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.ProtectedData.dll", - "source": "vendor/pwsh/System.Security.Cryptography.ProtectedData.dll", - "bytes": 59216, - "sha256": "1E5826CE27C48902506553C8BC7C648004A2767ED786979538D725BA2561E2D9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.X509Certificates.dll", - "source": "vendor/pwsh/System.Security.Cryptography.X509Certificates.dll", - "bytes": 16680, - "sha256": "752EB010626928B51584B2B58D7C67110D16E73B9ACE071ADC6009F33275636C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Xml.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Xml.dll", - "bytes": 452392, - "sha256": "FF3E018DA84A0294FDD2A4F8A120CBC05B40E99CE4AD57CE45809AB3280C6DEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.dll", - "source": "vendor/pwsh/System.Security.dll", - "bytes": 18216, - "sha256": "4C5D662D946BA84F5159FAA8197661AAD054B98899977F5BFC3C4F7A66AC13FC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Permissions.dll", - "source": "vendor/pwsh/System.Security.Permissions.dll", - "bytes": 190248, - "sha256": "37D4C12B1BAC234541871123511525B680089A61758CE6F28B09A18F8F0B39E6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Principal.dll", - "source": "vendor/pwsh/System.Security.Principal.dll", - "bytes": 15144, - "sha256": "40671DC07A55EC29B00DA6D45610CB663A8C03AE3A686695A9E28412AB87ACF5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Principal.Windows.dll", - "source": "vendor/pwsh/System.Security.Principal.Windows.dll", - "bytes": 177960, - "sha256": "4C6EE4BC691D111FF4500A6D455DEEEEB3F56F14F5CD686F404BA063C8D2741B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.SecureString.dll", - "source": "vendor/pwsh/System.Security.SecureString.dll", - "bytes": 15656, - "sha256": "83941FEE8EB17E7215855D7D655243DC5D1D5ED631C92C39F4163D4A491AEBE8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.dll", - "source": "vendor/pwsh/System.ServiceModel.dll", - "bytes": 25160, - "sha256": "CF390C8379F3249937DA11DAD3A5BA88804A7453503A246BE01FC849B899653B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Duplex.dll", - "source": "vendor/pwsh/System.ServiceModel.Duplex.dll", - "bytes": 15432, - "sha256": "5E7D8985922C7D7D673D2D204C2F6D12E3F70F70D0E9FF15507FECD2026A8815" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Http.dll", - "source": "vendor/pwsh/System.ServiceModel.Http.dll", - "bytes": 481096, - "sha256": "4B4F5C934A4CBAD1227507570697BC75F7E332A7A5D85EC62F9A24E5897E7B32" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.NetFramingBase.dll", - "source": "vendor/pwsh/System.ServiceModel.NetFramingBase.dll", - "bytes": 477000, - "sha256": "98175B9ABD29326B1C410AA6ED82370677F1781A5A26B7A9AB8479C76A4A6EF2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.NetTcp.dll", - "source": "vendor/pwsh/System.ServiceModel.NetTcp.dll", - "bytes": 137032, - "sha256": "8169433D0DF8D3186ABD4C94CCFF3987CA267D56F5A384CF539E613C997FC8FA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Primitives.dll", - "source": "vendor/pwsh/System.ServiceModel.Primitives.dll", - "bytes": 5388112, - "sha256": "C53A7289B255E6A32E42FEB63CE24E32C2E0FAF4B5972EC2EC83BB17C4F01EF9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Security.dll", - "source": "vendor/pwsh/System.ServiceModel.Security.dll", - "bytes": 16928, - "sha256": "99EC51765CF53B36DC1064A48DB4C910EDB17C92E176E03B947E5E200B82F7D2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Syndication.dll", - "source": "vendor/pwsh/System.ServiceModel.Syndication.dll", - "bytes": 313152, - "sha256": "CF572334CFBB7791716282B0802DD2A33691B55B13FE3F65A45058E57700EECA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Web.dll", - "source": "vendor/pwsh/System.ServiceModel.Web.dll", - "bytes": 16680, - "sha256": "58A681BB145F2CDC9E996B48E40FFFAEA30DE469A535DDB985454A4AF8828D50" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceProcess.dll", - "source": "vendor/pwsh/System.ServiceProcess.dll", - "bytes": 15696, - "sha256": "203F52B3F352E0E3DF54B9F588131743EB562AF8612BFA2C2DEBAB21B29110FA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceProcess.ServiceController.dll", - "source": "vendor/pwsh/System.ServiceProcess.ServiceController.dll", - "bytes": 149336, - "sha256": "2CEC344EF1D23339A5257FA8E9B96D4842B1E15AFD04D1A56261B1990A0F9B7C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Speech.dll", - "source": "vendor/pwsh/System.Speech.dll", - "bytes": 1386304, - "sha256": "1A06AB169EACB6FB9CDF76096284284236F6C55A3F588CAA24BD7AF06A8F4A10" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encoding.CodePages.dll", - "source": "vendor/pwsh/System.Text.Encoding.CodePages.dll", - "bytes": 861992, - "sha256": "9D8D9E0FA33437502FD659FCB726E295CF439E56BDB044970FE5B715D7813E37" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encoding.dll", - "source": "vendor/pwsh/System.Text.Encoding.dll", - "bytes": 15656, - "sha256": "1E717906C9F94DAA686B68E9E86F4A1C08916DB62B05AF5A17661A5B83B9C7A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encoding.Extensions.dll", - "source": "vendor/pwsh/System.Text.Encoding.Extensions.dll", - "bytes": 15656, - "sha256": "F507CC18B0449947A86606DFE9281B89316B39C1BA60A9249E41C0A1CD30E201" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encodings.Web.dll", - "source": "vendor/pwsh/System.Text.Encodings.Web.dll", - "bytes": 124712, - "sha256": "AB138D71B868D606ACD1D48EF17B9F12E51F75ECD297120ADD0C4552F6758160" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Json.dll", - "source": "vendor/pwsh/System.Text.Json.dll", - "bytes": 1890088, - "sha256": "ABCECB266DFD7AF409EBA45CD2FD581D40F72D5FE916A1A95F05B32AC738968F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.RegularExpressions.dll", - "source": "vendor/pwsh/System.Text.RegularExpressions.dll", - "bytes": 1034024, - "sha256": "CC5C01E9C1591DF68708CA6386C1259B0AC34257336CF53106CE784F9D1175C0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.AccessControl.dll", - "source": "vendor/pwsh/System.Threading.AccessControl.dll", - "bytes": 75600, - "sha256": "78A2D7FD94410ABEFEFC9AF09FADB9FBDC7069F5572949C62E81423DC9F8A33B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Channels.dll", - "source": "vendor/pwsh/System.Threading.Channels.dll", - "bytes": 157480, - "sha256": "104051A542F04760C2C89998A49396E0F4FCE403E86D7D976984280BF835C966" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.dll", - "source": "vendor/pwsh/System.Threading.dll", - "bytes": 83792, - "sha256": "B1AAE26002952704D7D7913170DFD3C786DE6DEA50463BB671A9AA99423320BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Overlapped.dll", - "source": "vendor/pwsh/System.Threading.Overlapped.dll", - "bytes": 15656, - "sha256": "239A90D22546A4ED01A05BFA8CEBA846E4C73F73BA6C2B3E06E176F859890DB5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.Dataflow.dll", - "source": "vendor/pwsh/System.Threading.Tasks.Dataflow.dll", - "bytes": 472872, - "sha256": "1412A7614F9FAFFE37E102384B0917B436BA3CAC685F8ED64607912C805DB9E9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.dll", - "source": "vendor/pwsh/System.Threading.Tasks.dll", - "bytes": 16680, - "sha256": "3C3CBA2C78819A31B400B52014678A4AF1DC1F482B2424E38B85E28EEF1C40D0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.Extensions.dll", - "source": "vendor/pwsh/System.Threading.Tasks.Extensions.dll", - "bytes": 15656, - "sha256": "DAAC261AD95033C320720BE3AE53263ABD6344FD548BA36F405D2D5AB39207F2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.Parallel.dll", - "source": "vendor/pwsh/System.Threading.Tasks.Parallel.dll", - "bytes": 132904, - "sha256": "47733F454E9D7DA1BEC529A36AE86DB431E8A89AD1FE84641E80C9207D91F4C7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Thread.dll", - "source": "vendor/pwsh/System.Threading.Thread.dll", - "bytes": 15656, - "sha256": "0B016736E7E4995A7E0A127A7BAEB6F8487A97433E2DD034B8CC8DD5FA1048AC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.ThreadPool.dll", - "source": "vendor/pwsh/System.Threading.ThreadPool.dll", - "bytes": 15656, - "sha256": "104C0F37199430CCEBF3B81EB28E4C072547447075EACFF8D488954035BD231F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Timer.dll", - "source": "vendor/pwsh/System.Threading.Timer.dll", - "bytes": 15144, - "sha256": "632C125112639BD454C6E07DC2481E9EEC8D9FC300FA0EA6DF622729DE499A77" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Transactions.dll", - "source": "vendor/pwsh/System.Transactions.dll", - "bytes": 16680, - "sha256": "0F55A3E2F9BAA40E596E93C76D97C0F5B4A53775D8865F57877E77FB90B8E09D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Transactions.Local.dll", - "source": "vendor/pwsh/System.Transactions.Local.dll", - "bytes": 640808, - "sha256": "B7CE5D6C73B6E2A298946474473C6E12AC2A81584EFAC155F85ACAD4CD1695E0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ValueTuple.dll", - "source": "vendor/pwsh/System.ValueTuple.dll", - "bytes": 15696, - "sha256": "1C02D7D7BEF69434A1F9EA0BFAF4A83321E644A5478D39681849883BC1114A61" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Web.dll", - "source": "vendor/pwsh/System.Web.dll", - "bytes": 15144, - "sha256": "88650D1871B5BAC043665F06610799862B66303794FB76DE0DB5E58C86E1B1D9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Web.HttpUtility.dll", - "source": "vendor/pwsh/System.Web.HttpUtility.dll", - "bytes": 63312, - "sha256": "1CFF49AD07732F67D54CFDF00100692499B24F00955DED4DB528012F5F11A9A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Web.Services.Description.dll", - "source": "vendor/pwsh/System.Web.Services.Description.dll", - "bytes": 800576, - "sha256": "AB6BFC2802083328D63EA2A520ED6F5DCF2AD2E0D67276A3884D577A2C13EC7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Controls.Ribbon.dll", - "source": "vendor/pwsh/System.Windows.Controls.Ribbon.dll", - "bytes": 1439528, - "sha256": "E7E47AF351ED12E58337B7877F6E1C53A0E652D0D42DEF2CA1F61E81F0C93EA1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.dll", - "source": "vendor/pwsh/System.Windows.dll", - "bytes": 15656, - "sha256": "8A97C2FC1A57682ED6031CB27A2D0B1BD352EEE9A4AF53C4411F8C0304DEE2FE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Extensions.dll", - "source": "vendor/pwsh/System.Windows.Extensions.dll", - "bytes": 120616, - "sha256": "EB33D2AF1F7F76F7C179856025981A1F30766D3F7CE9F61D3A09DD22B59553E2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.Design.dll", - "source": "vendor/pwsh/System.Windows.Forms.Design.dll", - "bytes": 6092584, - "sha256": "DE308D2141B4EA269DAB0E30510116646F625A16A6493B13218E7428DBEF08D7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.Design.Editors.dll", - "source": "vendor/pwsh/System.Windows.Forms.Design.Editors.dll", - "bytes": 16168, - "sha256": "B5B8172A40E5E72A54C6B8C4BB3C926CA4EE6248D5BEBE511B060F0D6D053003" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.dll", - "source": "vendor/pwsh/System.Windows.Forms.dll", - "bytes": 13715240, - "sha256": "65D1A845A47F955CD9E4C893E9FDCAE255F63894F45BC0B721F5DE22F223C753" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.Primitives.dll", - "source": "vendor/pwsh/System.Windows.Forms.Primitives.dll", - "bytes": 3622736, - "sha256": "215C6626C23DC962B32A42B218ABD653D78EA09E3D1162C65B0C3BB836A96D6C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Input.Manipulations.dll", - "source": "vendor/pwsh/System.Windows.Input.Manipulations.dll", - "bytes": 141096, - "sha256": "B51133A16A9EA33269D1AA8A9F9CB7171FFC339D251066903D17209FAAA7360F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Presentation.dll", - "source": "vendor/pwsh/System.Windows.Presentation.dll", - "bytes": 30504, - "sha256": "9C752F71E3C79BEBF747A4BE28FA791404C30EE7486893B4D73CD3EF3DDD4654" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Primitives.dll", - "source": "vendor/pwsh/System.Windows.Primitives.dll", - "bytes": 386856, - "sha256": "7FD4DC6CF1C20115ADB7EC2519A4A2BE1805E2C2A6133E32BCF52B598020E152" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xaml.dll", - "source": "vendor/pwsh/System.Xaml.dll", - "bytes": 1390416, - "sha256": "DF6837BCC37DF3E0AC022A38F545BCF6155A57A55ADE8E4500D481A5F48A3544" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.dll", - "source": "vendor/pwsh/System.Xml.dll", - "bytes": 23336, - "sha256": "77C887B9C9D608538C78607D752E3567B090DD8BD8D41CAADE2201D34A1BE8BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.Linq.dll", - "source": "vendor/pwsh/System.Xml.Linq.dll", - "bytes": 15656, - "sha256": "A3581BC00766E7A78974009B2FAFB83B0D0158C3A1619AF11E9389981DE1DF1A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.ReaderWriter.dll", - "source": "vendor/pwsh/System.Xml.ReaderWriter.dll", - "bytes": 21800, - "sha256": "4E8265FE5D68F42648394F0D6CA0C06C60664F5E0C7456BD65C942C664891BD0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.Serialization.dll", - "source": "vendor/pwsh/System.Xml.Serialization.dll", - "bytes": 16168, - "sha256": "66AC0372545EDB9C426E8E884292F81B26E6E185E7A90729695497F1FDCE31BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XDocument.dll", - "source": "vendor/pwsh/System.Xml.XDocument.dll", - "bytes": 15656, - "sha256": "7E9217B005D31B73E5CD83BC6DA85217677A773020AC736C67D53E6375FF5908" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XmlDocument.dll", - "source": "vendor/pwsh/System.Xml.XmlDocument.dll", - "bytes": 15656, - "sha256": "E604D3C3D891756E18817637E3323C9E67F4DAFB404C877CE3332E598669940C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XmlSerializer.dll", - "source": "vendor/pwsh/System.Xml.XmlSerializer.dll", - "bytes": 17704, - "sha256": "AC7EFC90B0E73146CFA23A91D97E7BB05E1C3CF02416A7EE5853EB39149AE985" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XPath.dll", - "source": "vendor/pwsh/System.Xml.XPath.dll", - "bytes": 15696, - "sha256": "3E3D6A10185F44C1B78686E08DE8FD59EDF5BA4D953C2FFA4B277B369792DD71" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XPath.XDocument.dll", - "source": "vendor/pwsh/System.Xml.XPath.XDocument.dll", - "bytes": 30504, - "sha256": "EF4F101B820CD5BA369C6FE06729BDC7E2E2DD023D720058B546DB32227FB500" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ThirdPartyNotices.txt", - "source": "vendor/pwsh/ThirdPartyNotices.txt", - "bytes": 210587, - "sha256": "5A2D792AA9DAE45DA7579398CA08C0FBB47D78C563CCE492E311B9D5EB9A2142" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationClient.dll", - "source": "vendor/pwsh/UIAutomationClient.dll", - "bytes": 407336, - "sha256": "6074F520F4630DC6DAF4E1CD2F714E4123C4CFCDAD909C76ECC7CF69026C719F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationClientSideProviders.dll", - "source": "vendor/pwsh/UIAutomationClientSideProviders.dll", - "bytes": 857896, - "sha256": "1C287748EB1C5B5BE118FFFD6E9254A67D959A4EA04EF2EE245236CBD6C015C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationProvider.dll", - "source": "vendor/pwsh/UIAutomationProvider.dll", - "bytes": 63272, - "sha256": "3A9A965E8759EE679612C3A59BAFE70DFBADA3CE1260D815B1D2D62F941105B2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationTypes.dll", - "source": "vendor/pwsh/UIAutomationTypes.dll", - "bytes": 296744, - "sha256": "9A26003F55DAC193DAEED86CA35BC01B72DBBEDBFDFF9846E150CEEF80D3D70A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/vcruntime140_cor3.dll", - "source": "vendor/pwsh/vcruntime140_cor3.dll", - "bytes": 134600, - "sha256": "23AB6BFC1D127318005279A4608401F5C3FB872B5BD32A706863E8D0C2C5065F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/WindowsBase.dll", - "source": "vendor/pwsh/WindowsBase.dll", - "bytes": 2176808, - "sha256": "ABBEA4B64EFF6A29767E64F99E2574A827B43C383C97145DC9FB316964A8DFB8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/WindowsFormsIntegration.dll", - "source": "vendor/pwsh/WindowsFormsIntegration.dll", - "bytes": 214864, - "sha256": "0C2C08B731513110D237865AB84E16A22BA05073F6301EA28E894BA343E99415" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/wpfgfx_cor3.dll", - "source": "vendor/pwsh/wpfgfx_cor3.dll", - "bytes": 1956136, - "sha256": "38FFCC5A72DEDFC518D03CCC9B8F5A20D5C8EC84578B42BC30771D79955C2E11" - } - ] -} +{ + "schema": 2, + "product": "Direct Internet Method", + "version": "1.5.5-rc1", + "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", + "offlineRuntime": true, + "normalRunRequiresAdmin": false, + "installOrPrivilegedUpgradeRequiresAdmin": true, + "directUpdate": { + "transport": "GitHub latest release HTTPS", + "privilegeModel": "DirectInternetMethodSvc performs download/verification/installer handoff", + "userUacRequired": false, + "integrity": "GitHub asset SHA-256 digest must equal SHA256SUMS.txt and downloaded installer SHA-256" + }, + "bundledPowerShellVersion": "7.6.6", + "bundledPowerShellArchiveSha256": "02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860", + "files": [ + { + "scope": "user", + "file": "app/DirectInternetMethod.exe", + "source": "app/DirectInternetMethod.exe", + "bytes": 139900030, + "sha256": "FBC67BE3B361308953CD07899A160F1A79A8B20124E54B90585BE00C8B391513" + }, + { + "scope": "user", + "file": "app/DirectInternetMethod.ico", + "source": "app/DirectInternetMethod.ico", + "bytes": 33478, + "sha256": "00D25A881AB5CA731437F5C631C031FD0FC0A8223F45181ACB6240EAA28AB01F" + }, + { + "scope": "user", + "file": "app/Status.ps1", + "source": "app/Status.ps1", + "bytes": 6394, + "sha256": "EC4B00233C1A0BD462D7AD674E54254F02BF462A6D3D53ACED47888CEE4A3447" + }, + { + "scope": "user", + "file": "app/router_gateway/providers.json", + "source": "../router_gateway/providers.json", + "bytes": 7111, + "sha256": "D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD" + }, + { + "scope": "user", + "file": "docs/README-FA.md", + "source": "docs/README-FA.md", + "bytes": 2191, + "sha256": "81C4242367FB6E15EBB2E03F0B4424EFD173091A46FE724AAEB1E9F22DD1B491" + }, + { + "scope": "user", + "file": "docs/ARCHITECTURE.md", + "source": "docs/ARCHITECTURE.md", + "bytes": 1863, + "sha256": "E4AC239D27A060B5CA0EF55C46B99FC9196725B44E07AB16F67C35E162C86BB8" + }, + { + "scope": "user", + "file": "RELEASE.json", + "source": "RELEASE.json", + "bytes": 5851, + "sha256": "AFB3CC3ED64FD3D1E4D9460C1171899FAE0B9233AB96337498BAABF0CF353C5F" + }, + { + "scope": "privileged", + "file": "app/DirectInternetMethod.Service.exe", + "source": "app/DirectInternetMethod.Service.exe", + "bytes": 73627467, + "sha256": "EE02C5188BAF3997DDB56787A671AA7947D2BE895C208271D4AE5ECFE309FF46" + }, + { + "scope": "privileged", + "file": "app/Start-Direct.ps1", + "source": "app/Start-Direct.ps1", + "bytes": 26860, + "sha256": "B186872EDE733AE876FF1463E030988E9149A2CC88188F4B6C1FA5B564AEFC05" + }, + { + "scope": "privileged", + "file": "app/Stop-Direct.ps1", + "source": "app/Stop-Direct.ps1", + "bytes": 10426, + "sha256": "4E113237768506F940870CA7550EB3DD6A1B41C51879DD01D944A5E77BD96327" + }, + { + "scope": "privileged", + "file": "app/Recovery.ps1", + "source": "app/Recovery.ps1", + "bytes": 6157, + "sha256": "A406F1B23EF95C77C6B2FB4ECDB6337E582AB06D9A7927DEB31E916F06C870E1" + }, + { + "scope": "privileged", + "file": "app/SafeLegacyProviderRollback.ps1", + "source": "app/SafeLegacyProviderRollback.ps1", + "bytes": 13397, + "sha256": "522D78CB2A40A917B4E86DD7F98F4D3B076FD8A551B97C4B0C211FDFC7D9B322" + }, + { + "scope": "privileged", + "file": "bin/ctrld/ctrld.exe", + "source": "bin/ctrld/ctrld.exe", + "bytes": 7602136, + "sha256": "FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD" + }, + { + "scope": "privileged", + "file": "bin/ctrld/ctrld.toml", + "source": "bin/ctrld/ctrld.toml", + "bytes": 540, + "sha256": "B056AD8A51078208AED39744D48C4F4369AE5BA1E76F7D6F84B027C34CE1314B" + }, + { + "scope": "privileged", + "file": "bin/ctrld/LICENSE.txt", + "source": "bin/ctrld/LICENSE.txt", + "bytes": 1122, + "sha256": "5B166F4937F5D1ADED9C6D90675524ABAB6A6F0EBA1E226FA30119F37DBAB754" + }, + { + "scope": "privileged", + "file": "bin/zapret/winws.exe", + "source": "bin/zapret/winws.exe", + "bytes": 223232, + "sha256": "A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8" + }, + { + "scope": "privileged", + "file": "bin/zapret/WinDivert.dll", + "source": "bin/zapret/WinDivert.dll", + "bytes": 47616, + "sha256": "C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2" + }, + { + "scope": "privileged", + "file": "bin/zapret/WinDivert64.sys", + "source": "bin/zapret/WinDivert64.sys", + "bytes": 94144, + "sha256": "8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2" + }, + { + "scope": "privileged", + "file": "bin/zapret/cygwin1.dll", + "source": "bin/zapret/cygwin1.dll", + "bytes": 2954293, + "sha256": "103104A52E5293CE418944725DF19E2BF81AD9269B9A120D71D39028E821499B" + }, + { + "scope": "privileged", + "file": "bin/zapret/hosts.txt", + "source": "bin/zapret/hosts.txt", + "bytes": 287, + "sha256": "48EF43491901AB4336DDDFE6324C5820AC8B90F59511F138A7202DBCC8B033A3" + }, + { + "scope": "privileged", + "file": "bin/zapret/adult-fallback-hosts.txt", + "source": "bin/zapret/adult-fallback-hosts.txt", + "bytes": 448, + "sha256": "25BA72F6D56EF1C405115C7AA5D6ED1985E8F6D56F306A95D5AD800602A75288" + }, + { + "scope": "privileged", + "file": "bin/zapret/strong-override-hosts.txt", + "source": "bin/zapret/strong-override-hosts.txt", + "bytes": 99, + "sha256": "A371B754B201D1E87A9C0474BDEA1821539D7DAF20B5DA6C8F5105A1F85C8506" + }, + { + "scope": "privileged", + "file": "bin/zapret/LICENSE.txt", + "source": "bin/zapret/LICENSE.txt", + "bytes": 1090, + "sha256": "1FACB290FF6425CF6AB61FC882E71FFB0DF50FDA527FEF90D4098935097BD984" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Accessibility.dll", + "source": "vendor/pwsh/Accessibility.dll", + "bytes": 20816, + "sha256": "7E2CA83155D9F4B80909036B964461E1D020D25E107E378161AE7A16F52102A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/build.manifest", + "source": "vendor/pwsh/build.manifest", + "bytes": 3874, + "sha256": "58A354C02F0674CF951CEFF4E780F7532EAEE9C814B1FC75BDD3B943DD109F7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/build.manifest.sig", + "source": "vendor/pwsh/build.manifest.sig", + "bytes": 3277, + "sha256": "5E8F9EBCEE16A53E65BF204C4CFF40CF137D567229143126C9BA789E5B75FCA1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clretwrc.dll", + "source": "vendor/pwsh/clretwrc.dll", + "bytes": 317776, + "sha256": "557B5E6D5CC6C0BAECA172B987B023BEDB8B0C0C0ADF1352186768A926F671DD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clrgc.dll", + "source": "vendor/pwsh/clrgc.dll", + "bytes": 674088, + "sha256": "1AD35FC3FC5D8E26E007CFA21377579C748F1EB78BC7AC820813B505398E6403" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clrgcexp.dll", + "source": "vendor/pwsh/clrgcexp.dll", + "bytes": 711464, + "sha256": "2271063DED2A3A5A520380147300B483CA30403F0ACADCF03BAF8FBA0B53E23F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clrjit.dll", + "source": "vendor/pwsh/clrjit.dll", + "bytes": 2092328, + "sha256": "8B3E57D21CC9914A0F2DF5DF3B0FA5FC426D91282846EB88DA07736CFB7416AA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/coreclr.dll", + "source": "vendor/pwsh/coreclr.dll", + "bytes": 4614952, + "sha256": "20EA763D5AD8BE8656A3123C4AD5AFDB4C714A009BFC63BB8F4906D6678130DB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/createdump.exe", + "source": "vendor/pwsh/createdump.exe", + "bytes": 71512, + "sha256": "6A1B3A1AF1AFC2B756E2E45C36A3AC0D05D3134B14D8C66BCCFDBCC1F308B4E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/D3DCompiler_47_cor3.dll", + "source": "vendor/pwsh/D3DCompiler_47_cor3.dll", + "bytes": 4751568, + "sha256": "2D3B1B3057234778DCEBA837FF082355E6F6625AA5B68C4E69DCDF21106CEB4D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/DirectWriteForwarder.dll", + "source": "vendor/pwsh/DirectWriteForwarder.dll", + "bytes": 505640, + "sha256": "0384825B584E93D197040E7199E1C3D990190BBEDDBF65F327282D1CDA3FAE7F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/en-US/default.help.txt", + "source": "vendor/pwsh/en-US/default.help.txt", + "bytes": 3420, + "sha256": "5AD33AD117ECD78AD5E84AD745E69D45E6A6A10E68A0C7333C2CE95E102FD602" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/getfilesiginforedist.dll", + "source": "vendor/pwsh/getfilesiginforedist.dll", + "bytes": 67104, + "sha256": "153EEFB2EAFA8B2B909854CC1F941350EFB1170E179A299DE8836B8EC5CE6A7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/getfilesiginforedistwrapper.dll", + "source": "vendor/pwsh/getfilesiginforedistwrapper.dll", + "bytes": 30528, + "sha256": "7643100BB96726F8603975EAC7074174BF06F05AC3351CF8FCE0B8AAEF90ADD0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/hostfxr.dll", + "source": "vendor/pwsh/hostfxr.dll", + "bytes": 379688, + "sha256": "6D2D8B595846BFCBB5EF7B1CB1A3156AFF4930D2002B6AC813910B848C918855" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/hostpolicy.dll", + "source": "vendor/pwsh/hostpolicy.dll", + "bytes": 378664, + "sha256": "400E8547169E8D4405ADFD79510AE429F76F7A580932AC8A602EF5D88E4FEB2E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Humanizer.dll", + "source": "vendor/pwsh/Humanizer.dll", + "bytes": 845656, + "sha256": "E6AD30F638466744B2F64006AB48E8712B35D95269C08B64E9C9E6044150B612" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Install-PowerShellRemoting.ps1", + "source": "vendor/pwsh/Install-PowerShellRemoting.ps1", + "bytes": 23219, + "sha256": "A7BB6A752B186E225AB5080F0E5D26CD8598D827322D5927444508AEE937E285" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/InstallPSCorePolicyDefinitions.ps1", + "source": "vendor/pwsh/InstallPSCorePolicyDefinitions.ps1", + "bytes": 17206, + "sha256": "0B392011A5B57C05B7A8974406B20F8654CE1B32E11972DB6BABEB2765AE8EF8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Json.More.dll", + "source": "vendor/pwsh/Json.More.dll", + "bytes": 145224, + "sha256": "A34CF6FDCA5028DAC2D51F97951A13AEC724F172718568708CB9EBE2767813E6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/JsonPointer.Net.dll", + "source": "vendor/pwsh/JsonPointer.Net.dll", + "bytes": 75592, + "sha256": "D570D76B007F563490074D4BA738904F2886F9BD8F44D4C3F365D6AE51A90311" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/JsonSchema.Net.dll", + "source": "vendor/pwsh/JsonSchema.Net.dll", + "bytes": 812848, + "sha256": "262C0DFCFE5E1B2E99BA4D915E00B18C1094195643B19F04559DFDA1A8218A23" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/LICENSE.txt", + "source": "vendor/pwsh/LICENSE.txt", + "bytes": 1095, + "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Markdig.Signed.dll", + "source": "vendor/pwsh/Markdig.Signed.dll", + "bytes": 1361728, + "sha256": "17E508A3B13A1F52C463D0126004095032E4F0614CC275628F55BA267D281F56" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.ApplicationInsights.dll", + "source": "vendor/pwsh/Microsoft.ApplicationInsights.dll", + "bytes": 923480, + "sha256": "EA8B0774AFFEEC16ADFD24FC55CECCE39F778CA55E89A2FF6B2B457AFCCF44BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", + "source": "vendor/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", + "bytes": 30536, + "sha256": "53029EFFB9F6426939C5B702B4B2B322E4A5ACD35CF4A1ACE8E90F38B5BDEC15" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.CodeAnalysis.CSharp.dll", + "source": "vendor/pwsh/Microsoft.CodeAnalysis.CSharp.dll", + "bytes": 18536312, + "sha256": "78533CFBF2E43A374E58F05EF2C495ED56BF93EAA63BE0EC3F2B35FC621D008E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.CodeAnalysis.dll", + "source": "vendor/pwsh/Microsoft.CodeAnalysis.dll", + "bytes": 8128336, + "sha256": "14856FD1CDB7F425323C82D276A9ADD64454EDF0325A28B588C9825838938200" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.CSharp.dll", + "source": "vendor/pwsh/Microsoft.CSharp.dll", + "bytes": 980776, + "sha256": "C412EB8721B14AFC5BAE7C8D26B4239D1DB11486F0192FA39C3A8B4F6F23A1D2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Extensions.ObjectPool.dll", + "source": "vendor/pwsh/Microsoft.Extensions.ObjectPool.dll", + "bytes": 42824, + "sha256": "68AB071B3E6031888BD4944C2CED33B4973CB4C04877D9202781F6BD41F23EAE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", + "source": "vendor/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", + "bytes": 493376, + "sha256": "3C3FEDB396CFB166288A4ED6726F3F7E9CC4057A1052830573BE4B3313146965" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/microsoft.management.infrastructure.dll", + "source": "vendor/pwsh/microsoft.management.infrastructure.dll", + "bytes": 309040, + "sha256": "A5138F7B4EA083FA297E744D26AF2254EE3B593186CAA750A6E4963C00F3D778" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/microsoft.management.infrastructure.native.dll", + "source": "vendor/pwsh/microsoft.management.infrastructure.native.dll", + "bytes": 362288, + "sha256": "FFA5E12A8A10634F88796D20711CA0585C42BB22652157A2D776965AEBD34EAE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", + "source": "vendor/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", + "bytes": 28192, + "sha256": "9BEE4E35576355156F00E2E47EF57AA2C8CA64390C112F083A772F2219026293" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", + "bytes": 333624, + "sha256": "50F7A79D52316975A92AAC3996248C4B842EE6CE5EB41A6D0B25667B4791E336" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Management.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Management.dll", + "bytes": 1124152, + "sha256": "556085D8CDCF6D442F0F59776DE3D74535BBFA9FACF33D1358D90BB948F6BC92" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Utility.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Utility.dll", + "bytes": 1652536, + "sha256": "D156B47A70718A54D1B5691D0FF58663D11270BC94911D870AB2895648C1057D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.ConsoleHost.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.ConsoleHost.dll", + "bytes": 603960, + "sha256": "456BE117016C7B7E474C66E48439C1917D4E9717C2EE247B6DD4E19B4B7A674A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", + "bytes": 231224, + "sha256": "9EF54EB74EDA1C44BB1E7B71218DE2887A0490A526A3BAC967DC319ED3A74D3A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll", + "bytes": 993048, + "sha256": "D0CAC79C0CAFC557A981B1B7F3AB79544E764056B5CAF9EAC16A0095009E421E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", + "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", + "bytes": 4904, + "sha256": "04EF1B37822130AE6AC35D2CA9CB5224C87FBEB3AD2B0C48F953C87B083CC26C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.MarkdownRender.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.MarkdownRender.dll", + "bytes": 55104, + "sha256": "08A7A5C37E857A0197C710B8D9B52A4269AFE12F00374C077389E3C7DBD584E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.SDK.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.SDK.dll", + "bytes": 142624, + "sha256": "8637EC9E121C43B3280ECAB0AD12DDC76424AE08EE6A84A47F10548BDF076232" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Security.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Security.dll", + "bytes": 345904, + "sha256": "2944F4A3EA39EE33FA783A751037CCC7DA4B4A6AEC57DB7F652FEB544BE5D082" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.VisualBasic.Core.dll", + "source": "vendor/pwsh/Microsoft.VisualBasic.Core.dll", + "bytes": 1193808, + "sha256": "788558DDB1196D021FE17CCEF9DA03CC1C8C95174CC7A6CD56D0049AE65C2227" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.VisualBasic.dll", + "source": "vendor/pwsh/Microsoft.VisualBasic.dll", + "bytes": 18728, + "sha256": "4B8D9F34CC136D4F5FE156D3C0CCFA526231D042BE68DD0F412388EF7E0191CE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.VisualBasic.Forms.dll", + "source": "vendor/pwsh/Microsoft.VisualBasic.Forms.dll", + "bytes": 247592, + "sha256": "44A8EBEBEDC5746BBC22411BF34A5C869E3428908DB2D12B89270D1C762264FE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.Primitives.dll", + "source": "vendor/pwsh/Microsoft.Win32.Primitives.dll", + "bytes": 15656, + "sha256": "0327272BF4555EBD266751DFB3AC42463C8C0F266CDFD352263D4C7AA8FBAF04" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.Registry.AccessControl.dll", + "source": "vendor/pwsh/Microsoft.Win32.Registry.AccessControl.dll", + "bytes": 34600, + "sha256": "EA85FD3A01751393A2C4EB8D816EE50A0443C96C91CA2AEAD1F2AA15C52939B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.Registry.dll", + "source": "vendor/pwsh/Microsoft.Win32.Registry.dll", + "bytes": 116520, + "sha256": "FAD9AF3A76A4B89BEE820381FDE9CE06DFAADC3D90859953503F4BE81C74EE31" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.SystemEvents.dll", + "source": "vendor/pwsh/Microsoft.Win32.SystemEvents.dll", + "bytes": 116520, + "sha256": "75DA025453C94EE0D959A8077ADC2A69B05E28B987BC5EDF93887DF6C1CB393E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.WSMan.Management.dll", + "source": "vendor/pwsh/Microsoft.WSMan.Management.dll", + "bytes": 542512, + "sha256": "B95BB3EBAA7F562BA3EEA65B78429E267819D65A71F5839ADFD498EA695BB09F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.WSMan.Runtime.dll", + "source": "vendor/pwsh/Microsoft.WSMan.Runtime.dll", + "bytes": 157472, + "sha256": "D51427C022C1CEB37DE10991BD712F491B15D9BF8E0D42A3270ABAC050DFA0E3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", + "source": "vendor/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", + "bytes": 15219, + "sha256": "5417424A8746E0024526C21D09DD309F8B04B25BE711F99DF4F1507E8F335D70" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", + "bytes": 12873, + "sha256": "1C4EE42E1FA99A2768FA8FEDDAADF70A84F8EA8F273234061A6781D41DA0FAD4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", + "bytes": 17109, + "sha256": "15C2C0874890E5C715AA513C744CA99803A2629466CBB3C62E41AE3CFC857D66" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", + "bytes": 14859, + "sha256": "95E193E52B66E19C8ACD007AB2958B5020B3FDFF52227162B09C5110F01E2685" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", + "bytes": 77022, + "sha256": "48F095C3A48F95AAB0714C06A6EFDB798C77F957948AAF02CAA16CF50692D2F7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", + "bytes": 18243, + "sha256": "01EE5A86A4E774DBBACD911B7CF9070C72C6EF47699C1B94D70C8DF5FA9F0FA9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", + "bytes": 20849, + "sha256": "BD36F3F1242E75599579D6E3F805AF77663767C4DBF3750413FC6794B2EA5D12" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", + "bytes": 20298, + "sha256": "DD35B80DA89FF364BBA431A26223DE85252B3320C06D6489C5ECAE33A4EA8320" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", + "bytes": 14887, + "sha256": "F511D03F0A312CB4C74D1FFAC6E777190A37FDF057388F0DA3AC0DE8CE9CCEFF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", + "bytes": 14721, + "sha256": "BB209A688D755B76351364FE9B4ABB224F5BF881D9281383C7D0A62ACCFFCE63" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", + "bytes": 16092, + "sha256": "4BFCB08F3F388A74BE6DA9B765585974BE9AA37EE4EEBFFE52FC1BF762C25DB8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", + "bytes": 12873, + "sha256": "882CEF83D74AD2226EC51479280913B0B6C919DA35BC7365438D6EC540FB1018" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", + "bytes": 429624, + "sha256": "0C85C8CC3856D5BEB91895D1A8E00F785453F2AA538D85EE9ACE030B537F0FC2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", + "bytes": 357944, + "sha256": "B960AB88C55A7EDF0FF7A659CBC88FDA2DC8219221D46B7FCB92245B8E8EEFB4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", + "bytes": 26696, + "sha256": "FD587EF95461251B8D4B3D493D77102120622B1FD32B87BB98E032E1365480A1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", + "bytes": 22048, + "sha256": "FA1FECF3C081A0EF366100169CD539AD03EE10CD18BCF89DE56E9CB8D3659D4F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", + "bytes": 45088, + "sha256": "152BEBA2D6F82210F17696B2BE9498ADC9C38D32B9E2FD03BA87810B838F811D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", + "bytes": 44064, + "sha256": "7C6BC24A7D6A1C3C1FB4FC521174CF43E931A19F8BB88754FB738A6402848E20" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", + "bytes": 1664544, + "sha256": "924340C47CBC74B1B811059860DEFA3B136500BB97EF8B6DB95631F2391741D0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", + "bytes": 65568, + "sha256": "B084586684B03456481A6AA0AC0B149F87EBBC6FB01735D8005F987031BD0343" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", + "bytes": 18976, + "sha256": "7A77F7894D7F925C2E806737B8F4F036CD0D8B7E7843F824BA1139C10A0B3E25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", + "bytes": 732328, + "sha256": "1DC3D1DB10368906573D443667CED170FD7A01F3349D93906A8B60F1D914B6BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", + "bytes": 613824, + "sha256": "762988B531247A8AC282B85E0BC81004FA9D02B9118A24C3B18B3A97F1DADE48" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", + "bytes": 122800, + "sha256": "E8932AC0C5D7E306551BC4B585F582BAEF7651F21C88D4976445C0BFDF29F227" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", + "bytes": 170424, + "sha256": "0BB37C45575CBCCCC279A4250FB7F0D87DE5C0A3DDC16565542A6ED0E5682C6B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", + "bytes": 67616, + "sha256": "E5F44DDC086A8F40989C70460E22E25095D75AC402FAF6FF436E6A08C9CEC71F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", + "bytes": 93728, + "sha256": "077951FEA107ED42180AA5EAAA0BE047070A0EFF32A62999A8280535CB5D4F3D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", + "bytes": 131104, + "sha256": "C79E07959989FE7E99F31171EF712F9665474E652B4C16BC729160114697F8D4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", + "bytes": 56760, + "sha256": "EAF71D1D5DD771DAC1510C8779C2FDC98D539C8C07934EE114679D453FA60C21" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", + "bytes": 689088, + "sha256": "877E82582D7802ACA0B2FD53D34175B42D01BCF713A77D6D0A9E8A73C47EE2A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", + "bytes": 219056, + "sha256": "275A531DE109D62F71D745DB656D295511DD53CF0FD141CC0153AD357BFC8163" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", + "bytes": 786368, + "sha256": "AB3FCF87ABA83AC429FB040D5C36A4B7E9AF334C923F8F27041C93046D2AF9CB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", + "bytes": 65984, + "sha256": "77B3FAD9D3D369538F765035DD142D3D91785CE1308C40C5CA13BF84C9E78ECD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", + "bytes": 41240, + "sha256": "11568C79856612F9B5AF07FA8EF0A38E524F912D98C188E2510FD7E5ED520EC3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", + "bytes": 189104, + "sha256": "1887592615FC08BBC91AD62307C8C11DFC14C45BF120D271D66BE4A0D1A149A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", + "bytes": 28552, + "sha256": "E6026501AD4056FF2F1655B0AFDFE8923BC6E8FBAD67E1E9EF56E3002F49FBB9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", + "bytes": 31016, + "sha256": "6CF06482F4FD82AF659C2EB4B7BDEAE51EC171AB1CA3B3CBF0328C6A4B882242" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", + "bytes": 162624, + "sha256": "2728C8F0D153C72D3F50363FA1762536675C303F4CF4E7502EE3F099B3474554" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", + "bytes": 126152, + "sha256": "E5B1E3DEFA97A3AA29CC20BE0C9D525C86BB9C716F3B85F9A82CE1F30FF6310E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", + "bytes": 38424, + "sha256": "2154F3A7C647BD58C9DA47650E10CC42F906B7F7F1080211023917CA9684839F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", + "bytes": 33672, + "sha256": "1FDA491EEBDB19EA0A83CF6C16AB5DD004A1BFDFC845EDE017EBE0945BEB927F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", + "bytes": 23696, + "sha256": "A79AD1E9E7205BD790F40D6C1900BAF1A9C8CAA06089878D24ED3599AC3228A9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", + "bytes": 18312, + "sha256": "B4D8E15ADC235D0E858E39B5133E5D00A4BAA8C94F4F39E3B5E791B0F9C0C806" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", + "bytes": 99408, + "sha256": "A58026527BED9F3098CA3D016A7B6C93BD6F0134092F484B099970EC3D839AB9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", + "bytes": 644904, + "sha256": "0CF5A9763C98F09E94DFCDAA4437E9508EABE2E50B408BA421D5FAF734C5556A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", + "bytes": 25984, + "sha256": "4F81FFD0DC7204DB75AFC35EA4291769B07C440592F28894260EEA76626A23C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", + "bytes": 25232, + "sha256": "E905D102585B22C6DF04F219AF5CBDBFA7BC165979E9788B62DF6DCC165E10F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", + "bytes": 17567, + "sha256": "7429201A7A4BE49FEB0456DEDBD47E9E753DDA4EE871A8E6B977A0917F96745D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", + "bytes": 1095, + "sha256": "BE5723395260D4F07283D596F4B375810EB695653D48D52A5062C0F7070573A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", + "bytes": 433184, + "sha256": "C9ECCB0E5313CED30E7DA3903C27F1A97C6E8B0793A0A91160C4BEE346F3EE02" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", + "bytes": 138856, + "sha256": "A8890484E98D618F366A5AADCF34F9AB8D9D2C51AC08E7E4789E7C733D62B653" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", + "bytes": 20644, + "sha256": "13A0C3EA11F35E08DD1EE06A15EE9EE793062752EF5DA63B43E7C4BC64976328" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", + "bytes": 17978, + "sha256": "303A5B8DD3749DA6A7530E2F040DDC1ECFCCA280120DCEDA473EBB2285661CE8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", + "bytes": 158819, + "sha256": "CB3714C1F7FC609FEE9264C5F7E87DF89121D09242A4DCCEFE4815E9D784AA65" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", + "bytes": 23783, + "sha256": "097FE2BBE4C6BC7829C64114E46E51479574B5B9E4B2F84D148F29B2FAE0BE9A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", + "bytes": 1364, + "sha256": "1C8163F8163797C4B91466A94207E356256B8F770A0C591C68CD1FADD015A79A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", + "bytes": 1839, + "sha256": "37BCB0428C05343483E7B5AA2F76AD3F804BA87830526EC42E8F8A91736D4AA9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", + "bytes": 15415, + "sha256": "9CBBDDBC38465378D4FDF3529B74C39161B728EF295974B5B9D90FD5A87806C3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", + "bytes": 20141, + "sha256": "B226CC25C17CDD19CE028EA22B3B596EF9A3A8C8E3C58BF3A9E76E45D9F38514" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", + "bytes": 12812, + "sha256": "3DB2F058A8F27F3E7BE0E22E8258944F9A6412A379DFFC66F99F3E592899DEC4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", + "bytes": 19596, + "sha256": "6E5AA422BFDE0133D9FC12CAB32C404557CA7CDDDA1FF888BF69395595928003" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", + "bytes": 1095, + "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", + "bytes": 34840, + "sha256": "F89AB468EC1329474288FB0FAAD81600005BE3DA88DC2B8A2631CFBDD64F394A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", + "bytes": 18088, + "sha256": "4E9CB7A6D8A94560F0025B807162BAAA9C4A6AF4254D3FA7E71B23E0DCC1FB25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", + "bytes": 1979, + "sha256": "296FF3E4A93BC8C20CA5C20D0E1A70B8C4622FADF0351A37845A63ED52585279" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", + "bytes": 16830, + "sha256": "BB9BD6F3EAB1CF227E1AFAFEC89934E6EB10C6C8530908C6436849120F631D9C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", + "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", + "bytes": 15033, + "sha256": "8E06AA0AB008337C59AEBB32FF672BCD1646FA927DDF4E4EFE82AC454AAB30B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", + "bytes": 24000, + "sha256": "6A47415133889D1D676C6170DD14DC43C2C983DC6ADAFE73DC0F66891E5150FE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", + "bytes": 89504, + "sha256": "5A140EB9BFB2EBB42F02E75DA9A408A86BA6034EAA27AC57A10A534011EFD509" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", + "bytes": 72080, + "sha256": "50CB7269866EC2E3663221662FD1649A0C1B2736525D65231E29B39B25BB2467" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", + "bytes": 318352, + "sha256": "23689DD357312F0924305C612AA41BE0BD592AD9712FED70A3DA72A9C40EB712" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", + "bytes": 83864, + "sha256": "8F5BFD7FFFFE4930AD035194447B8FB24257C8C799302F04879CD5D5211A38C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", + "bytes": 290192, + "sha256": "C0579B5BCA0ADFE99DC17C253F4F00D669AD008A60854D2508BF6D79A886B4B2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", + "bytes": 189328, + "sha256": "10D3F4E3AA47CFC3AAC0E94C3B20000EA73CB0062E5037A79FCBE932112EB0A1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", + "bytes": 28469, + "sha256": "B7CD11228923E8747B04AE8A03F246F1490B4D51C8A54B41D26CCB20164FE558" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", + "bytes": 31676, + "sha256": "8D9E6819AF5265B47DC514F82EEEF657449C2F3DC72CE4F4F8531FC2FCCDECF0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", + "bytes": 31412, + "sha256": "50EE9087694A8325E0BC6FF4E5AE008984D46FF84CB9BCC6ED655CFF1A8547A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", + "bytes": 53524, + "sha256": "56BD33242E6D59F02514EB3B902F65B83A3EA8EA7E44099F57892AB640823C25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", + "bytes": 31566, + "sha256": "ACD658401A6A84F29E091F6549F4AD6BC6E16B71005F542E4BE558C40B50EAFA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", + "bytes": 32494, + "sha256": "AB2DD00EE1AB028EF4C506CAC98E5407FB866DA40720C232A3426ADBB57EA67A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", + "bytes": 24136, + "sha256": "CA8EA0F3E2208FDEDD51C2F282EA9E9A50BE7D8A38C56EEF0141191F7E484776" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", + "bytes": 31104, + "sha256": "24E1182E6F6E3EDDF2528EC6D335E0D584CE37995B0D849B5DD5F5EE75A558BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", + "bytes": 32443, + "sha256": "6CC6DA35A87357E213FA7A2CD0FAF9B1DB1BB83BDAAF9F73C952FE9AD2BB4596" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psd1", + "bytes": 17889, + "sha256": "EC633E756164C26D1154FAB722648E2737F3650399856D29C089F0C25B190D33" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psm1", + "bytes": 16422, + "sha256": "D484E31593FE26C8E670F8A0D72D4400A3E8EB75F587DF4B11554B8C000E128A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", + "bytes": 14984, + "sha256": "734BFFF81999A08B4145399A6284B00E46F070A7038BB4C82AB79165FF15F543" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", + "bytes": 25115, + "sha256": "481FDED09BF8A376EC4BD53CF34C0D66C8D8B1D31AB2012A768227F92B099156" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", + "bytes": 15939, + "sha256": "4081ABD182F805AB3B9705279478A2A8AF17A0E43F07887E3C68962BCD09C9E8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", + "bytes": 38882, + "sha256": "6C724F3B86D5D5233C2906FE41206D6EB2279B6D053164F51AAF83B4891619EB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", + "bytes": 1766, + "sha256": "CF126CC6FD7AFAEC54F07942F346F379725795625B80EC5FC32F547AB7B49BFC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", + "bytes": 14446, + "sha256": "2107051FAB99F1E8545CC30F72FB089899B19A5BD4D33A6891FBF0DF794F573A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", + "bytes": 14171, + "sha256": "7520000BE81F281384AF99B787A4629E55269EF73D8FA162CF29390DC273A017" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", + "bytes": 23645, + "sha256": "627EDB8150EC8C52B33E363652D27E4FD71C1374D03C5B56D98AF244D3C1CC10" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", + "bytes": 1578, + "sha256": "B9F9A62A3D2D0497BF81CC69A456B9E0BDBF9D1F34909A0283F088940EFFFC34" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", + "bytes": 14325, + "sha256": "6AA312F0A73494723FE4CAA98BD74CCF3724226BEE4BFE4EC0A7229BFAD7141C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", + "bytes": 59328, + "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", + "bytes": 20118, + "sha256": "14A35134461ABCFAA7DFE393C7B3A16605032719F5EA0EE062B4655A181C0C76" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", + "bytes": 15108, + "sha256": "1302F09514E514B4A2C5A2F8D4DFCFC5087070E211357319FB516E27994C88B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", + "bytes": 27387, + "sha256": "640240A2632C22E75AE372414BEE7136C2ADC932F92A0F656C436BE616B19CDA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", + "bytes": 23683, + "sha256": "B1315712ADE77AF6C5115D8C9AE2F9A5D8CCBFA5E9A5F00C19AB283A52057F2B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", + "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", + "bytes": 23680, + "sha256": "4F12DD41E0FAB7B1A97AE83A84BDDB567CE9876F9E6C3496D833CFBB8E52BD7F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", + "bytes": 59328, + "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PSModule.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/PSModule.psm1", + "bytes": 1368306, + "sha256": "573D4A3861CB967E52EAF6155A41F930FCD4168368A6946F65D985F30DC357EC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", + "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", + "bytes": 14978, + "sha256": "1DCFF05CD03BEC2F6117BB7267DADDB9770CB10FA6FE4AB9D892E87DA15349F7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", + "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", + "bytes": 26145, + "sha256": "2697A9EC7792940DA5F3D91CCAC24B1A3948CFF4E5901D5B98B7689A1C4A9DBF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/.signature.p7s", + "source": "vendor/pwsh/Modules/PSReadLine/.signature.p7s", + "bytes": 12873, + "sha256": "ECDF2C3BC44D32AF003FD81BEF96E83F19D7EFC9658C07F7D78687F105F721A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/Changes.txt", + "source": "vendor/pwsh/Modules/PSReadLine/Changes.txt", + "bytes": 41347, + "sha256": "E18C4D35A40A544877378B36B7D86B5C79DAED0C6E7A55C3027C4F0F998426C0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/License.txt", + "source": "vendor/pwsh/Modules/PSReadLine/License.txt", + "bytes": 1324, + "sha256": "25C2FDFCDC653F65629233F5EF217A83287733D3C0299C207B0BCA865508C463" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", + "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", + "bytes": 16784, + "sha256": "451994C3D3E38D939B4FA5F8594D0207840F45E7A210AA75F81AEF85AA17C592" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", + "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", + "bytes": 339528, + "sha256": "F8E3A5B7E3E8CAD2130CE10647564A2A0EA15D98DB8A0CC8D589F80154C108E2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "source": "vendor/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "bytes": 14920, + "sha256": "6D33EA289B405C64BF5DAA9C59E459596CAC59ECE399D0D215EE9F3D46AFD675" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "source": "vendor/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "bytes": 16928, + "sha256": "2E63DC86D9240243CCFB07DA5D0B4FCE2BC5CE5F9952658A7CE68C26BCD3E14C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", + "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", + "bytes": 26820, + "sha256": "1CA887463598D38AA18236A97A4193B2228D1D8434FA73CC8C7932F42A0565CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psd1", + "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psd1", + "bytes": 15471, + "sha256": "DD8766BD4DB0C1D17111B81D145D32448F61F48451D51C780A79BE9CA98D837E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psm1", + "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psm1", + "bytes": 15076, + "sha256": "9BC0252F616067CA5EF1D0328EC0B97C7D31C757F9FC80ADEE234EA6132A9C7E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", + "source": "vendor/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", + "bytes": 41594, + "sha256": "53B518B3D72AFEB6C8A3F4ABF079C93CEBAF7100A12C93668B83A4D4BCD45795" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscordaccore.dll", + "source": "vendor/pwsh/mscordaccore.dll", + "bytes": 1366744, + "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", + "source": "vendor/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", + "bytes": 1366744, + "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscordbi.dll", + "source": "vendor/pwsh/mscordbi.dll", + "bytes": 1247992, + "sha256": "1875AB683E30F79B39FF861F440A246561B72247C22366615B97CC14537B4808" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscorlib.dll", + "source": "vendor/pwsh/mscorlib.dll", + "bytes": 59688, + "sha256": "E7BE6C67FA82F5F0C10C6553D82CCC2854DF3B49CF8DA706629F80A34A1AE309" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscorrc.dll", + "source": "vendor/pwsh/mscorrc.dll", + "bytes": 133928, + "sha256": "11917AEAEC22F310A26A466E7D57CDCAB40F6ED1233B286E39AC675E881A7DAE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/msquic.dll", + "source": "vendor/pwsh/msquic.dll", + "bytes": 536344, + "sha256": "B7158D427B01C70483BB3D80C5211E387DD98E9471C8A70E4484D449DE9157E8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/netstandard.dll", + "source": "vendor/pwsh/netstandard.dll", + "bytes": 100648, + "sha256": "42388B4A19485ADC2A7D384F5257BB9D806F3BAC6F8871DDE42AFD944BBFC0C5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Newtonsoft.Json.dll", + "source": "vendor/pwsh/Newtonsoft.Json.dll", + "bytes": 1824584, + "sha256": "2B6FDDE54CC2D2E9277D458A42FDF9FCA30FC4299652E97144A31C90FEC91829" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PenImc_cor3.dll", + "source": "vendor/pwsh/PenImc_cor3.dll", + "bytes": 157992, + "sha256": "962CC2EB9DBBC45680649AC084685FB0E57DA4DE09D3DD5762188D10B76F598C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/powershell.config.json", + "source": "vendor/pwsh/powershell.config.json", + "bytes": 190, + "sha256": "98C0E5B6EE17EB8B8F4E4940C2B2528689CEC8470DB4BDE427AD16D90D6A52D4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShell.Core.Instrumentation.dll", + "source": "vendor/pwsh/PowerShell.Core.Instrumentation.dll", + "bytes": 106808, + "sha256": "AEEB8062AAFF1057E90E9485F610B508B1179AAC4DF7FC465090BDEE49E3CFF3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShell.Core.Instrumentation.man", + "source": "vendor/pwsh/PowerShell.Core.Instrumentation.man", + "bytes": 227126, + "sha256": "8B2585E50EDE697905DFDB8E3878DA2AB42039135BA23E377207CC8D96E8125F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.adml", + "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.adml", + "bytes": 9675, + "sha256": "2B8C4B7D5BD585431FBF918F223A9B932D80EB9703A1976A1CE3492D4DD26966" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.admx", + "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.admx", + "bytes": 6198, + "sha256": "EE8EB99C1E66009D95AF46E4F6C8F5C13563C9A0AD827E631149176A1B8D34D2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationCore.dll", + "source": "vendor/pwsh/PresentationCore.dll", + "bytes": 8324904, + "sha256": "7A610FD470016E5C74E2BFC403D2E08DDFF10F1EEFCBBF3AA7B07577855EE746" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemCore.dll", + "source": "vendor/pwsh/PresentationFramework-SystemCore.dll", + "bytes": 38696, + "sha256": "12DDD4029181D0AA27D32A69777760037A85F06BC57D56478669E098417E9990" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemData.dll", + "source": "vendor/pwsh/PresentationFramework-SystemData.dll", + "bytes": 34600, + "sha256": "0075922DB11FDE4BE1E8668BB9ABF5596DA8A40367E1D7264FE44B30B83F2228" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemDrawing.dll", + "source": "vendor/pwsh/PresentationFramework-SystemDrawing.dll", + "bytes": 34640, + "sha256": "953D322E29718C113DB240EB73396E65BA5B9AA55AD25C8A796B7BEB2E11573E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemXml.dll", + "source": "vendor/pwsh/PresentationFramework-SystemXml.dll", + "bytes": 34600, + "sha256": "EBDDF88BDA059E766772C7F3BB073C4660A48FE7CAB1238B971FE6DDBA4C01CA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemXmlLinq.dll", + "source": "vendor/pwsh/PresentationFramework-SystemXmlLinq.dll", + "bytes": 30504, + "sha256": "8D5290BF6787A161D2689C03C1936BC9D28CB22292197B2EED3A57ADD05DC81A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Aero.dll", + "source": "vendor/pwsh/PresentationFramework.Aero.dll", + "bytes": 440104, + "sha256": "D64069A4E059D5F74EFAE734541A5BC54B13E00A087B7D7A0CE224B4F9F7B3C3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Aero2.dll", + "source": "vendor/pwsh/PresentationFramework.Aero2.dll", + "bytes": 444200, + "sha256": "092E068D4F0117CF5020A072A53A3010EEB54F125DA3BC8D4EA1913A3370E4E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.AeroLite.dll", + "source": "vendor/pwsh/PresentationFramework.AeroLite.dll", + "bytes": 239440, + "sha256": "B7B8BF2214F9653CA2257D1A5E1DD0D978FAF15BFDE826F0ED3EE57B6A1A9A90" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Classic.dll", + "source": "vendor/pwsh/PresentationFramework.Classic.dll", + "bytes": 272168, + "sha256": "CB733EA566618B52415EB32FC32C86A3FE6BC915D471D63DED3A46AAF3071AEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.dll", + "source": "vendor/pwsh/PresentationFramework.dll", + "bytes": 15816528, + "sha256": "6A3C5B98ED693C577FF25827315D7EEE89F73DEDF0088B80B43B4AFB337018C4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Fluent.dll", + "source": "vendor/pwsh/PresentationFramework.Fluent.dll", + "bytes": 980816, + "sha256": "E33301B7F7A38B9A5065127F79EC3860A6827B30BD1C85A669E3C3AE191C4258" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Luna.dll", + "source": "vendor/pwsh/PresentationFramework.Luna.dll", + "bytes": 669480, + "sha256": "F9030C4D064B7F097B6F73FAA9C7683BEFB7A1A4C18C2D6DD741EB34A2325658" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Royale.dll", + "source": "vendor/pwsh/PresentationFramework.Royale.dll", + "bytes": 333648, + "sha256": "118F34B2213AFE8075BE78D66FDB5B5B0C099A89F70AEE9CA09A9DC09BF07102" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationNative_cor3.dll", + "source": "vendor/pwsh/PresentationNative_cor3.dll", + "bytes": 1235240, + "sha256": "42E2E543C17E1B37499BAF00D956433B8AFB655348EC0FAE89C926FB7D149B6E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationUI.dll", + "source": "vendor/pwsh/PresentationUI.dll", + "bytes": 1259304, + "sha256": "5DE46036ACAB98C8B970BFB4234F66438EE102039F9A59A72053A9818C3ED1F0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/preview/pwsh-preview.cmd", + "source": "vendor/pwsh/preview/pwsh-preview.cmd", + "bytes": 25, + "sha256": "7A24CB96B62009CFFB38C85B6ACBF48A5DB5746459B85FF3E727BD952FFD8832" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwrshplugin.dll", + "source": "vendor/pwsh/pwrshplugin.dll", + "bytes": 159032, + "sha256": "FBF2C8FC0D8A3CD5F2DEE9E0F15315F92862B6DF26BF1B02209588A07CC0D429" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.deps.json", + "source": "vendor/pwsh/pwsh.deps.json", + "bytes": 99499, + "sha256": "DFC3A3FB00F53720610F389E2525FB268928B381E55F899B84B478502D5736A2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.dll", + "source": "vendor/pwsh/pwsh.dll", + "bytes": 157472, + "sha256": "91F9892F15A7E2DCF440AFC3A497A7D839F9D38D1AB4A50BD36080D982A01449" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.exe", + "source": "vendor/pwsh/pwsh.exe", + "bytes": 301368, + "sha256": "BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.profile.dsc.resource.json", + "source": "vendor/pwsh/pwsh.profile.dsc.resource.json", + "bytes": 5024, + "sha256": "378030311FCDD7A8D6C401142275BAEB6C031307EC2E12F51399B84851906380" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.profile.resource.ps1", + "source": "vendor/pwsh/pwsh.profile.resource.ps1", + "bytes": 20443, + "sha256": "BA90CDBF60C48C05343A7C4A451BEC2A5A133DEB10F1F7CD763E76F5D2BA7944" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.runtimeconfig.json", + "source": "vendor/pwsh/pwsh.runtimeconfig.json", + "bytes": 790, + "sha256": "6C1779B71C67B5F40B2D0041BACAE0873320DD55F0DC3D5DF76D88C8D486275D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ReachFramework.dll", + "source": "vendor/pwsh/ReachFramework.dll", + "bytes": 1570600, + "sha256": "E0EE50A412ABA6DC2FAAD0655D5AEBCFF33587A113984CAC2777ED7B763BD193" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.CSharp.dll", + "source": "vendor/pwsh/ref/Microsoft.CSharp.dll", + "bytes": 18216, + "sha256": "AE0ECD8F42EA3EFACC2612ADE56F29FEEA9A123FCE9FAA455126EC9F4A4540CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.VisualBasic.Core.dll", + "source": "vendor/pwsh/ref/Microsoft.VisualBasic.Core.dll", + "bytes": 59216, + "sha256": "7A7E44D7AC1F870E0B7D8F3F7598CCA79E35CAB12E77C58ED46E9B6B3C78BCFB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.VisualBasic.dll", + "source": "vendor/pwsh/ref/Microsoft.VisualBasic.dll", + "bytes": 17192, + "sha256": "E192508ADDE6A758138A5A8E28998DCCB64E7F45EAF30D8AB3564F87749028B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.Win32.Primitives.dll", + "source": "vendor/pwsh/ref/Microsoft.Win32.Primitives.dll", + "bytes": 16168, + "sha256": "AB197DEC717C52F5A72C8F2366C33E2FC5149BFA7CF49ACD59BBFB563DEFCD05" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.Win32.Registry.dll", + "source": "vendor/pwsh/ref/Microsoft.Win32.Registry.dll", + "bytes": 21288, + "sha256": "E8EB7F780B9CECA6DAE35E74A11FA85E26A1B8B4A7EDD5237CCCC6B63B4E4B7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/mscorlib.dll", + "source": "vendor/pwsh/ref/mscorlib.dll", + "bytes": 55592, + "sha256": "894D95D82126559637417B4F14C48ED20BB1168B13A0E65BD1337CDC2A981D1B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/netstandard.dll", + "source": "vendor/pwsh/ref/netstandard.dll", + "bytes": 100648, + "sha256": "3DC81DBC9DACD28261632C94410859EDA24C2DB50CD6C14309CBB2A434B50C57" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.AppContext.dll", + "source": "vendor/pwsh/ref/System.AppContext.dll", + "bytes": 15144, + "sha256": "D9DD9FA44E019F27172D6B2662E2B6038F48C270912AC8AAE53D4D909DA7E9BE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Buffers.dll", + "source": "vendor/pwsh/ref/System.Buffers.dll", + "bytes": 15144, + "sha256": "A1A35F5524E2F831D08CA55F702FBDDCB8C6DFECD264F0F563D81A1C8B8280BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.Concurrent.dll", + "source": "vendor/pwsh/ref/System.Collections.Concurrent.dll", + "bytes": 27432, + "sha256": "27B446A401163BAA325F9CD82F787DFD32D0E205011093E0AB3057E5DA609555" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.dll", + "source": "vendor/pwsh/ref/System.Collections.dll", + "bytes": 55080, + "sha256": "130FCDD915C3FF8D8279FEFD9A198B784D37693F4C1E96D1F2CFF3EDC96B638D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.Immutable.dll", + "source": "vendor/pwsh/ref/System.Collections.Immutable.dll", + "bytes": 75048, + "sha256": "D4145735395753187965A6F0A8BF6BB6F301563C4D4080249614B8487B0604AC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.NonGeneric.dll", + "source": "vendor/pwsh/ref/System.Collections.NonGeneric.dll", + "bytes": 22864, + "sha256": "99ADF08652A16E5AF30797B98742851B94604AA44BAB677DDC38C7826BD529A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.Specialized.dll", + "source": "vendor/pwsh/ref/System.Collections.Specialized.dll", + "bytes": 25896, + "sha256": "17EF2B9433A1F1E9FC956786067F8D60672437D3FEB76AF84175F2B3BEE4DED1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.Annotations.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.Annotations.dll", + "bytes": 31568, + "sha256": "43C467051FEAFAA27144CC672836F38108377439245833FC8D99DE97D3667C37" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.DataAnnotations.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.DataAnnotations.dll", + "bytes": 16720, + "sha256": "D40E8C7062E6B9CE11BA83EAB3D26BE0EEC8066B99970EA88B8E87FB75F411BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.dll", + "bytes": 15656, + "sha256": "8811E7CC4288C435F54B0350AFBFFCBB13F77618CF9C0D3436CAD89A83D29B23" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", + "bytes": 19240, + "sha256": "B96D6D2F159EE5E192B2406A0EC993C8AD470A847F2E7ACD2AA92CBC0A4C6A58" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.Primitives.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.Primitives.dll", + "bytes": 25936, + "sha256": "09A575C38BD010F3131704E9294941FF9740A0BA70BE0FD6AB8FA54C44DBFCA7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.TypeConverter.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.TypeConverter.dll", + "bytes": 104744, + "sha256": "98A3E34140052C828AB7BDB81040421616FA4E6356C27F7970DF141EF5681B42" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Configuration.dll", + "source": "vendor/pwsh/ref/System.Configuration.dll", + "bytes": 19240, + "sha256": "3952A72E1F18774F16952B46C20E80ED2BC601A06ECA01F33FA924C738022A9C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Console.dll", + "source": "vendor/pwsh/ref/System.Console.dll", + "bytes": 26408, + "sha256": "783E5EA6D049218EC30316598654E031415F0D1EC6CE6932C2D1A9CBD97A77E0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Core.dll", + "source": "vendor/pwsh/ref/System.Core.dll", + "bytes": 23336, + "sha256": "AC23FAF1B37FA49F1B117BAAD951A65609E799A6E669A6DF06D3552677E0C086" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Data.Common.dll", + "source": "vendor/pwsh/ref/System.Data.Common.dll", + "bytes": 154448, + "sha256": "E563226DCE5E8D2F1ECF24B13A37D8948E42208D7B93BC9812475074382F2303" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Data.DataSetExtensions.dll", + "source": "vendor/pwsh/ref/System.Data.DataSetExtensions.dll", + "bytes": 15656, + "sha256": "AEEAE0755C8C7F6747765CDF82FA43182E320E97F2035E3A0A19F21905E6C113" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Data.dll", + "source": "vendor/pwsh/ref/System.Data.dll", + "bytes": 23848, + "sha256": "8BEE1D4675FEE975B5454E7407827AA3C6D4A2E53B9EFB40BE5409AC50900EDB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Contracts.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Contracts.dll", + "bytes": 19752, + "sha256": "4366CCD79A875FB64E38813A81BDF2F61915B4C838CB74572D87919C2E7DEE41" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Debug.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Debug.dll", + "bytes": 15656, + "sha256": "8B160FFE7518588BF64E13C01C6DCB2EF504D64F38BBF0F198D31E7B698DB92C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", + "bytes": 43304, + "sha256": "6CAA24C3FE3819A6F78AEDB0E8F718A6BD83EAB4D9A786C609A228E330557BB0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", + "bytes": 16720, + "sha256": "AE73589F3288FC3648A718797361F0AB37606CC18667C8AAEBEA2787F62736A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Process.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Process.dll", + "bytes": 31568, + "sha256": "CD121308CB25B34EBC2549D72DA361E56A093B29EF5AB0A85870D13FB91DE052" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.StackTrace.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.StackTrace.dll", + "bytes": 22824, + "sha256": "E287630ABE5753B658E64E947616C8BC8291BCD27BFCADC745A33818A43AD30D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", + "bytes": 17704, + "sha256": "2C567AD9A2EFE67D171E0AB06D0C7035383508588CAE62B3C3D08F579A064595" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Tools.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Tools.dll", + "bytes": 15144, + "sha256": "841C5E2B6C171FA9AAFF0FC6295F3FBC93DC5DCAB44118B62F1C942BB674AFF2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.TraceSource.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.TraceSource.dll", + "bytes": 27984, + "sha256": "ECFE5B354EA6B7BF3736D8C4B02AE322C766F2BCCF260182632A492F3309D883" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Tracing.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Tracing.dll", + "bytes": 28968, + "sha256": "E0F72905EB1B1CFD6DB130E6B2F18424788769D79FAE486054A242615D713A50" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.dll", + "source": "vendor/pwsh/ref/System.dll", + "bytes": 49448, + "sha256": "FF11E2AB9009EF141E315187254F8327DD1E2591E44B8BF158730869028F1F0E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Drawing.dll", + "source": "vendor/pwsh/ref/System.Drawing.dll", + "bytes": 20304, + "sha256": "585A8DA9952D715F8DE7A7B1879B59D68FF8608B92F6ED1C5F0E1D251180D3C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Drawing.Primitives.dll", + "source": "vendor/pwsh/ref/System.Drawing.Primitives.dll", + "bytes": 35616, + "sha256": "8FFDA23FAFB73BDAC7B6C8DFCAC1C06894C729F65157E218980DB9A4B74C7427" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Dynamic.Runtime.dll", + "source": "vendor/pwsh/ref/System.Dynamic.Runtime.dll", + "bytes": 16168, + "sha256": "C0EAC30C791DB8C1A2B50B61C50D029F4DDD0EADFDD35433745001688A3E7466" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Formats.Asn1.dll", + "source": "vendor/pwsh/ref/System.Formats.Asn1.dll", + "bytes": 26408, + "sha256": "C6F8CB70A20321AC4C93219CCB5264F50D21D7F7CDD0BD61C02360FF87CD38F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Formats.Tar.dll", + "source": "vendor/pwsh/ref/System.Formats.Tar.dll", + "bytes": 20264, + "sha256": "C6D0AA4D2E3ED3AB6C1949669D79BFC763FC9E7B04830AB7174D4CC55569757C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Globalization.Calendars.dll", + "source": "vendor/pwsh/ref/System.Globalization.Calendars.dll", + "bytes": 15656, + "sha256": "E0F3F3F6E4ACC7B6755782FED5CB075ECF70BEEE4FC5D404579AE99BC0883EF2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Globalization.dll", + "source": "vendor/pwsh/ref/System.Globalization.dll", + "bytes": 15696, + "sha256": "66902CFFD057673B4B77AA68FAF80D33FFA4C94510E61DB2A067943CC53B7702" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Globalization.Extensions.dll", + "source": "vendor/pwsh/ref/System.Globalization.Extensions.dll", + "bytes": 15144, + "sha256": "BE0390557CC72078A7217CF94FD44B9AB66AA30838C00A92AA78C62469C44AA0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.Brotli.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.Brotli.dll", + "bytes": 18256, + "sha256": "1401375A39B7BA5E65E0B49274C0168B6C8B7E5455AFEFD269C1418FD019E997" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.dll", + "bytes": 21800, + "sha256": "AC573888BA94E958243674D82E51E88692E5BEA6FD94BB68D87D30C28CDAECF5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.FileSystem.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.FileSystem.dll", + "bytes": 15184, + "sha256": "4AEEC2C0189F73BA8122F05C0D759008DDAE1A8873B167CC8A558D53F2AF80E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.ZipFile.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.ZipFile.dll", + "bytes": 18728, + "sha256": "C34EF5004F535F10ADCEB66B3F20F743B15656ED4C7DCF5946A8B83A6CDEE329" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.dll", + "source": "vendor/pwsh/ref/System.IO.dll", + "bytes": 15656, + "sha256": "388400CCB189B819D6EC5F1EF70710A39641D93ED5B870FC9324866B75DFB78B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.AccessControl.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.AccessControl.dll", + "bytes": 20264, + "sha256": "38EB787FD20B30F36E207364387FD34A5C68207BAFFDDF318986216F4B6CC6B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.dll", + "bytes": 15656, + "sha256": "C251F33B29382C47B773D907463EDD9FE978A06E39EFD5670EF3B3B1D4F8C2B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", + "bytes": 17192, + "sha256": "5CE6349A752189B3BCAA553FEF38F9C0CDD1FB2E39E7CABA67C966F52EF66391" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.Primitives.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.Primitives.dll", + "bytes": 15144, + "sha256": "D3B21B8B38E48C6B4D1CB5CFE9ADFE4ABAD7B3353601FEBD3C7E4027F9EF9C6B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.Watcher.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.Watcher.dll", + "bytes": 20776, + "sha256": "07FC2B07B7D53173C695E60E93F2DA4325B68C797C4351FFF571923F01F8054C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.IsolatedStorage.dll", + "source": "vendor/pwsh/ref/System.IO.IsolatedStorage.dll", + "bytes": 22312, + "sha256": "6720027218D12697D30640FB97ACDB02438649CCCB038FFDB3AD80DC27F6F736" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.MemoryMappedFiles.dll", + "source": "vendor/pwsh/ref/System.IO.MemoryMappedFiles.dll", + "bytes": 18256, + "sha256": "028850F2CEBBCC3CCB48FCEF97751522EA6455C05FF9234686A16CC088FAC5A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Pipelines.dll", + "source": "vendor/pwsh/ref/System.IO.Pipelines.dll", + "bytes": 20304, + "sha256": "08344A31693477A61B45DFF21739BF576C7482B394FAB2FA26D2D8BA87B2CB91" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Pipes.AccessControl.dll", + "source": "vendor/pwsh/ref/System.IO.Pipes.AccessControl.dll", + "bytes": 18216, + "sha256": "80FDA558926F7D7A085A6A210596000D281E4AF2FB235E4C8DC9CBEF8F1A87D0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Pipes.dll", + "source": "vendor/pwsh/ref/System.IO.Pipes.dll", + "bytes": 22312, + "sha256": "0B7AAA8FEA2A9483857B2AC918814AD1D8FF3303C98D6EF71D4593B16ADEE9BA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", + "source": "vendor/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", + "bytes": 15696, + "sha256": "AD6A5F859589E1951103A00F45566DA079CF87BD8B03F0C6AD985A6F39F19C5A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.AsyncEnumerable.dll", + "source": "vendor/pwsh/ref/System.Linq.AsyncEnumerable.dll", + "bytes": 36176, + "sha256": "3367B7D6D2BE9D171E158B544979131E8B97328B293D6E77917234A2A7279507" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.dll", + "source": "vendor/pwsh/ref/System.Linq.dll", + "bytes": 33616, + "sha256": "9CB73D57C035503BDC70FDC422DEE5BF4F9DE97B812CD0DCB379D652C6F91205" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.Expressions.dll", + "source": "vendor/pwsh/ref/System.Linq.Expressions.dll", + "bytes": 63312, + "sha256": "772112C3F1C10FB713CCC3AD62CC313C2CAD1CBAF122EF6D25120AA7B65B0447" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.Parallel.dll", + "source": "vendor/pwsh/ref/System.Linq.Parallel.dll", + "bytes": 31016, + "sha256": "B4B597374FFEBCCF8A46793F03D5E6D5E477DFC5047157217D79449F9729E761" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.Queryable.dll", + "source": "vendor/pwsh/ref/System.Linq.Queryable.dll", + "bytes": 32040, + "sha256": "5BAF83B98F103D9304EAC427C4296C9DD501D320B7B5495ED8EDE892AAF1076D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Memory.dll", + "source": "vendor/pwsh/ref/System.Memory.dll", + "bytes": 57168, + "sha256": "D7E674A1FEFF0546C9932B0FCFFA9D055976736342A4B1904F1F27894F3C0ED6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.dll", + "source": "vendor/pwsh/ref/System.Net.dll", + "bytes": 17192, + "sha256": "481B38C912F8779B3510CD00F079E45096EA22B6DEFDAAB66780D5AC3AE6986A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Http.dll", + "source": "vendor/pwsh/ref/System.Net.Http.dll", + "bytes": 60200, + "sha256": "3FF081221D0ADEB99B5CF40788E20DAB1D0D706618044B34E68A47529C761601" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Http.Json.dll", + "source": "vendor/pwsh/ref/System.Net.Http.Json.dll", + "bytes": 23336, + "sha256": "BF97FCFA0D20ADB597878F902B761480FB02FA0F9F81521F16F2455DDC6A1ED2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.HttpListener.dll", + "source": "vendor/pwsh/ref/System.Net.HttpListener.dll", + "bytes": 25384, + "sha256": "03CA98ABEB9EA2C7E372C31AA460D2A9E526617C22FA97C9D8750977167A9634" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Mail.dll", + "source": "vendor/pwsh/ref/System.Net.Mail.dll", + "bytes": 32040, + "sha256": "8193E3652B1ECB1AA46258087BB5F44BECD72693552DA120E1274EF1AD443E53" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.NameResolution.dll", + "source": "vendor/pwsh/ref/System.Net.NameResolution.dll", + "bytes": 17704, + "sha256": "97E0CC175BF13E4861E1508820F08561B38F28E98EBC9C5ADDB28C58FA7C344D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.NetworkInformation.dll", + "source": "vendor/pwsh/ref/System.Net.NetworkInformation.dll", + "bytes": 33576, + "sha256": "B90B729446F4ED3FA470B5C96E9C5ADECBF4E438EAA91D0548832C58673A4C18" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Ping.dll", + "source": "vendor/pwsh/ref/System.Net.Ping.dll", + "bytes": 20264, + "sha256": "F623594E3309D40D36A7DD63DCB5251F72EB94EB2E74246805B4A0D23FC8C6B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Primitives.dll", + "source": "vendor/pwsh/ref/System.Net.Primitives.dll", + "bytes": 37160, + "sha256": "129E7B324FBC12F8C54B89E6D6706B08455532FB64A8913038ADD92BE2924244" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Quic.dll", + "source": "vendor/pwsh/ref/System.Net.Quic.dll", + "bytes": 23848, + "sha256": "F9651DCDE17F5823C7D42BE080059FD9F88AED81D91C64E58F95AFCA44A2E24A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Requests.dll", + "source": "vendor/pwsh/ref/System.Net.Requests.dll", + "bytes": 41256, + "sha256": "D45910C06D2C2FCD6039E8B4076B5878FD195D0FC09D27C4CEB4009AD9491672" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Security.dll", + "source": "vendor/pwsh/ref/System.Net.Security.dll", + "bytes": 53544, + "sha256": "C4AF7099D2077E34C7074E94EFEB57AC5707AF128EE6E5E0E75D398B8E5843BA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.ServerSentEvents.dll", + "source": "vendor/pwsh/ref/System.Net.ServerSentEvents.dll", + "bytes": 17192, + "sha256": "87BA81CD8FD6AB3A9A3F4DCE922849AEA832928F52495788D371DDA66E57DA01" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.ServicePoint.dll", + "source": "vendor/pwsh/ref/System.Net.ServicePoint.dll", + "bytes": 15144, + "sha256": "84FA83CD96E62FDA02991B8A8E99FA1C7B3AC08807B04F7486A896CFE6B4A760" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Sockets.dll", + "source": "vendor/pwsh/ref/System.Net.Sockets.dll", + "bytes": 47440, + "sha256": "8B7CEBAFD550083A8D242DBCFB7501384D17142AD6C2BBB047DD37A524762179" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebClient.dll", + "source": "vendor/pwsh/ref/System.Net.WebClient.dll", + "bytes": 27944, + "sha256": "E4E6CC79E23892C8E67A4CB594BB304A26A0328507C45DC2D1998BC6836311A0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebHeaderCollection.dll", + "source": "vendor/pwsh/ref/System.Net.WebHeaderCollection.dll", + "bytes": 19240, + "sha256": "C4EE735A52B966F3805CFBD5C172F151FFE013BD70C080F15C2F68BE628CD357" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebProxy.dll", + "source": "vendor/pwsh/ref/System.Net.WebProxy.dll", + "bytes": 18216, + "sha256": "42C8EB8BF1563806069E1DD1C6C651159DAA5589B7F69FF817CDEAB3BD02915F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebSockets.Client.dll", + "source": "vendor/pwsh/ref/System.Net.WebSockets.Client.dll", + "bytes": 19240, + "sha256": "5AC14D45C1B92E9C831B37591941EA01F3097A39D130BC292506DD099AEB7260" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebSockets.dll", + "source": "vendor/pwsh/ref/System.Net.WebSockets.dll", + "bytes": 23848, + "sha256": "C16043D3FFA90A8FDAF193F0DF91F99D6C23518D8432B4CAA2D70A92D4C3CFA7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Numerics.dll", + "source": "vendor/pwsh/ref/System.Numerics.dll", + "bytes": 15184, + "sha256": "35ADC9360421B3B7A3D50399CB2C651C160158D7E8C302907ACBCE5F61F4A4F7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Numerics.Vectors.dll", + "source": "vendor/pwsh/ref/System.Numerics.Vectors.dll", + "bytes": 58152, + "sha256": "CC06324F279C581D8F76C0C8F6B011CF8D05EFD0C95E998D68F9243DA124EB52" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ObjectModel.dll", + "source": "vendor/pwsh/ref/System.ObjectModel.dll", + "bytes": 23336, + "sha256": "3A591E043CD443761537580084F714392BB3278176C9A19F1BE8700A29EF6F56" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.DispatchProxy.dll", + "source": "vendor/pwsh/ref/System.Reflection.DispatchProxy.dll", + "bytes": 15656, + "sha256": "0D46DFB9C4FFC27D05A1C089D8F60A1CB365855140A9B30972133208270DD08B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.dll", + "source": "vendor/pwsh/ref/System.Reflection.dll", + "bytes": 16168, + "sha256": "95059346D6271DD6D0322CC321E5FB8CCEAB662F550C67AEE7615059E41739BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Emit.dll", + "source": "vendor/pwsh/ref/System.Reflection.Emit.dll", + "bytes": 44368, + "sha256": "29021699E4128964365A86C22AFB3B0A8368DA7A3F7463C9C1A8FC0A1402A39F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", + "source": "vendor/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", + "bytes": 20776, + "sha256": "0201069B706D397C573B9342902613A71879A2701994A48E2821C32D43101AE4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Emit.Lightweight.dll", + "source": "vendor/pwsh/ref/System.Reflection.Emit.Lightweight.dll", + "bytes": 19240, + "sha256": "EEF97E1049D8DD7ECC695080DD22C199EE8CA82B1CFCB3E33866CF7125DBD1A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Extensions.dll", + "source": "vendor/pwsh/ref/System.Reflection.Extensions.dll", + "bytes": 15144, + "sha256": "CEB1B81E1EB488D87BD43D16DC54450B2A78E0B653C44C988AAE2A20ABC162BC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Metadata.dll", + "source": "vendor/pwsh/ref/System.Reflection.Metadata.dll", + "bytes": 122152, + "sha256": "92EC811516B768220CC41FB6A7197F7BA735F97B694716DFD1BFDF274078A90A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Primitives.dll", + "source": "vendor/pwsh/ref/System.Reflection.Primitives.dll", + "bytes": 21840, + "sha256": "FA52402A7B4096EF109853F6BF03ADEF0E1DE67E3652BB31F413D197F19F9B22" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.TypeExtensions.dll", + "source": "vendor/pwsh/ref/System.Reflection.TypeExtensions.dll", + "bytes": 19752, + "sha256": "78178074BDB0F7714C634E4A1B425570264B13B2CA5B9AD6880622BFF0BDA58E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Resources.Reader.dll", + "source": "vendor/pwsh/ref/System.Resources.Reader.dll", + "bytes": 15144, + "sha256": "20BDB5D302B5E28F6042C9F281CFA3AE2F2358544CC40783052FE5ABF6AA859B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Resources.ResourceManager.dll", + "source": "vendor/pwsh/ref/System.Resources.ResourceManager.dll", + "bytes": 15696, + "sha256": "F709EB5CF16E26679782784C428825019785FCC7085562092425D4999BECE03D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Resources.Writer.dll", + "source": "vendor/pwsh/ref/System.Resources.Writer.dll", + "bytes": 16208, + "sha256": "7E772363E799C78D8D1D37FE1D412723DA9350496A6D7BACD37491F2BB777CC4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", + "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", + "bytes": 15184, + "sha256": "6E685EC0477B21A688CD38BFCAFC673888CDF186C99DF3C96FAB352E96D6FDC8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", + "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", + "bytes": 17192, + "sha256": "1E5EB23824055D6AF5BABB6A7CE47E314F78B0B464466DB32B60F543304A9140" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.dll", + "source": "vendor/pwsh/ref/System.Runtime.dll", + "bytes": 862504, + "sha256": "10CEAA9B398F512B46DEF6FD16FB5374C82AC03A7495859ADBAAA70868FE70AA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Extensions.dll", + "source": "vendor/pwsh/ref/System.Runtime.Extensions.dll", + "bytes": 17704, + "sha256": "3D7B517D4FD89EE58C53E0F642CC9BE42E7A8669B51A85E861DAFE118067CBFD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Handles.dll", + "source": "vendor/pwsh/ref/System.Runtime.Handles.dll", + "bytes": 15656, + "sha256": "985457A3E6A67E96A445EE0FB69B2D58BCDADC4C964253FB63E2D5061A36B050" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.InteropServices.dll", + "source": "vendor/pwsh/ref/System.Runtime.InteropServices.dll", + "bytes": 101160, + "sha256": "8CED47E6A2A717C459C3FA13F76A521B35917E123A9B1EE7ED1B5DE13DB80709" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", + "source": "vendor/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", + "bytes": 25896, + "sha256": "CB551CC19DF1067DD8897D14BF02ECFA63D8ECF96067A21902406791D68CE226" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", + "source": "vendor/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", + "bytes": 15656, + "sha256": "C26D9F94329422BD000F28F6C65FDC162BF9397A6D29E418A189D4CA49C5C3EC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Intrinsics.dll", + "source": "vendor/pwsh/ref/System.Runtime.Intrinsics.dll", + "bytes": 466216, + "sha256": "DF8F497F64B6A3AC02941FAE92E057FADDF93F3F135F2F6444F725617C38B619" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Loader.dll", + "source": "vendor/pwsh/ref/System.Runtime.Loader.dll", + "bytes": 19752, + "sha256": "42884BCDC89EBD4EE509D5FCB4A679B1D7EEBD6DDD9B66CB9E118063DDD30482" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Numerics.dll", + "source": "vendor/pwsh/ref/System.Runtime.Numerics.dll", + "bytes": 36136, + "sha256": "4AFC84F3E73B63FF6683941FCFDA44E8A6AA5227B6AFC128DCB979865E23D183" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.dll", + "bytes": 17192, + "sha256": "D565FC261CA98CBD14833821F777860291E8A0F775DE7AEE91778A9F6647F6F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Formatters.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Formatters.dll", + "bytes": 23848, + "sha256": "EF24BB5669B54B31DA46EB13238FB2B7F880DD780DC9B34A224A69DD59D93AF8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Json.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Json.dll", + "bytes": 20776, + "sha256": "DE7EECA7DF9F6BB664383FBEAD141719BD08D88B6AEF43C0605528719F607E62" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Primitives.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Primitives.dll", + "bytes": 19752, + "sha256": "A547B0D205CEBE84508C2DDEFD56D063C48CDFEAE5B0C44BDE6BAE9F3D72F98A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Xml.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Xml.dll", + "bytes": 39760, + "sha256": "117BCB0DD828EAF6BFAD06D8380BA32D32729B45879D907D7023FE73E9655BD9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.AccessControl.dll", + "source": "vendor/pwsh/ref/System.Security.AccessControl.dll", + "bytes": 37160, + "sha256": "2BD23D6AA92B920DAFD374E1AB47ECB597EF3B1CF1652A4EC4944A0DEFB1A4AC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Claims.dll", + "source": "vendor/pwsh/ref/System.Security.Claims.dll", + "bytes": 32552, + "sha256": "693A61DC317AB5A651EF598578A4B55912AC39342A0842FEA003761F861B0CAF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Algorithms.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Algorithms.dll", + "bytes": 17232, + "sha256": "EB99AE7DBE058A1B015ABA6769CE1F65155B7FF51FF9A7203FCF6D7CDEA5FEF7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Cng.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Cng.dll", + "bytes": 16168, + "sha256": "7319F97896D92A7D0729CF28DC6C4D1F797C1520B656EE01A780EEC726EC2C16" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Csp.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Csp.dll", + "bytes": 16208, + "sha256": "4002AF141E9CB29875B677841ADF64A9681E5672C2595492DCBC161460D4E121" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.dll", + "bytes": 163112, + "sha256": "A700A7F6C32169E62BE879088AA2218BDD5450C3945ED6C6D0ACB3D25DB0B1BF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Encoding.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Encoding.dll", + "bytes": 15656, + "sha256": "994494AEC7760F40FADE2264AA7C13EEBAE36360F0B1399A7134BB19C9D58FDA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", + "bytes": 15656, + "sha256": "1A8902BBAD1B577A98BEC62A8472105DC1E9A253E390F69FBE3D167E93206968" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Primitives.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Primitives.dll", + "bytes": 15656, + "sha256": "194CCCEA9354BB248E44C954B5373F9069163F8F21524B3EE4B3185FE436BC15" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", + "bytes": 16680, + "sha256": "7439C3B90BC2EE49A9275B28ED29052B4D48B45999AC1B7D25593033B1BB8DD6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.dll", + "source": "vendor/pwsh/ref/System.Security.dll", + "bytes": 18256, + "sha256": "D8EFD304765E859369698EFEF9B3CA707DEFA8E142721FE5605D7A2DE7D38375" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Principal.dll", + "source": "vendor/pwsh/ref/System.Security.Principal.dll", + "bytes": 15144, + "sha256": "1D3E351058BE3A33214B23B308FE57F78CD6FD4D8FAD448FC84CBBE7376129F9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Principal.Windows.dll", + "source": "vendor/pwsh/ref/System.Security.Principal.Windows.dll", + "bytes": 26920, + "sha256": "4B8EAB6E832E99E8C08DB2B9649F95AE8298E1105DEEA45A62E7D3B68C6593B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.SecureString.dll", + "source": "vendor/pwsh/ref/System.Security.SecureString.dll", + "bytes": 15696, + "sha256": "73AAC97072B7DEEFA4DE9647BF822162224F1452AD467841B989FF89D4D94595" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ServiceModel.Web.dll", + "source": "vendor/pwsh/ref/System.ServiceModel.Web.dll", + "bytes": 16680, + "sha256": "CE91013EE3A9A9D69A85E258F7749E6ECE488295D7CB66F7C1079F2E8678FC5E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ServiceProcess.dll", + "source": "vendor/pwsh/ref/System.ServiceProcess.dll", + "bytes": 15696, + "sha256": "63FC498C9D04E3C4345DFDB8016AD0639D54562DD7D8C707C072792C854DF944" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encoding.CodePages.dll", + "source": "vendor/pwsh/ref/System.Text.Encoding.CodePages.dll", + "bytes": 15656, + "sha256": "2D66F9AE5E7439756EBA063C0DF86D6D3A0DC571F4BB07ADD18C59D6F10138CE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encoding.dll", + "source": "vendor/pwsh/ref/System.Text.Encoding.dll", + "bytes": 15696, + "sha256": "5E347DB6B4F9B2746D50E1AC85BE2D9CC7DC4038FDE98AC4295BC7030C83CDA9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encoding.Extensions.dll", + "source": "vendor/pwsh/ref/System.Text.Encoding.Extensions.dll", + "bytes": 20776, + "sha256": "D6224360B0E91DFE544EC3BAF0D5DED78A13349F6378493C96344A310C8CAF08" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encodings.Web.dll", + "source": "vendor/pwsh/ref/System.Text.Encodings.Web.dll", + "bytes": 26408, + "sha256": "F9F6120753FC9FAEAA5B09E10EF309594DBD4C20C8CDF1717EB910455549793A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Json.dll", + "source": "vendor/pwsh/ref/System.Text.Json.dll", + "bytes": 82216, + "sha256": "4133A8597A10EEB99D56A962FF581DB9FD3AC85506EE3A20A2D23119C73A4346" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.RegularExpressions.dll", + "source": "vendor/pwsh/ref/System.Text.RegularExpressions.dll", + "bytes": 36648, + "sha256": "347C497CF2BD60A298EFB59FE47156909CA854FDAB4DABB944AF7D6F8C270CEF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.AccessControl.dll", + "source": "vendor/pwsh/ref/System.Threading.AccessControl.dll", + "bytes": 20776, + "sha256": "8449A1EE09EDF8BDC69C61605BEBEC602C7B28A9CA401CA463512E4D369A3B7B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Channels.dll", + "source": "vendor/pwsh/ref/System.Threading.Channels.dll", + "bytes": 19240, + "sha256": "C72028E4423FB04F3A644A76E1807E3651191AEB606B8F7B15B3650E4714CDCA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.dll", + "source": "vendor/pwsh/ref/System.Threading.dll", + "bytes": 33576, + "sha256": "0C2A3B4FDDB2D732B8EBD0BCA9AC57A8C66C9D4F5FBCB55FCD17E3CBCF678A4B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Overlapped.dll", + "source": "vendor/pwsh/ref/System.Threading.Overlapped.dll", + "bytes": 18768, + "sha256": "7837D3BD05E28F95749029C832FB1D0CE5BF403A17BEF7BF95DF522BA93DAC3A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.Dataflow.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.Dataflow.dll", + "bytes": 32040, + "sha256": "5D605D4C470C38BC37A04D58B4023A9C60179172790A29F4F7CB92ABA6251648" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.dll", + "bytes": 16680, + "sha256": "F80B21F7DAE04377B73945456CABE25E2AB1F4B564095AED546C2AA571644567" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.Extensions.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.Extensions.dll", + "bytes": 15696, + "sha256": "F148E3A1414B7C2763AE21529AD79AD759F7CFD3C885256B64B0D2209DEC2613" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.Parallel.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.Parallel.dll", + "bytes": 20264, + "sha256": "97F9CCB69D9F1D4D86DF2869E1EF667C4C68285B04F336B1188E633C8F814D05" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Thread.dll", + "source": "vendor/pwsh/ref/System.Threading.Thread.dll", + "bytes": 23888, + "sha256": "FFF2281F039C07E564BCB198E041627082702BEDA24DCCC8DF39683701EA25A0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.ThreadPool.dll", + "source": "vendor/pwsh/ref/System.Threading.ThreadPool.dll", + "bytes": 18216, + "sha256": "12FEDF2DE6449BFEC4C1A08D762493BC0188E1B9B0A58028289105ED81B983F5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Timer.dll", + "source": "vendor/pwsh/ref/System.Threading.Timer.dll", + "bytes": 15144, + "sha256": "9E4620846572F49AEB476AA99405071B4BBD0DF4323E4E232787ED73642B1EE5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Transactions.dll", + "source": "vendor/pwsh/ref/System.Transactions.dll", + "bytes": 16720, + "sha256": "56ABFCA0350C111D077B3D86A2469EA3DA4FCCC1871F7C51A8B4FDB664BB0EDE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Transactions.Local.dll", + "source": "vendor/pwsh/ref/System.Transactions.Local.dll", + "bytes": 25896, + "sha256": "C9FAFFC186D677DE7800FF7D4D1B0D204AE7614E6B8518ADD582C840D7532A3B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ValueTuple.dll", + "source": "vendor/pwsh/ref/System.ValueTuple.dll", + "bytes": 15696, + "sha256": "07A5347F6A597E5E0D72109EFA1A525697E8C3F74FB0029005500F8EBBF9187A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Web.dll", + "source": "vendor/pwsh/ref/System.Web.dll", + "bytes": 15144, + "sha256": "BC260B3DB247AD9A6E4A82A275DA85C3691A41272CC745CB7EAEFCF1F31CC817" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Web.HttpUtility.dll", + "source": "vendor/pwsh/ref/System.Web.HttpUtility.dll", + "bytes": 17192, + "sha256": "F26B0586048FDF472E0FB33CA9435B163C91325DE4281FCE8684251CACC304CC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Windows.dll", + "source": "vendor/pwsh/ref/System.Windows.dll", + "bytes": 15696, + "sha256": "716EEE98CEEE87D38F0B301224462F8442AA793F4B1B9C3FFAC315753320AB37" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.dll", + "source": "vendor/pwsh/ref/System.Xml.dll", + "bytes": 23376, + "sha256": "277A84BA155FE7808A514F32694A8157EC9F0EEEEF00EF9643E5250004054FF0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.Linq.dll", + "source": "vendor/pwsh/ref/System.Xml.Linq.dll", + "bytes": 15696, + "sha256": "AE04903B25717FA4695E7E63489091F0D461E791E0BD264C6C85250D668F540D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.ReaderWriter.dll", + "source": "vendor/pwsh/ref/System.Xml.ReaderWriter.dll", + "bytes": 116008, + "sha256": "942964D32F72BA10288593F212E3A52C4811B65CA4D5F1A3979C92D2D526AA4C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.Serialization.dll", + "source": "vendor/pwsh/ref/System.Xml.Serialization.dll", + "bytes": 16208, + "sha256": "489694401C60905CB6DCA8EF0BF738CB609CD45B337126A01C825B913086BA43" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XDocument.dll", + "source": "vendor/pwsh/ref/System.Xml.XDocument.dll", + "bytes": 34088, + "sha256": "05F6380411A81245960714A9FD81F6134F7BD279CD70B248A1E1586977798F1A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XmlDocument.dll", + "source": "vendor/pwsh/ref/System.Xml.XmlDocument.dll", + "bytes": 15656, + "sha256": "A50B9B3A03596E36E73E7A7D54F4D018E8DCFAA7FE79E8CF900AF8838EF3EC36" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XmlSerializer.dll", + "source": "vendor/pwsh/ref/System.Xml.XmlSerializer.dll", + "bytes": 50984, + "sha256": "F957611B80E082A453264A13BF27B5B251FAF598F377F948195123A374B2E8F6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XPath.dll", + "source": "vendor/pwsh/ref/System.Xml.XPath.dll", + "bytes": 16680, + "sha256": "0E68260B4C36D00C9A77C30A270100461123C0E7E1A37402BB7F67174051B8F3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XPath.XDocument.dll", + "source": "vendor/pwsh/ref/System.Xml.XPath.XDocument.dll", + "bytes": 16168, + "sha256": "B8F74E198C3C2CD00CCC90549579F1B69C6AE1655F8B348ACE414FD9075C921F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/WindowsBase.dll", + "source": "vendor/pwsh/ref/WindowsBase.dll", + "bytes": 16208, + "sha256": "0EC46A38F29A234B9A4D0AE9364C8322F661A47C831C5B948F2787B7CA12DBBD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/RegisterManifest.ps1", + "source": "vendor/pwsh/RegisterManifest.ps1", + "bytes": 17502, + "sha256": "13D50727D810BABE2A8FCBC77A0A4EA967A6C78DE4960469116DF9D8EEF53EC0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/RegisterMicrosoftUpdate.ps1", + "source": "vendor/pwsh/RegisterMicrosoftUpdate.ps1", + "bytes": 16907, + "sha256": "8DD5BF144D53B976B3F414973FB0F6C886F754E515427A2E8FD58B9C3B3E1B52" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/base.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/base.xsd", + "bytes": 6434, + "sha256": "B860EF30B01C57486A56948800E9B2DA116EC473CBECBB33FA9F71B8CEAB90AE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/baseConditional.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/baseConditional.xsd", + "bytes": 5297, + "sha256": "D26D4EF29124B2F9107B167D73E611F649FB2021D30C48A8616C6CC96230DE81" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/block.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/block.xsd", + "bytes": 917, + "sha256": "9A8B3C184394711480C38D71DB4850659B87729DF9EAC5A2235BDCF37A2020CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/blockCommon.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/blockCommon.xsd", + "bytes": 3872, + "sha256": "928717B190B014F271C09C2FFB7633C1E660B26F0ECBDD926662121914795789" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/blockSoftware.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/blockSoftware.xsd", + "bytes": 1282, + "sha256": "88AA3462598B7C394350F83EF366E5F83885C50A9C6966F02179228277358334" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/command.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/command.xsd", + "bytes": 13061, + "sha256": "A3DA7F8F2260A826CE253C33BDA8576246B4298794765EDC4F80A433357C6B87" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/conditionSet.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/conditionSet.xsd", + "bytes": 4279, + "sha256": "506415CD77EDE25B33C3E28FB89012B282E90B5D01F266323A83FEA9625F8EAB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developer.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developer.xsd", + "bytes": 11403, + "sha256": "AB9A7DDB85084F331ED79CB34374509767D9A64C510B0264682D5E682DC59497" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.rld", + "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.rld", + "bytes": 115907, + "sha256": "CD8A9A6B113DB5005F2A5C8321E52368411691B5F0C5EB90595E2A97B0FDEE89" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.xsd", + "bytes": 14520, + "sha256": "BBC9B2DD6D4473F09CE95A4E44B7F654A8D34A70197BEABACD1C9FF5233C3DEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerDscResource.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerDscResource.xsd", + "bytes": 2896, + "sha256": "7C22D9A1393062734E4923657C48D9117F6AC824D4E3F85EF88D8DE23F8C7BDF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManaged.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManaged.xsd", + "bytes": 10238, + "sha256": "5585FD25ED4FA8D39B75E9A83082EAB1D305B00839E7BCF05A24530E3D91D6EA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedClass.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedClass.xsd", + "bytes": 3693, + "sha256": "4CB1C23D86D22B4BCC5ACA98CD309C37F54A30358D673115D460FEEACB78DF9A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", + "bytes": 2557, + "sha256": "0CA55A744AD968B537742ED37CD62852DF01CD96533D4E17874AD4F75271C8DC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", + "bytes": 2558, + "sha256": "961DAA1C4B3513B83CF2E37FDB1361BE0D123E8B2CFE7F32510AD534FEA98E3D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", + "bytes": 2306, + "sha256": "3AB7329914D7A9A6D711A62405089E736CE714EE4DE72F3493222887A72FDD39" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", + "bytes": 3108, + "sha256": "93EBD3AF5A078830839B10C9DB753CD445DC2D1A78A5449C90AC605E7FFC397C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedField.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedField.xsd", + "bytes": 2803, + "sha256": "B17F73ADD6678C4BC259065ADD380A9070531BC24A66AF3C4CAD3CF3D811F3A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", + "bytes": 2882, + "sha256": "B851D8BE146F1F51E5857045C28726585CC96A29C5A5A57DFC86CF7C509D8234" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", + "bytes": 2544, + "sha256": "36EB2BC76B2CDBFD908DF62E16CA915F7490CE75CF7BC386974EF89AA77B32FF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", + "bytes": 3359, + "sha256": "68D68D8CEC8181C0C3046B419426EE1BD4D0371ABFDBF9A6789755B0F5764D91" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", + "bytes": 1896, + "sha256": "053C18557BE353860222E14D7CE5E5844F5A746BA45347A9E08DA1D5A2B29F3B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", + "bytes": 2573, + "sha256": "7D2695D2F6AB6EB3C0BAB6125EEE415545C3EABE0923B1B37519F2D4A8B9251D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", + "bytes": 2843, + "sha256": "832A25485EAA9060868E9258AA1F93626804ED439EE2B43B2D74F9CB0DC438C4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", + "bytes": 2951, + "sha256": "B7D1F7DDBCFEF43BD171FD78D5A255089A97762AD67C116B6A6998815905901F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerReference.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerReference.xsd", + "bytes": 12604, + "sha256": "42C585D48C418C7EB47CBA298A147923597B967E236997BB815301B57E31CB92" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerStructure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerStructure.xsd", + "bytes": 4112, + "sha256": "D1C0E02BB7207095E49EFC216A574B596A82E97B88E686E1C7A9717362623C44" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerXaml.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerXaml.xsd", + "bytes": 6746, + "sha256": "83ED2EE26CF6AB4C95A880F1424F7D5E09385B71239B84B3CFFB58288EEFF3B2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/endUser.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/endUser.xsd", + "bytes": 2144, + "sha256": "24CAEE5D0536ED81EB68D6FD836CF08849E056497B6064971A145397A04EB853" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/hierarchy.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/hierarchy.xsd", + "bytes": 10070, + "sha256": "D50D8096A11350A8E3B6873CF9CBC173B8A4322324B16F949538B1B98857C5B4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inline.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inline.xsd", + "bytes": 7644, + "sha256": "5A1A2D4586203BB3D6A4E6EC93E4D09D0AB66D33539B88A2550A36100D1E16FD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inlineCommon.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inlineCommon.xsd", + "bytes": 8860, + "sha256": "02071402B963966AD574AB09BB42719BDB1CE6CD7E9AF260A13FAF9EBB580C55" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inlineSoftware.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inlineSoftware.xsd", + "bytes": 8016, + "sha256": "C444CD1121A8B83B0FFE5A8467CC10D2D810C5A7765F4B4406D1EB329CD8901D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inlineUi.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inlineUi.xsd", + "bytes": 6400, + "sha256": "E7FF025576E482632878709E208B95916CCDCA2AF108C1C7B9218FB8472C1AE3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ITPro.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ITPro.xsd", + "bytes": 1208, + "sha256": "767328AD7B14EC90731E52D04EF27873EA674D34DD30297CB0FBA2A11242E96E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.rld", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.rld", + "bytes": 115835, + "sha256": "A711A691FDFDBEEEF09CB23543A8AC3D6BBF92AABEEC05939FCA51665229E710" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.tbr", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.tbr", + "bytes": 30575, + "sha256": "0FFD2E7097B51E0C6E1C1A18867658312D39AC68D8179049521F9ECAEB2CBF08" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsd", + "bytes": 1571, + "sha256": "FDA94A92FE41A7280D7F5ECE13CFC0042D4E1049E8FF961717628412317F6AE2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsx", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsx", + "bytes": 211, + "sha256": "639E5ED55BB9FD20672C74C20020C6C96B5FF721E45E9A337133AF858998B08C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML.xsl", + "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML.xsl", + "bytes": 363, + "sha256": "BDA0F195C928E6C18E226A83C77A1477DD88B70025D78F12D92605BBD10723BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", + "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", + "bytes": 14422, + "sha256": "99E1AD569C30396EFC7F37ACD08A8F0F41B20FF82E513D738E331C0EAC5DDC1C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", + "bytes": 14517, + "sha256": "3B89CB9A72549C46A56D9EA2EA12AC53CBC7DDD3392012FDD9D69EF261961AD2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", + "bytes": 3391, + "sha256": "0FA8EACF615F4A726F846D2938E18E84BB641BB28579BD9E2AB83F3D46D505FA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ProviderHelp.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ProviderHelp.xsd", + "bytes": 6766, + "sha256": "0E6ADBE5B896F075BF55AE6F2C1966E0E5596F79BC282C2D17CC36CCF41C30CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/shellExecute.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/shellExecute.xsd", + "bytes": 2250, + "sha256": "52F48B0830346E30C782AA5C379DC547D0F40265FF1504276C904B819446409E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structure.xsd", + "bytes": 5973, + "sha256": "F35E24830E9E1A6AC79DEDFBC3F2EF03EFF8D41EF8C2414E8BA1C1A52E527391" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureGlossary.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureGlossary.xsd", + "bytes": 4186, + "sha256": "4425E365BA3B646B11E5625644D5F98B0151F60ED2BA0A28316570D7D68F6FA5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureList.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureList.xsd", + "bytes": 4173, + "sha256": "176D55D7715DDECCCBE51330E9C012E09972AE3E4A9C9AC8D1C5869B9510540A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureProcedure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureProcedure.xsd", + "bytes": 1994, + "sha256": "D323AEB14F98A01583E538DD92EF445E1094DB861DC7205D5D2062976DE539B3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureTable.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureTable.xsd", + "bytes": 6137, + "sha256": "63D397B018E5D3783AE3057658D7ADCB44F968BFD81329D2FCEA1ABB91F2C7B8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", + "bytes": 6794, + "sha256": "C37308C8CDB9AC988C94A850A848FC79707FB5D268F7E17072D6D2FB8E83E852" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/task.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/task.xsd", + "bytes": 22391, + "sha256": "808A13CEEEBDA94C0152B93A52A7825E20B5C01780A3EC6C2AE0C8AC0230ED74" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/troubleshooting.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/troubleshooting.xsd", + "bytes": 3239, + "sha256": "FBA82FDA979C33074968ED01AD95970FCF1151F3E7C298955BD501D5CA554633" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/sni.dll", + "source": "vendor/pwsh/sni.dll", + "bytes": 160040, + "sha256": "68705C4EF9AB818F2956A78E05F3FEFCE501A1448793B073B46110BEB49B47D6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.AppContext.dll", + "source": "vendor/pwsh/System.AppContext.dll", + "bytes": 15184, + "sha256": "6CD5D7FAE26815702D087EADBCE804B157CFDE3A57A8D897016822183FA2190E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Buffers.dll", + "source": "vendor/pwsh/System.Buffers.dll", + "bytes": 15184, + "sha256": "91F7DD9DBBEF1A7A2BD7C83EAB8110F8C1915DEB38723B05E7F65A5FFA491220" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.CodeDom.dll", + "source": "vendor/pwsh/System.CodeDom.dll", + "bytes": 476968, + "sha256": "28B49CE532AF31CA14A28C9B823E628457D2F3D62F96FDF0E54F206E5B1B7169" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.Concurrent.dll", + "source": "vendor/pwsh/System.Collections.Concurrent.dll", + "bytes": 231208, + "sha256": "EFD70AF76FE0182D974DDC1520F335E9764ECFDD6A517168944B76C07A31E7A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.dll", + "source": "vendor/pwsh/System.Collections.dll", + "bytes": 296744, + "sha256": "DC868C8736277084D26726D726C6CE07FFF9EAB3ACA2D3476CFACE8FC3B2403F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.Immutable.dll", + "source": "vendor/pwsh/System.Collections.Immutable.dll", + "bytes": 976680, + "sha256": "CDEFF36E2129E3A1CBF08698C4D692A13F2C25CD1C04CED0120CAEF982461354" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.NonGeneric.dll", + "source": "vendor/pwsh/System.Collections.NonGeneric.dll", + "bytes": 104272, + "sha256": "D84C613DA29BAF7D8DFD7E8AB7CD806E1086EB16B350C8377947B5EDB77B6EEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.Specialized.dll", + "source": "vendor/pwsh/System.Collections.Specialized.dll", + "bytes": 104232, + "sha256": "E203E92897552D5109F4F096EE7019443F83DAC9DC7190217359255CEED1E3A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Annotations.dll", + "source": "vendor/pwsh/System.ComponentModel.Annotations.dll", + "bytes": 198480, + "sha256": "62C92AA7C62EB1CF7B506F0F3A9A1D63A4F6941EAAF9F6D8180A6C68E05BCD9B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Composition.dll", + "source": "vendor/pwsh/System.ComponentModel.Composition.dll", + "bytes": 718656, + "sha256": "7294AB56FC43F92EBB60700463262EFFE7CCD5BF36777CC78648E5D57F683240" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Composition.Registration.dll", + "source": "vendor/pwsh/System.ComponentModel.Composition.Registration.dll", + "bytes": 108336, + "sha256": "0CB63A3465A5C2A21C2B63B37586E9E61CA6A5454363905F3283707286F2354E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.DataAnnotations.dll", + "source": "vendor/pwsh/System.ComponentModel.DataAnnotations.dll", + "bytes": 16680, + "sha256": "6D8DD0135A19828A0B4F8F62FB79AEE094ADD47EBFFA6D2F3BCDC8CC2CC2425D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.dll", + "source": "vendor/pwsh/System.ComponentModel.dll", + "bytes": 30544, + "sha256": "283C355A88E039F5A49F791F89F1AF721FB82576447E23859A4CB269085A69D1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.EventBasedAsync.dll", + "source": "vendor/pwsh/System.ComponentModel.EventBasedAsync.dll", + "bytes": 46888, + "sha256": "6B5D96FB4CF007AA303FE4EFA2AEEB345A9AD2E93DABD668EB234DC793453CD4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Primitives.dll", + "source": "vendor/pwsh/System.ComponentModel.Primitives.dll", + "bytes": 79696, + "sha256": "A6268FE9416484303A678B72837B461AAA686D4CD4509430C1E4C5203397E63B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.TypeConverter.dll", + "source": "vendor/pwsh/System.ComponentModel.TypeConverter.dll", + "bytes": 767784, + "sha256": "9FC839CC0C4138DEEEE0080C72D29069D4EA22A4385FB78CDB4FEA6A911C30C0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Configuration.ConfigurationManager.dll", + "source": "vendor/pwsh/System.Configuration.ConfigurationManager.dll", + "bytes": 1046312, + "sha256": "68265C15B59229C842C7239D9456926096DDAB61AA39C2C29F90D98B12ADC609" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Configuration.dll", + "source": "vendor/pwsh/System.Configuration.dll", + "bytes": 19280, + "sha256": "87876603DABB331C85986D8AC56F0B317C4769051F369FB37409158B924F3FD0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Console.dll", + "source": "vendor/pwsh/System.Console.dll", + "bytes": 169768, + "sha256": "53B599B15B7EB4DB618946B943B00229D998D9BF5450CF23274B0F66BCDCB4EA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Core.dll", + "source": "vendor/pwsh/System.Core.dll", + "bytes": 23336, + "sha256": "FFFC038ABE34BB1BCDC13B57C146CD121B91D8BB2F8C80D2292F50C2563D8FAA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.Common.dll", + "source": "vendor/pwsh/System.Data.Common.dll", + "bytes": 2770728, + "sha256": "1CBEF4B075D6F36E914666010CE93FC831858622E775ABE9F73966B24C816AEE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.DataSetExtensions.dll", + "source": "vendor/pwsh/System.Data.DataSetExtensions.dll", + "bytes": 15656, + "sha256": "3FECFE3CAA4A21FF3BADC04354C036FDBE20636AD40A899B0F3CCF30471D39DE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.dll", + "source": "vendor/pwsh/System.Data.dll", + "bytes": 25384, + "sha256": "B801A07AD2B340F4808F1B683997A9D1AF9A70622FF104B1E89E37405403AC49" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.Odbc.dll", + "source": "vendor/pwsh/System.Data.Odbc.dll", + "bytes": 612144, + "sha256": "F609F522D9668A3761C21FD74D4C5EF8B1C76CF21E67377E550630110B5C66AB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.OleDb.dll", + "source": "vendor/pwsh/System.Data.OleDb.dll", + "bytes": 853808, + "sha256": "C6524C47A9584C10B7359A88D6ED5462E9BDFE4B1085129296E82F8C9A2F7BB0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.SqlClient.dll", + "source": "vendor/pwsh/System.Data.SqlClient.dll", + "bytes": 2434864, + "sha256": "C37A712C5E0B4F2D388611B0CEB9BCECA902FE8751075ABA4F5DCDCC291EDA03" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Design.dll", + "source": "vendor/pwsh/System.Design.dll", + "bytes": 23376, + "sha256": "3ED1B23FBA9A8E1B61F5036CC84058335F34BE254EACDFAE83F20FF484A422D8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Contracts.dll", + "source": "vendor/pwsh/System.Diagnostics.Contracts.dll", + "bytes": 16168, + "sha256": "E529CEFF3E677722C0779EFB90F464325C37F4598D2275684B6F3E44475DCA54" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Debug.dll", + "source": "vendor/pwsh/System.Diagnostics.Debug.dll", + "bytes": 15656, + "sha256": "97A2FAD35E9C07C9BB37B627FF5517A969421A0CA6C5D31DE665B9B72FB7F1CA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.DiagnosticSource.dll", + "source": "vendor/pwsh/System.Diagnostics.DiagnosticSource.dll", + "bytes": 501544, + "sha256": "1F3297FFFDFD0C97EC4C3AB7B628839B1F43FCB44193E44E96D4480631C5F07B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.EventLog.dll", + "source": "vendor/pwsh/System.Diagnostics.EventLog.dll", + "bytes": 366376, + "sha256": "47100510AA1489B538D0D8F58FF6D97AC79DBE3F09C541CB3E49B2CB8168E3F1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.EventLog.Messages.dll", + "source": "vendor/pwsh/System.Diagnostics.EventLog.Messages.dll", + "bytes": 800552, + "sha256": "5BF5DE8C874F06129A9A016F25A2532F323098F669EC766BEE212354F05ADB13" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.FileVersionInfo.dll", + "source": "vendor/pwsh/System.Diagnostics.FileVersionInfo.dll", + "bytes": 46888, + "sha256": "1A0073C382851890010F74B559F27A033EB783EEA1E38205782E6C8DACB5D10E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.PerformanceCounter.dll", + "source": "vendor/pwsh/System.Diagnostics.PerformanceCounter.dll", + "bytes": 300880, + "sha256": "B4E97299CB6BA71B5F4AB79774F1D5AFB72061CE7866188486770C9DC7D4F10B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Process.dll", + "source": "vendor/pwsh/System.Diagnostics.Process.dll", + "bytes": 329512, + "sha256": "7F70EA57A7868DFEEEA304D5DA3843B9B52D98767BC41A81162031BAA9361405" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.StackTrace.dll", + "source": "vendor/pwsh/System.Diagnostics.StackTrace.dll", + "bytes": 46888, + "sha256": "D696C411324CF024A89D830C57CB713C487950DB5FCD7A3F8790559675A02CE4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.TextWriterTraceListener.dll", + "source": "vendor/pwsh/System.Diagnostics.TextWriterTraceListener.dll", + "bytes": 67408, + "sha256": "308FA04540B284354DC0DB843B88EFCC562B43CB3EC5ABCE2ACE437521A31AEB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Tools.dll", + "source": "vendor/pwsh/System.Diagnostics.Tools.dll", + "bytes": 15144, + "sha256": "3EF95CD1BE21128929FF5A59EB656B79E2F1CF620E7EE8518AD54EF15642ABCC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.TraceSource.dll", + "source": "vendor/pwsh/System.Diagnostics.TraceSource.dll", + "bytes": 141136, + "sha256": "C707B27E877FB0C7D38848828AB08637915E2B7A05563AA1A08327DC69EEA911" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Tracing.dll", + "source": "vendor/pwsh/System.Diagnostics.Tracing.dll", + "bytes": 16168, + "sha256": "72F5F814B5E142C87F514A38AF6C08D103FF14DAA22160867F987151C7F26C39" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.DirectoryServices.AccountManagement.dll", + "source": "vendor/pwsh/System.DirectoryServices.AccountManagement.dll", + "bytes": 710472, + "sha256": "809BF2787752D82E9B75760FB742E32901470013066E193E5A3C5D21654CD0C7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.DirectoryServices.dll", + "source": "vendor/pwsh/System.DirectoryServices.dll", + "bytes": 1021736, + "sha256": "18A53AF12ED4CDFC3F6FCBE0787E53101ACA6B5C28B730ABEC789FBBE040801A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.DirectoryServices.Protocols.dll", + "source": "vendor/pwsh/System.DirectoryServices.Protocols.dll", + "bytes": 366408, + "sha256": "B5AE31C1B36D24149561B5DD47F3D6E4124CCD3CA7DC94DB21405D945F84BD00" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.dll", + "source": "vendor/pwsh/System.dll", + "bytes": 50472, + "sha256": "2F5F3CCB0BB4E730F8EEDE5585E64AA4164CFDA1EF0C07B3E6C20DD4036F11B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.Common.dll", + "source": "vendor/pwsh/System.Drawing.Common.dll", + "bytes": 923432, + "sha256": "924F083442617CADED6A137E8926791461D8735F2ECDF4821C56B5F836283155" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.Design.dll", + "source": "vendor/pwsh/System.Drawing.Design.dll", + "bytes": 15656, + "sha256": "3ECF8AD363EBA3E9DEDF01C63698BA257C0F743F734FF92B8FD03C5E7A2F5F26" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.dll", + "source": "vendor/pwsh/System.Drawing.dll", + "bytes": 20816, + "sha256": "E3FEC0493ED82D0F8BA9AB365428EA2008C5344184634D737DAD005F5FC4F1CE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.Primitives.dll", + "source": "vendor/pwsh/System.Drawing.Primitives.dll", + "bytes": 132944, + "sha256": "00CB49E97C49A30C2282904EB851B5C8D3F3E1B240C7DFF1BB850AC8E424BDAA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Dynamic.Runtime.dll", + "source": "vendor/pwsh/System.Dynamic.Runtime.dll", + "bytes": 16168, + "sha256": "D2A82A14EE1DFCE78D7B4F1F3D161935C56CB4C07EBC8A7DAFA8260745FA3771" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Formats.Asn1.dll", + "source": "vendor/pwsh/System.Formats.Asn1.dll", + "bytes": 239440, + "sha256": "027EC42F8A81D9999425FD9B75FDA1D20F610F2FC2F57C2B55D4D37B2A1FE75C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Formats.Nrbf.dll", + "source": "vendor/pwsh/System.Formats.Nrbf.dll", + "bytes": 137040, + "sha256": "9852CE3FD4EAE4B7638F910860E382BA8EDE1CF51628C3619691B7F24CD4DACA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Formats.Tar.dll", + "source": "vendor/pwsh/System.Formats.Tar.dll", + "bytes": 272168, + "sha256": "D4E9806746831BDE8670F8AF3A0DF7AA370678CDDB7950B28F4FAD0BA3353727" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Globalization.Calendars.dll", + "source": "vendor/pwsh/System.Globalization.Calendars.dll", + "bytes": 15656, + "sha256": "48F3B8DB0B6AF61E000C70C29C977B9CEE6D4C6FA0CF794418DFF29D1BA4E709" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Globalization.dll", + "source": "vendor/pwsh/System.Globalization.dll", + "bytes": 15656, + "sha256": "769DDE445009C78BE8D4CDB28DCC6603DF2218DAFE9491F5F1B915B1945A3F6E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Globalization.Extensions.dll", + "source": "vendor/pwsh/System.Globalization.Extensions.dll", + "bytes": 15144, + "sha256": "D3497DFC648BF299FCE08FC992918C542D1ED66EBD19EF726595369B0CD513B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.Brotli.dll", + "source": "vendor/pwsh/System.IO.Compression.Brotli.dll", + "bytes": 83752, + "sha256": "A14E6E6B390BF8F96A6EF0AA4450C2B0A8455202309C589D1BA8995C0D586611" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.dll", + "source": "vendor/pwsh/System.IO.Compression.dll", + "bytes": 419664, + "sha256": "9362497FA6D4ABE5B8D3DD9F2256564E9A5C84BFB8E9C709A741503DC602EBAA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.FileSystem.dll", + "source": "vendor/pwsh/System.IO.Compression.FileSystem.dll", + "bytes": 15184, + "sha256": "C2635E40545F18B1D0B15887C1AE5DE5504BB21A407E9371BEE83CE8974EA84D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.Native.dll", + "source": "vendor/pwsh/System.IO.Compression.Native.dll", + "bytes": 937296, + "sha256": "9FBB2CE965AE5F0AFB8601F9190326C6FC7A345F4B6E3FD4AF6740A4F6CF2548" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.ZipFile.dll", + "source": "vendor/pwsh/System.IO.Compression.ZipFile.dll", + "bytes": 100136, + "sha256": "79409DEBB2680FF86B7513FBDF622CAAEB791328358F4290EC6A78B403FD4F48" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.dll", + "source": "vendor/pwsh/System.IO.dll", + "bytes": 15656, + "sha256": "5329ADEC3B4B1C869E9814912B80E50E0094F795EFF2A7E360FCCB81A1BC55C9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.AccessControl.dll", + "source": "vendor/pwsh/System.IO.FileSystem.AccessControl.dll", + "bytes": 104232, + "sha256": "1C54898A3EC1EB02A9DFF3BA726C6328D1D0F306926A9A370C0C468E065BAB17" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.dll", + "source": "vendor/pwsh/System.IO.FileSystem.dll", + "bytes": 15696, + "sha256": "CB1CF391EF106BEECB0C7D835E2B24D03101AA7B6D4B279B98C235654ED521C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.DriveInfo.dll", + "source": "vendor/pwsh/System.IO.FileSystem.DriveInfo.dll", + "bytes": 55120, + "sha256": "AD0FA0F61602BABDB65280CFB5295D7F3905E7739431CA64D531919703729D2E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.Primitives.dll", + "source": "vendor/pwsh/System.IO.FileSystem.Primitives.dll", + "bytes": 15184, + "sha256": "2673EA5084171A31D826A8D93C91C7F34798385EAC41155932192A3C9066C20B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.Watcher.dll", + "source": "vendor/pwsh/System.IO.FileSystem.Watcher.dll", + "bytes": 87888, + "sha256": "7B5E2B5E413C7A234F971FE2C1E0A04063380018336F0C22F0C52955B6E7578F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.IsolatedStorage.dll", + "source": "vendor/pwsh/System.IO.IsolatedStorage.dll", + "bytes": 87848, + "sha256": "0494E0FC71AECF0AC89EA062E0887BCB7075DFEA986B2D87E88DEEF2ADDEB5B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.MemoryMappedFiles.dll", + "source": "vendor/pwsh/System.IO.MemoryMappedFiles.dll", + "bytes": 83752, + "sha256": "8D17D7A790D463C15C3D683A92D449A1D02C423DA680D2DC5B809C9CDE1B1C2F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Packaging.dll", + "source": "vendor/pwsh/System.IO.Packaging.dll", + "bytes": 321320, + "sha256": "6B178F0D551444A502A66055EBDF3FBB65FCDE25056C43866055C2B5B759F2AA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Pipelines.dll", + "source": "vendor/pwsh/System.IO.Pipelines.dll", + "bytes": 190248, + "sha256": "40A3244F2A4EBA94430C8813F7FD4778E15E1D957DCE9034D8DFA17725A0FCF0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Pipes.AccessControl.dll", + "source": "vendor/pwsh/System.IO.Pipes.AccessControl.dll", + "bytes": 15656, + "sha256": "1FA1F01D59F34DD66398931BBC51A4D1C981CE422FC149E7EEC914991A8588F6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Pipes.dll", + "source": "vendor/pwsh/System.IO.Pipes.dll", + "bytes": 165672, + "sha256": "A1858A82360321B757B41F8F7C33CB793D626A712CC29409E5E8D77493F62062" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Ports.dll", + "source": "vendor/pwsh/System.IO.Ports.dll", + "bytes": 165680, + "sha256": "378B0F569EAF4BA9E6B8DE64186A9CEAEE127FE0BFB13B7D3E832050F0A8D233" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.UnmanagedMemoryStream.dll", + "source": "vendor/pwsh/System.IO.UnmanagedMemoryStream.dll", + "bytes": 15656, + "sha256": "A18AC4B8B1DE6DFC0FA4F9CDA77EF9763E1721A52731B7AD8000BF733FA9FE8E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.AsyncEnumerable.dll", + "source": "vendor/pwsh/System.Linq.AsyncEnumerable.dll", + "bytes": 1292112, + "sha256": "7648125B74F53ABFE7E6F627E4D56D555BCF1AB1A0BFE4912C21969D47110C78" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.dll", + "source": "vendor/pwsh/System.Linq.dll", + "bytes": 698152, + "sha256": "F170005154FE77B1A5AF72179A454CA1D24FF3F805E6DC1A629EF1CF82D3E7AD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.Expressions.dll", + "source": "vendor/pwsh/System.Linq.Expressions.dll", + "bytes": 3639080, + "sha256": "E2627EA364EC3868E20B9F84D2C2A3ECF5FAAD4DC548F53A9D0C6A2D6F9FB282" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.Parallel.dll", + "source": "vendor/pwsh/System.Linq.Parallel.dll", + "bytes": 776016, + "sha256": "75459D9290969BC7E0BE2A352F7E4BC910FAD343F51C01359570CDACE014755D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.Queryable.dll", + "source": "vendor/pwsh/System.Linq.Queryable.dll", + "bytes": 182056, + "sha256": "DFCCADFD6219CE61D1C1263191C919FF2D095395E1B796B82ECF15C486A2B70C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Management.Automation.dll", + "source": "vendor/pwsh/System.Management.Automation.dll", + "bytes": 19658592, + "sha256": "ED8D39663E05435AAB59F1C7FB0AD20292E22B0CB12CF47AAD4B62B31E4D4126" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Management.dll", + "source": "vendor/pwsh/System.Management.dll", + "bytes": 833352, + "sha256": "AD07E0C250FE08CE6B23D63B7A564DA19E128D0A5A7ABA9CE49810ED7752A3DF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Memory.dll", + "source": "vendor/pwsh/System.Memory.dll", + "bytes": 161576, + "sha256": "A714F1A2B4B8D2695BEA4E5D2A85E62621CED26A7E4EBC5FCD61E85EE4838B77" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.dll", + "source": "vendor/pwsh/System.Net.dll", + "bytes": 17192, + "sha256": "D16A0494E92F84355FB5526A4E2F0587AB4F3C296E9971903E17523169E58E7E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Http.dll", + "source": "vendor/pwsh/System.Net.Http.dll", + "bytes": 1746728, + "sha256": "ED96D8A3995CF09E84981DB6EE8A5199E41FC2A2599749F70CF84ADA7D4CCE53" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Http.Json.dll", + "source": "vendor/pwsh/System.Net.Http.Json.dll", + "bytes": 128848, + "sha256": "DC58C398BAEF3AFFB78A9F2440E153D66BD8DC5F78079ECA71AD1BF18C726F19" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Http.WinHttpHandler.dll", + "source": "vendor/pwsh/System.Net.Http.WinHttpHandler.dll", + "bytes": 304960, + "sha256": "EA7676CB7C3FBA9167FD3B4308D422DBEC961282319267F924306744C2BC12F2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.HttpListener.dll", + "source": "vendor/pwsh/System.Net.HttpListener.dll", + "bytes": 534312, + "sha256": "5EF8C9B43FEDE96449D0AE2C99423658C1549121F31A260078E00173A330CAE4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Mail.dll", + "source": "vendor/pwsh/System.Net.Mail.dll", + "bytes": 481064, + "sha256": "B0C2C70BA2035E683E2232699E0547DF7AB4F361DAF4A5418C8F3B45EA147C13" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.NameResolution.dll", + "source": "vendor/pwsh/System.Net.NameResolution.dll", + "bytes": 124712, + "sha256": "EB7AC4D71D4DDD3A05129221F35C4C02926DAC2A9A664EBBC442B1307D6A74E4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.NetworkInformation.dll", + "source": "vendor/pwsh/System.Net.NetworkInformation.dll", + "bytes": 157480, + "sha256": "2A85A3E70D84F237E10A9BEAE7D4B1FDFE05CE0DE135D5CA45B46DE48491CDE3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Ping.dll", + "source": "vendor/pwsh/System.Net.Ping.dll", + "bytes": 91944, + "sha256": "1269783428EFC8C8A430C20ECEE9F931AA40D790B953E1D6C97FAAA71AC660B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Primitives.dll", + "source": "vendor/pwsh/System.Net.Primitives.dll", + "bytes": 210768, + "sha256": "6CA45C7299B6B76A5650F01E63A3290AC50432C6301CEA135C89FFC54AF72E25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Quic.dll", + "source": "vendor/pwsh/System.Net.Quic.dll", + "bytes": 358184, + "sha256": "11F53F8A10F14C789953C82CBEE8BEF8A9F0DDF3F86527125FBC6797E8E3860D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Requests.dll", + "source": "vendor/pwsh/System.Net.Requests.dll", + "bytes": 374568, + "sha256": "663A98F7F7A8082FA47A7197F362041E1479BF0A50D57A00872A2F128BD97121" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Security.dll", + "source": "vendor/pwsh/System.Net.Security.dll", + "bytes": 665424, + "sha256": "2CA204D66FE544FEF6D92E0EA1CDD2DF2F80160E6FBA6D04E3BA80382C69694D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.ServerSentEvents.dll", + "source": "vendor/pwsh/System.Net.ServerSentEvents.dll", + "bytes": 83792, + "sha256": "BF227E04A1D642D83211A4A1136FB31946B8B7148BBA5DDE2310D7ABCBCADE71" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.ServicePoint.dll", + "source": "vendor/pwsh/System.Net.ServicePoint.dll", + "bytes": 15144, + "sha256": "C77F8868279BF5F0D7129FDF5240ABC2527437B8928E8ABF06BC774438416C64" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Sockets.dll", + "source": "vendor/pwsh/System.Net.Sockets.dll", + "bytes": 530216, + "sha256": "B0386EBC2A62CDF0BA4D36973B3347C4372D9BFBCEFABD8E3CDD1B0194753653" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebClient.dll", + "source": "vendor/pwsh/System.Net.WebClient.dll", + "bytes": 165712, + "sha256": "52A615FD624996B513848D1CA1BDA1D02D49991AF0A000909779DDBF35FB17EC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebHeaderCollection.dll", + "source": "vendor/pwsh/System.Net.WebHeaderCollection.dll", + "bytes": 63272, + "sha256": "59E2FF9EE6AC647929BD689486F141645C2412B5B01CBF8A397D2888ADF0E764" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebProxy.dll", + "source": "vendor/pwsh/System.Net.WebProxy.dll", + "bytes": 42832, + "sha256": "41526195B63CD77C37DF1A99F621E2E5A8F40666E9613D974E9935D99F662905" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebSockets.Client.dll", + "source": "vendor/pwsh/System.Net.WebSockets.Client.dll", + "bytes": 104232, + "sha256": "45996C74C3C6C2E144EC006DEB5C70781F2E3C333782B815BB44B612D41EA27E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebSockets.dll", + "source": "vendor/pwsh/System.Net.WebSockets.dll", + "bytes": 243536, + "sha256": "7EF764FC11991DB23A90920DC3D49BBDDC6D2F766529D19463B17EAEE720CA71" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Numerics.dll", + "source": "vendor/pwsh/System.Numerics.dll", + "bytes": 15144, + "sha256": "49351764F0118538F1E531D91F416F325F362F6638C55896AF3D0FF631296B05" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Numerics.Vectors.dll", + "source": "vendor/pwsh/System.Numerics.Vectors.dll", + "bytes": 15656, + "sha256": "F7D2DBA3D30B09FA8CA802CDA5D0EE0827CB3AA44E1E19F790F62C390DDF5DE8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ObjectModel.dll", + "source": "vendor/pwsh/System.ObjectModel.dll", + "bytes": 79656, + "sha256": "9A7D09EF51C648C4CE51A0E21CDC64DEDE904420CCEC224D62C09FCEEE6B150A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Printing.dll", + "source": "vendor/pwsh/System.Printing.dll", + "bytes": 968488, + "sha256": "1CB55A01A300A2EBC0C8E14BD498005F4B34A0F4FC5F5CF7A88D50EC8AB8DCE6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.CoreLib.dll", + "source": "vendor/pwsh/System.Private.CoreLib.dll", + "bytes": 16033616, + "sha256": "63D83DD8169F23820F5D3BF0F0399F60E5E26200361787DFCB1C24CABC76E423" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.DataContractSerialization.dll", + "source": "vendor/pwsh/System.Private.DataContractSerialization.dll", + "bytes": 2062120, + "sha256": "A34745F8431BA8D38EAC37A65BCBD644C0F50AB88AFD66AF236A9A49C693B9F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Uri.dll", + "source": "vendor/pwsh/System.Private.Uri.dll", + "bytes": 255784, + "sha256": "D14188A1352F793F862726FA2C956DC270FB0E2578D46C6561F682FF2B4E110E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Windows.Core.dll", + "source": "vendor/pwsh/System.Private.Windows.Core.dll", + "bytes": 1906472, + "sha256": "0341B9E7466D143656E804D694A3C0B0BDB040FD44DE709047873661EC638884" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Windows.GdiPlus.dll", + "source": "vendor/pwsh/System.Private.Windows.GdiPlus.dll", + "bytes": 427816, + "sha256": "4ED46AD143DB84BDFA38C0973F1525296264FCCCA14AAB7D8C177982390C8260" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Xml.dll", + "source": "vendor/pwsh/System.Private.Xml.dll", + "bytes": 7788328, + "sha256": "65F1252BAD1A18D985A55D6434608E4A66DBA8A426310B2C6FDAE12B09E3FAF6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Xml.Linq.dll", + "source": "vendor/pwsh/System.Private.Xml.Linq.dll", + "bytes": 390992, + "sha256": "8A82630530FB3AD2E29A3D755BCE1F50497EC764804DDC7B014428A88E6DF16C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Context.dll", + "source": "vendor/pwsh/System.Reflection.Context.dll", + "bytes": 190256, + "sha256": "B7B226B5E08F84B6D6DDF77EED92A112ECA3D61087EF689F5BCCEB8996A736AE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.DispatchProxy.dll", + "source": "vendor/pwsh/System.Reflection.DispatchProxy.dll", + "bytes": 75560, + "sha256": "827499A045D2E71731BEB0391D392436D911E9D3195663E14F3CCA0F6B13691A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.dll", + "source": "vendor/pwsh/System.Reflection.dll", + "bytes": 16168, + "sha256": "2D0A26F77691CAC4D6522D9212FD70116DC183E1035B3AE4E6087C1EB9255F89" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Emit.dll", + "source": "vendor/pwsh/System.Reflection.Emit.dll", + "bytes": 309032, + "sha256": "F3CAEDCFF578946F80ECE81947A5ABB5D905BF1567D77D05C0725E0FF1FA1782" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Emit.ILGeneration.dll", + "source": "vendor/pwsh/System.Reflection.Emit.ILGeneration.dll", + "bytes": 15696, + "sha256": "9D003F3B5DF43C3062FA1E363E5F6B3ECC5D4B84213FC7397DA53E583494F390" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Emit.Lightweight.dll", + "source": "vendor/pwsh/System.Reflection.Emit.Lightweight.dll", + "bytes": 15656, + "sha256": "0461AC768E7F2E3BCD20A016C1E06E207092DF250458C868FF628C2E7766B748" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Extensions.dll", + "source": "vendor/pwsh/System.Reflection.Extensions.dll", + "bytes": 15144, + "sha256": "28B889CEA13DCE1ECFECF62CF887C1C9587255596333680C8441120E4EDA25C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Metadata.dll", + "source": "vendor/pwsh/System.Reflection.Metadata.dll", + "bytes": 1156904, + "sha256": "B453AC5B26229328268803037B95EBDFD9EA3C7AABD830513BD6A39CF73F7076" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Primitives.dll", + "source": "vendor/pwsh/System.Reflection.Primitives.dll", + "bytes": 15656, + "sha256": "B20C388943B74385F712CDAF74CDE691F6EEE14CB5320D8136C3D40B57C02D42" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.TypeExtensions.dll", + "source": "vendor/pwsh/System.Reflection.TypeExtensions.dll", + "bytes": 42832, + "sha256": "7CEDCBAB51CACAEEECA6A766F92A2B93FD74CA88D326BADFAD090067D9D19150" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.Extensions.dll", + "source": "vendor/pwsh/System.Resources.Extensions.dll", + "bytes": 194344, + "sha256": "CFBD1384747751A7BA4CE8A16F191501CDB762567029FF57F6E768AD4D4C58FD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.Reader.dll", + "source": "vendor/pwsh/System.Resources.Reader.dll", + "bytes": 15144, + "sha256": "3E83796FDDDA54188EBE691C4B45641BB2FE7780F8E4D1928D5F4B435BB6C6F8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.ResourceManager.dll", + "source": "vendor/pwsh/System.Resources.ResourceManager.dll", + "bytes": 15656, + "sha256": "DA9AD363EE8A17881D33768D75A3D9689605C626415AF780D0FF5BA743C3A3F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.Writer.dll", + "source": "vendor/pwsh/System.Resources.Writer.dll", + "bytes": 50984, + "sha256": "E089809FA22829491D90FB8BCBA9210EE7F37A7C9D07D0FA2E333C15016B87A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Caching.dll", + "source": "vendor/pwsh/System.Runtime.Caching.dll", + "bytes": 202560, + "sha256": "DB5D7363FFAF4616FD9FF0225DEFFE1644418D792AED29C90E63924A852A6838" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.CompilerServices.Unsafe.dll", + "source": "vendor/pwsh/System.Runtime.CompilerServices.Unsafe.dll", + "bytes": 15144, + "sha256": "3D00BF59EE665068C013BBF7258A0BF46A86CDACEC9FC5BD727ABFF9D190D3E5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.CompilerServices.VisualC.dll", + "source": "vendor/pwsh/System.Runtime.CompilerServices.VisualC.dll", + "bytes": 30504, + "sha256": "84820BF55EDAEEBCFC2F8AA5ED64ECDCB8EDA8A6ED1D44AA9DFD28C8AE362FD1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.dll", + "source": "vendor/pwsh/System.Runtime.dll", + "bytes": 44840, + "sha256": "2C733C433C3D26C0EDB1EB89C9ED7A53CB3194092D1A56A79BF14AB71F4A3425" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Extensions.dll", + "source": "vendor/pwsh/System.Runtime.Extensions.dll", + "bytes": 17704, + "sha256": "71CAB63C02F634D86377BB06C481E733347ABC22B56106B39228A1A01305DE10" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Handles.dll", + "source": "vendor/pwsh/System.Runtime.Handles.dll", + "bytes": 15656, + "sha256": "784E5ADCC91FB6C3154C3F4E9C7C4E311892A2182686205DC4C2B3118C8355BA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.InteropServices.dll", + "source": "vendor/pwsh/System.Runtime.InteropServices.dll", + "bytes": 112464, + "sha256": "F3587B04D04F05C5A22E129EBE3DABE34243315E035AB6BBD91A4A3350FBAD18" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.InteropServices.JavaScript.dll", + "source": "vendor/pwsh/System.Runtime.InteropServices.JavaScript.dll", + "bytes": 50984, + "sha256": "A865BF2368D23E2328C68DAC87B136E71E00BA8136A0CF4B7EC10F71CC3525A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", + "source": "vendor/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", + "bytes": 15656, + "sha256": "4E38FBC3D52D0EA22A13D8569D24F40C3C6043DAA28DDF242CF77288FBBE2DEA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Intrinsics.dll", + "source": "vendor/pwsh/System.Runtime.Intrinsics.dll", + "bytes": 17192, + "sha256": "58A88BA0B94E29F26C3846697A6A251FE7677CBCC7C2B8950090D4AF59109720" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Loader.dll", + "source": "vendor/pwsh/System.Runtime.Loader.dll", + "bytes": 15656, + "sha256": "B3A9500ED9E7F4A698FB46513B8C73B5AA752331597A544E2722B2C5DB262B09" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Numerics.dll", + "source": "vendor/pwsh/System.Runtime.Numerics.dll", + "bytes": 354088, + "sha256": "9F067C312E0C0C097D56BB3487933DCDA758D547480AB76E5A2987AEADD8FD7E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.dll", + "bytes": 17192, + "sha256": "CA3289757E8F3BC2BCCBC0B02205CD0F7A21AD9845417FB9496A7C9FA4117DF5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Formatters.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Formatters.dll", + "bytes": 124712, + "sha256": "7832E9DCC1A36276B2DF00427C5C926F1A48DFF2D03977632A83AD138D853013" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Json.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Json.dll", + "bytes": 15656, + "sha256": "3E23CF2C3DABB7F3D58A88838884FFA68E0A73E40EB0AFCB316BA29DDD567A38" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Primitives.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Primitives.dll", + "bytes": 38736, + "sha256": "1C2DED997862CD07FD93BD8450A98517875F7BB52A4C4E651A4D3B8832D7DB9C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Xml.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Xml.dll", + "bytes": 16680, + "sha256": "6E9DFAA42EEF9F406696E5D2372C100143BD47E0E95EC21158FE5FA7DD2EE428" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.AccessControl.dll", + "source": "vendor/pwsh/System.Security.AccessControl.dll", + "bytes": 227112, + "sha256": "AA6BF70A513CB43DAAD41F91BDE35740EC28456481639A57508BE02B08F1969B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Claims.dll", + "source": "vendor/pwsh/System.Security.Claims.dll", + "bytes": 100136, + "sha256": "6AE287447243764B203F58EABBC8E5B9EBCA135F6174AD73DD06AE572936DCC4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Algorithms.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Algorithms.dll", + "bytes": 17192, + "sha256": "D679F5CAF8BCF0007E4003D5CE728EF3DE03EB88F5DC90528AEEE1D266436318" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Cng.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Cng.dll", + "bytes": 16168, + "sha256": "E6D327C11059988B2C12584040C588CAEC73334E9745FFE85C3522BC85756F49" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Csp.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Csp.dll", + "bytes": 16168, + "sha256": "629D0E04F3605F8CABCB04105EB7CA369D3AF023FBE171CBACB60476395509A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.dll", + "source": "vendor/pwsh/System.Security.Cryptography.dll", + "bytes": 2549584, + "sha256": "ACB3E95912A1B3D2CC96BFD42346328462A930AC77858DFC62D7D712CEED4F5E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Encoding.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Encoding.dll", + "bytes": 15656, + "sha256": "7D824AC50A0D6A75B8F85BA007BD84BEC6714CCFFDEBEAD00A01EDB9CCD3918E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.OpenSsl.dll", + "source": "vendor/pwsh/System.Security.Cryptography.OpenSsl.dll", + "bytes": 15656, + "sha256": "4F6747F232BF9C3AC7B5352D818E31131DCC299503806A8190E9FBEF95D4A50A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Pkcs.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Pkcs.dll", + "bytes": 767784, + "sha256": "AC3A317E89EF612F3DC6169887C8A1B0A692121E1FF48D402F75F4A82DA4899A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Primitives.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Primitives.dll", + "bytes": 15656, + "sha256": "E17D719732B43BA6BEB751A7EF55390B9E9B9B659826FABF1288CD03AF0241C2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.ProtectedData.dll", + "source": "vendor/pwsh/System.Security.Cryptography.ProtectedData.dll", + "bytes": 59216, + "sha256": "1E5826CE27C48902506553C8BC7C648004A2767ED786979538D725BA2561E2D9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.X509Certificates.dll", + "source": "vendor/pwsh/System.Security.Cryptography.X509Certificates.dll", + "bytes": 16680, + "sha256": "752EB010626928B51584B2B58D7C67110D16E73B9ACE071ADC6009F33275636C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Xml.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Xml.dll", + "bytes": 452392, + "sha256": "FF3E018DA84A0294FDD2A4F8A120CBC05B40E99CE4AD57CE45809AB3280C6DEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.dll", + "source": "vendor/pwsh/System.Security.dll", + "bytes": 18216, + "sha256": "4C5D662D946BA84F5159FAA8197661AAD054B98899977F5BFC3C4F7A66AC13FC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Permissions.dll", + "source": "vendor/pwsh/System.Security.Permissions.dll", + "bytes": 190248, + "sha256": "37D4C12B1BAC234541871123511525B680089A61758CE6F28B09A18F8F0B39E6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Principal.dll", + "source": "vendor/pwsh/System.Security.Principal.dll", + "bytes": 15144, + "sha256": "40671DC07A55EC29B00DA6D45610CB663A8C03AE3A686695A9E28412AB87ACF5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Principal.Windows.dll", + "source": "vendor/pwsh/System.Security.Principal.Windows.dll", + "bytes": 177960, + "sha256": "4C6EE4BC691D111FF4500A6D455DEEEEB3F56F14F5CD686F404BA063C8D2741B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.SecureString.dll", + "source": "vendor/pwsh/System.Security.SecureString.dll", + "bytes": 15656, + "sha256": "83941FEE8EB17E7215855D7D655243DC5D1D5ED631C92C39F4163D4A491AEBE8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.dll", + "source": "vendor/pwsh/System.ServiceModel.dll", + "bytes": 25160, + "sha256": "CF390C8379F3249937DA11DAD3A5BA88804A7453503A246BE01FC849B899653B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Duplex.dll", + "source": "vendor/pwsh/System.ServiceModel.Duplex.dll", + "bytes": 15432, + "sha256": "5E7D8985922C7D7D673D2D204C2F6D12E3F70F70D0E9FF15507FECD2026A8815" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Http.dll", + "source": "vendor/pwsh/System.ServiceModel.Http.dll", + "bytes": 481096, + "sha256": "4B4F5C934A4CBAD1227507570697BC75F7E332A7A5D85EC62F9A24E5897E7B32" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.NetFramingBase.dll", + "source": "vendor/pwsh/System.ServiceModel.NetFramingBase.dll", + "bytes": 477000, + "sha256": "98175B9ABD29326B1C410AA6ED82370677F1781A5A26B7A9AB8479C76A4A6EF2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.NetTcp.dll", + "source": "vendor/pwsh/System.ServiceModel.NetTcp.dll", + "bytes": 137032, + "sha256": "8169433D0DF8D3186ABD4C94CCFF3987CA267D56F5A384CF539E613C997FC8FA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Primitives.dll", + "source": "vendor/pwsh/System.ServiceModel.Primitives.dll", + "bytes": 5388112, + "sha256": "C53A7289B255E6A32E42FEB63CE24E32C2E0FAF4B5972EC2EC83BB17C4F01EF9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Security.dll", + "source": "vendor/pwsh/System.ServiceModel.Security.dll", + "bytes": 16928, + "sha256": "99EC51765CF53B36DC1064A48DB4C910EDB17C92E176E03B947E5E200B82F7D2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Syndication.dll", + "source": "vendor/pwsh/System.ServiceModel.Syndication.dll", + "bytes": 313152, + "sha256": "CF572334CFBB7791716282B0802DD2A33691B55B13FE3F65A45058E57700EECA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Web.dll", + "source": "vendor/pwsh/System.ServiceModel.Web.dll", + "bytes": 16680, + "sha256": "58A681BB145F2CDC9E996B48E40FFFAEA30DE469A535DDB985454A4AF8828D50" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceProcess.dll", + "source": "vendor/pwsh/System.ServiceProcess.dll", + "bytes": 15696, + "sha256": "203F52B3F352E0E3DF54B9F588131743EB562AF8612BFA2C2DEBAB21B29110FA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceProcess.ServiceController.dll", + "source": "vendor/pwsh/System.ServiceProcess.ServiceController.dll", + "bytes": 149336, + "sha256": "2CEC344EF1D23339A5257FA8E9B96D4842B1E15AFD04D1A56261B1990A0F9B7C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Speech.dll", + "source": "vendor/pwsh/System.Speech.dll", + "bytes": 1386304, + "sha256": "1A06AB169EACB6FB9CDF76096284284236F6C55A3F588CAA24BD7AF06A8F4A10" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encoding.CodePages.dll", + "source": "vendor/pwsh/System.Text.Encoding.CodePages.dll", + "bytes": 861992, + "sha256": "9D8D9E0FA33437502FD659FCB726E295CF439E56BDB044970FE5B715D7813E37" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encoding.dll", + "source": "vendor/pwsh/System.Text.Encoding.dll", + "bytes": 15656, + "sha256": "1E717906C9F94DAA686B68E9E86F4A1C08916DB62B05AF5A17661A5B83B9C7A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encoding.Extensions.dll", + "source": "vendor/pwsh/System.Text.Encoding.Extensions.dll", + "bytes": 15656, + "sha256": "F507CC18B0449947A86606DFE9281B89316B39C1BA60A9249E41C0A1CD30E201" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encodings.Web.dll", + "source": "vendor/pwsh/System.Text.Encodings.Web.dll", + "bytes": 124712, + "sha256": "AB138D71B868D606ACD1D48EF17B9F12E51F75ECD297120ADD0C4552F6758160" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Json.dll", + "source": "vendor/pwsh/System.Text.Json.dll", + "bytes": 1890088, + "sha256": "ABCECB266DFD7AF409EBA45CD2FD581D40F72D5FE916A1A95F05B32AC738968F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.RegularExpressions.dll", + "source": "vendor/pwsh/System.Text.RegularExpressions.dll", + "bytes": 1034024, + "sha256": "CC5C01E9C1591DF68708CA6386C1259B0AC34257336CF53106CE784F9D1175C0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.AccessControl.dll", + "source": "vendor/pwsh/System.Threading.AccessControl.dll", + "bytes": 75600, + "sha256": "78A2D7FD94410ABEFEFC9AF09FADB9FBDC7069F5572949C62E81423DC9F8A33B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Channels.dll", + "source": "vendor/pwsh/System.Threading.Channels.dll", + "bytes": 157480, + "sha256": "104051A542F04760C2C89998A49396E0F4FCE403E86D7D976984280BF835C966" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.dll", + "source": "vendor/pwsh/System.Threading.dll", + "bytes": 83792, + "sha256": "B1AAE26002952704D7D7913170DFD3C786DE6DEA50463BB671A9AA99423320BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Overlapped.dll", + "source": "vendor/pwsh/System.Threading.Overlapped.dll", + "bytes": 15656, + "sha256": "239A90D22546A4ED01A05BFA8CEBA846E4C73F73BA6C2B3E06E176F859890DB5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.Dataflow.dll", + "source": "vendor/pwsh/System.Threading.Tasks.Dataflow.dll", + "bytes": 472872, + "sha256": "1412A7614F9FAFFE37E102384B0917B436BA3CAC685F8ED64607912C805DB9E9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.dll", + "source": "vendor/pwsh/System.Threading.Tasks.dll", + "bytes": 16680, + "sha256": "3C3CBA2C78819A31B400B52014678A4AF1DC1F482B2424E38B85E28EEF1C40D0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.Extensions.dll", + "source": "vendor/pwsh/System.Threading.Tasks.Extensions.dll", + "bytes": 15656, + "sha256": "DAAC261AD95033C320720BE3AE53263ABD6344FD548BA36F405D2D5AB39207F2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.Parallel.dll", + "source": "vendor/pwsh/System.Threading.Tasks.Parallel.dll", + "bytes": 132904, + "sha256": "47733F454E9D7DA1BEC529A36AE86DB431E8A89AD1FE84641E80C9207D91F4C7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Thread.dll", + "source": "vendor/pwsh/System.Threading.Thread.dll", + "bytes": 15656, + "sha256": "0B016736E7E4995A7E0A127A7BAEB6F8487A97433E2DD034B8CC8DD5FA1048AC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.ThreadPool.dll", + "source": "vendor/pwsh/System.Threading.ThreadPool.dll", + "bytes": 15656, + "sha256": "104C0F37199430CCEBF3B81EB28E4C072547447075EACFF8D488954035BD231F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Timer.dll", + "source": "vendor/pwsh/System.Threading.Timer.dll", + "bytes": 15144, + "sha256": "632C125112639BD454C6E07DC2481E9EEC8D9FC300FA0EA6DF622729DE499A77" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Transactions.dll", + "source": "vendor/pwsh/System.Transactions.dll", + "bytes": 16680, + "sha256": "0F55A3E2F9BAA40E596E93C76D97C0F5B4A53775D8865F57877E77FB90B8E09D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Transactions.Local.dll", + "source": "vendor/pwsh/System.Transactions.Local.dll", + "bytes": 640808, + "sha256": "B7CE5D6C73B6E2A298946474473C6E12AC2A81584EFAC155F85ACAD4CD1695E0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ValueTuple.dll", + "source": "vendor/pwsh/System.ValueTuple.dll", + "bytes": 15696, + "sha256": "1C02D7D7BEF69434A1F9EA0BFAF4A83321E644A5478D39681849883BC1114A61" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Web.dll", + "source": "vendor/pwsh/System.Web.dll", + "bytes": 15144, + "sha256": "88650D1871B5BAC043665F06610799862B66303794FB76DE0DB5E58C86E1B1D9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Web.HttpUtility.dll", + "source": "vendor/pwsh/System.Web.HttpUtility.dll", + "bytes": 63312, + "sha256": "1CFF49AD07732F67D54CFDF00100692499B24F00955DED4DB528012F5F11A9A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Web.Services.Description.dll", + "source": "vendor/pwsh/System.Web.Services.Description.dll", + "bytes": 800576, + "sha256": "AB6BFC2802083328D63EA2A520ED6F5DCF2AD2E0D67276A3884D577A2C13EC7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Controls.Ribbon.dll", + "source": "vendor/pwsh/System.Windows.Controls.Ribbon.dll", + "bytes": 1439528, + "sha256": "E7E47AF351ED12E58337B7877F6E1C53A0E652D0D42DEF2CA1F61E81F0C93EA1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.dll", + "source": "vendor/pwsh/System.Windows.dll", + "bytes": 15656, + "sha256": "8A97C2FC1A57682ED6031CB27A2D0B1BD352EEE9A4AF53C4411F8C0304DEE2FE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Extensions.dll", + "source": "vendor/pwsh/System.Windows.Extensions.dll", + "bytes": 120616, + "sha256": "EB33D2AF1F7F76F7C179856025981A1F30766D3F7CE9F61D3A09DD22B59553E2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.Design.dll", + "source": "vendor/pwsh/System.Windows.Forms.Design.dll", + "bytes": 6092584, + "sha256": "DE308D2141B4EA269DAB0E30510116646F625A16A6493B13218E7428DBEF08D7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.Design.Editors.dll", + "source": "vendor/pwsh/System.Windows.Forms.Design.Editors.dll", + "bytes": 16168, + "sha256": "B5B8172A40E5E72A54C6B8C4BB3C926CA4EE6248D5BEBE511B060F0D6D053003" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.dll", + "source": "vendor/pwsh/System.Windows.Forms.dll", + "bytes": 13715240, + "sha256": "65D1A845A47F955CD9E4C893E9FDCAE255F63894F45BC0B721F5DE22F223C753" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.Primitives.dll", + "source": "vendor/pwsh/System.Windows.Forms.Primitives.dll", + "bytes": 3622736, + "sha256": "215C6626C23DC962B32A42B218ABD653D78EA09E3D1162C65B0C3BB836A96D6C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Input.Manipulations.dll", + "source": "vendor/pwsh/System.Windows.Input.Manipulations.dll", + "bytes": 141096, + "sha256": "B51133A16A9EA33269D1AA8A9F9CB7171FFC339D251066903D17209FAAA7360F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Presentation.dll", + "source": "vendor/pwsh/System.Windows.Presentation.dll", + "bytes": 30504, + "sha256": "9C752F71E3C79BEBF747A4BE28FA791404C30EE7486893B4D73CD3EF3DDD4654" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Primitives.dll", + "source": "vendor/pwsh/System.Windows.Primitives.dll", + "bytes": 386856, + "sha256": "7FD4DC6CF1C20115ADB7EC2519A4A2BE1805E2C2A6133E32BCF52B598020E152" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xaml.dll", + "source": "vendor/pwsh/System.Xaml.dll", + "bytes": 1390416, + "sha256": "DF6837BCC37DF3E0AC022A38F545BCF6155A57A55ADE8E4500D481A5F48A3544" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.dll", + "source": "vendor/pwsh/System.Xml.dll", + "bytes": 23336, + "sha256": "77C887B9C9D608538C78607D752E3567B090DD8BD8D41CAADE2201D34A1BE8BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.Linq.dll", + "source": "vendor/pwsh/System.Xml.Linq.dll", + "bytes": 15656, + "sha256": "A3581BC00766E7A78974009B2FAFB83B0D0158C3A1619AF11E9389981DE1DF1A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.ReaderWriter.dll", + "source": "vendor/pwsh/System.Xml.ReaderWriter.dll", + "bytes": 21800, + "sha256": "4E8265FE5D68F42648394F0D6CA0C06C60664F5E0C7456BD65C942C664891BD0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.Serialization.dll", + "source": "vendor/pwsh/System.Xml.Serialization.dll", + "bytes": 16168, + "sha256": "66AC0372545EDB9C426E8E884292F81B26E6E185E7A90729695497F1FDCE31BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XDocument.dll", + "source": "vendor/pwsh/System.Xml.XDocument.dll", + "bytes": 15656, + "sha256": "7E9217B005D31B73E5CD83BC6DA85217677A773020AC736C67D53E6375FF5908" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XmlDocument.dll", + "source": "vendor/pwsh/System.Xml.XmlDocument.dll", + "bytes": 15656, + "sha256": "E604D3C3D891756E18817637E3323C9E67F4DAFB404C877CE3332E598669940C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XmlSerializer.dll", + "source": "vendor/pwsh/System.Xml.XmlSerializer.dll", + "bytes": 17704, + "sha256": "AC7EFC90B0E73146CFA23A91D97E7BB05E1C3CF02416A7EE5853EB39149AE985" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XPath.dll", + "source": "vendor/pwsh/System.Xml.XPath.dll", + "bytes": 15696, + "sha256": "3E3D6A10185F44C1B78686E08DE8FD59EDF5BA4D953C2FFA4B277B369792DD71" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XPath.XDocument.dll", + "source": "vendor/pwsh/System.Xml.XPath.XDocument.dll", + "bytes": 30504, + "sha256": "EF4F101B820CD5BA369C6FE06729BDC7E2E2DD023D720058B546DB32227FB500" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ThirdPartyNotices.txt", + "source": "vendor/pwsh/ThirdPartyNotices.txt", + "bytes": 210587, + "sha256": "5A2D792AA9DAE45DA7579398CA08C0FBB47D78C563CCE492E311B9D5EB9A2142" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationClient.dll", + "source": "vendor/pwsh/UIAutomationClient.dll", + "bytes": 407336, + "sha256": "6074F520F4630DC6DAF4E1CD2F714E4123C4CFCDAD909C76ECC7CF69026C719F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationClientSideProviders.dll", + "source": "vendor/pwsh/UIAutomationClientSideProviders.dll", + "bytes": 857896, + "sha256": "1C287748EB1C5B5BE118FFFD6E9254A67D959A4EA04EF2EE245236CBD6C015C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationProvider.dll", + "source": "vendor/pwsh/UIAutomationProvider.dll", + "bytes": 63272, + "sha256": "3A9A965E8759EE679612C3A59BAFE70DFBADA3CE1260D815B1D2D62F941105B2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationTypes.dll", + "source": "vendor/pwsh/UIAutomationTypes.dll", + "bytes": 296744, + "sha256": "9A26003F55DAC193DAEED86CA35BC01B72DBBEDBFDFF9846E150CEEF80D3D70A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/vcruntime140_cor3.dll", + "source": "vendor/pwsh/vcruntime140_cor3.dll", + "bytes": 134600, + "sha256": "23AB6BFC1D127318005279A4608401F5C3FB872B5BD32A706863E8D0C2C5065F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/WindowsBase.dll", + "source": "vendor/pwsh/WindowsBase.dll", + "bytes": 2176808, + "sha256": "ABBEA4B64EFF6A29767E64F99E2574A827B43C383C97145DC9FB316964A8DFB8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/WindowsFormsIntegration.dll", + "source": "vendor/pwsh/WindowsFormsIntegration.dll", + "bytes": 214864, + "sha256": "0C2C08B731513110D237865AB84E16A22BA05073F6301EA28E894BA343E99415" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/wpfgfx_cor3.dll", + "source": "vendor/pwsh/wpfgfx_cor3.dll", + "bytes": 1956136, + "sha256": "38FFCC5A72DEDFC518D03CCC9B8F5A20D5C8EC84578B42BC30771D79955C2E11" + } + ] +} diff --git a/windows/service/DirectInternetMethod.Service.csproj b/windows/service/DirectInternetMethod.Service.csproj index 845ad05d..67d2b139 100644 --- a/windows/service/DirectInternetMethod.Service.csproj +++ b/windows/service/DirectInternetMethod.Service.csproj @@ -1,15 +1,15 @@ - - - WinExe - net10.0-windows - enable - enable - DirectInternetMethod.Service - DirectInternetMethod.Service - ..\app\DirectInternetMethod.ico - 1.5.4 - 1.5.4.0 - 1.5.4.0 - Direct Internet Method Service - + + + WinExe + net10.0-windows + enable + enable + DirectInternetMethod.Service + DirectInternetMethod.Service + ..\app\DirectInternetMethod.ico + 1.5.5-rc1 + 1.5.5.0 + 1.5.5.0 + Direct Internet Method Service + \ No newline at end of file From 6a84171ac8f5a1b05945e5ff092537c4d722d620 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:53:15 +0330 Subject: [PATCH 010/188] fix(ci): store WPF source blob as Git-canonical LF for CRLF checkout (R535) The repository enforces text/eol=crlf for MainWindow.xaml.cs. The previous GitHub API blob inadvertently stored CRLF directly, making a pristine Windows runner checkout appear dirty. Normalize the indexed blob to LF; checkout converts to CRLF. No code logic or released artifacts changed. --- windows/gui/MainWindow.xaml.cs | 1384 ++++++++++++++++---------------- 1 file changed, 692 insertions(+), 692 deletions(-) diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index 32b67303..b62ba164 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -1,692 +1,692 @@ -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; - -namespace DirectInternetMethod; - -public partial class MainWindow : Window -{ - readonly string appDir = AppContext.BaseDirectory; - readonly string statePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectDnsDpiHarness", "state.json"); - readonly string actionStatusPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "action-status.json"); - readonly string bundledPwshPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "DirectInternetMethod", "Privileged", "runtime", "pwsh", "pwsh.exe"); - readonly Brush ok = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#68E8A6")); - readonly Brush warn = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFD166")); - readonly Brush bad = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF7A85")); - readonly Brush neutral = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9FB1C3")); - readonly string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DirectInternetMethod", "settings.json"); - readonly string customHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "custom-hosts.txt"); - readonly string adultEnabledPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-enabled.txt"); - readonly string adultHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.txt"); - readonly string adultMetaPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.meta.json"); - static readonly string[] AdultCatalogUrls = - { - "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/wildcard/nsfw-onlydomains.txt", - "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/nsfw-onlydomains.txt" - }; - static readonly Regex domainLabel = new("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", RegexOptions.CultureInvariant | RegexOptions.Compiled); - readonly string strategyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "strategy.txt"); - readonly string scopePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "scope.txt"); - UpdateInfo? availableUpdate; - bool adultCheckEnabled; - - public MainWindow() - { - InitializeComponent(); - adultCheckEnabled = LoadAdultSiteCheckSetting(); - AdultSiteCheckToggle.IsChecked = adultCheckEnabled; - AdultSiteCheckToggle.Checked += AdultSiteCheckToggle_Changed; - AdultSiteCheckToggle.Unchecked += AdultSiteCheckToggle_Changed; - Loaded += async (_, _) => - { - if (adultCheckEnabled) - { - try - { - SaveAdultSiteCheckSetting(); - await EnsureAdultCatalogAsync(false); - } - catch (Exception ex) - { - AdultSiteText.Text = "Coverage fallback active · catalog sync failed: " + ex.Message; - } - } - await RefreshStatusAsync(); - _ = CheckForUpdatesAsync(false); - }; - } - - Version CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 1, 0, 0); - - bool LoadAdultSiteCheckSetting() - { - try - { - if (File.Exists(adultEnabledPath)) - return File.ReadAllText(adultEnabledPath).Trim() == "1"; - if (!File.Exists(settingsPath)) return false; - using var doc = JsonDocument.Parse(File.ReadAllText(settingsPath)); - if (doc.RootElement.TryGetProperty("adultCoverageEnabled", out var coverage)) - return coverage.ValueKind == JsonValueKind.True; - return doc.RootElement.TryGetProperty("adultSiteLiveCheck", out var legacy) && legacy.ValueKind == JsonValueKind.True; - } - catch { return false; } - } - - void SaveAdultSiteCheckSetting() - { - var settingsDir = Path.GetDirectoryName(settingsPath)!; - Directory.CreateDirectory(settingsDir); - var settingsTemp = settingsPath + ".tmp"; - File.WriteAllText(settingsTemp, JsonSerializer.Serialize(new { adultCoverageEnabled = adultCheckEnabled, adultSiteLiveCheck = adultCheckEnabled })); - File.Move(settingsTemp, settingsPath, true); - - var configDir = Path.GetDirectoryName(adultEnabledPath)!; - Directory.CreateDirectory(configDir); - var enabledTemp = adultEnabledPath + ".tmp"; - File.WriteAllText(enabledTemp, adultCheckEnabled ? "1\r\n" : "0\r\n", new UTF8Encoding(false)); - File.Move(enabledTemp, adultEnabledPath, true); - if (!adultCheckEnabled) - { - try { if (File.Exists(adultHostsPath)) File.Delete(adultHostsPath); } catch { } - try { if (File.Exists(adultMetaPath)) File.Delete(adultMetaPath); } catch { } - } - } - - sealed record AdultCatalogInfo(int Count, string Sha256); - - static List ValidateAdultCatalog(string text) - { - var result = new List(); - var seen = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var raw in text.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None)) - { - var v = raw.Trim().ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(v) || v.StartsWith('#')) continue; - if (v.StartsWith('^')) v = v[1..]; - if (v.Length is < 3 or > 253 || !v.Contains('.')) continue; - if (v.Split('.').Any(x => !domainLabel.IsMatch(x))) continue; - if (seen.Add(v)) result.Add(v); - if (result.Count > 100000) throw new InvalidDataException("Adult catalog exceeds 100,000 validated domains."); - } - if (result.Count < 10000) throw new InvalidDataException("Adult catalog is unexpectedly small."); - foreach (var required in new[] { "xvideos.com", "xnxx.com", "xhamster.com", "pornhub.com", "redtube.com" }) - if (!seen.Contains(required)) throw new InvalidDataException("Adult catalog is missing a required coverage family."); - return result; - } - - async Task EnsureAdultCatalogAsync(bool force) - { - if (!force && File.Exists(adultHostsPath) && File.GetLastWriteTimeUtc(adultHostsPath) > DateTime.UtcNow.AddHours(-24)) - { - var existing = ValidateAdultCatalog(await File.ReadAllTextAsync(adultHostsPath)); - return new AdultCatalogInfo(existing.Count, Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(adultHostsPath)))); - } - - using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(20) }; - http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.5-rc1"); - Exception? lastError = null; - foreach (var sourceUrl in AdultCatalogUrls) - { - try - { - var bytes = await http.GetByteArrayAsync(sourceUrl); - if (bytes.Length is < 100000 or > 4 * 1024 * 1024) throw new InvalidDataException("Adult catalog size is outside the accepted range."); - var raw = Encoding.UTF8.GetString(bytes); - var domains = ValidateAdultCatalog(raw); - var dir = Path.GetDirectoryName(adultHostsPath)!; - Directory.CreateDirectory(dir); - var tmp = adultHostsPath + ".tmp"; - await File.WriteAllTextAsync(tmp, string.Join(Environment.NewLine, domains) + Environment.NewLine, new UTF8Encoding(false)); - File.Move(tmp, adultHostsPath, true); - var digest = Convert.ToHexString(SHA256.HashData(bytes)); - var metaTmp = adultMetaPath + ".tmp"; - await File.WriteAllTextAsync(metaTmp, JsonSerializer.Serialize(new - { - schema = 1, - source = sourceUrl, - fetchedUtc = DateTime.UtcNow.ToString("O"), - sourceSha256 = digest, - entries = domains.Count - }), new UTF8Encoding(false)); - File.Move(metaTmp, adultMetaPath, true); - return new AdultCatalogInfo(domains.Count, digest); - } - catch (Exception ex) - { - lastError = ex; - } - } - throw new InvalidOperationException("Adult catalog sync failed from all official upstream endpoints.", lastError); - } - - async Task RunPwshCaptureAsync(string script, string args = "") - { - if (!File.Exists(bundledPwshPath)) - throw new FileNotFoundException("Bundled PowerShell runtime is missing.", bundledPwshPath); - var psi = new ProcessStartInfo(bundledPwshPath, - $"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"{Path.Combine(appDir, script)}\" {args}") - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - WorkingDirectory = appDir - }; - using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start PowerShell 7."); - var output = await p.StandardOutput.ReadToEndAsync(); - var error = await p.StandardError.ReadToEndAsync(); - await p.WaitForExitAsync(); - if (p.ExitCode != 0) throw new InvalidOperationException(string.IsNullOrWhiteSpace(error) ? $"Status backend exited {p.ExitCode}." : error.Trim()); - return output; - } - - static string ReadStringList(JsonElement root, string property) - { - if (!root.TryGetProperty(property, out var v)) return "—"; - if (v.ValueKind == JsonValueKind.String) return v.GetString() ?? "—"; - if (v.ValueKind == JsonValueKind.Array) - { - var items = v.EnumerateArray().Select(x => x.ValueKind == JsonValueKind.String ? x.GetString() : x.ToString()) - .Where(x => !string.IsNullOrWhiteSpace(x)); - var text = string.Join(", ", items!); - return string.IsNullOrWhiteSpace(text) ? "—" : text; - } - return "—"; - } - - sealed record ProbeResult(bool Ok, bool Reached, string Code, string RemoteIp, long Ms); - - async Task ProbeAsync(string url, string localIp, string[] allowed) - { - var sw = Stopwatch.StartNew(); - try - { - var psi = new ProcessStartInfo("curl.exe", - $"-4 --interface {localIp} --noproxy * -A \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36\" -sS -o NUL -w \"%{{http_code}}|%{{remote_ip}}|%{{time_total}}\" --connect-timeout 5 --max-time 8 \"{url}\"") - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden - }; - using var p = Process.Start(psi)!; - var stdout = await p.StandardOutput.ReadToEndAsync(); - await p.WaitForExitAsync(); - sw.Stop(); - var parts = stdout.Trim().Split('|'); - var code = parts.Length > 0 ? parts[0] : ""; - var remote = parts.Length > 1 ? parts[1] : ""; - var reached = p.ExitCode == 0 && !string.IsNullOrWhiteSpace(code) && code != "000"; - return new ProbeResult(reached && allowed.Contains(code), reached, code, remote, sw.ElapsedMilliseconds); - } - catch - { - sw.Stop(); - return new ProbeResult(false, false, "", "", sw.ElapsedMilliseconds); - } - } - - static string FmtHttp(ProbeResult p) => - p.Ok ? $"PASS · HTTP {p.Code} · {p.Ms} ms" : - p.Reached ? $"FAIL · HTTP {p.Code} · {p.Ms} ms" : - "FAIL · transport"; - - // OpenAI API is an endpoint transport probe, never evidence of a signed-in ChatGPT browser. - static string FmtOpenAiApiProbe(ProbeResult p) => p.Code switch - { - "403" when p.Reached => "API HTTP 403 forbidden · login unverified", - "401" when p.Reached => "API HTTP 401 auth required · login unverified", - "200" when p.Reached => "API HTTP 200 reachable · login unverified", - _ when p.Reached => $"API HTTP {p.Code} unexpected · login unverified", - _ => "API transport unavailable · login unverified" - }; - - static string FmtAdult(IReadOnlyCollection probes) - { - var okCount = probes.Count(x => x.Ok); - return okCount == probes.Count - ? $"PASS · full pages {okCount}/{probes.Count}" - : $"FAIL · full pages {okCount}/{probes.Count}"; - } - - async Task RefreshStatusAsync() - { - try - { - HealthText.Text = "Checking…"; - var raw = await RunPwshCaptureAsync("Status.ps1", "-Json"); - using var d = JsonDocument.Parse(raw); - var r = d.RootElement; - var mode = r.TryGetProperty("mode", out var me) ? me.GetString() ?? "UNKNOWN" : "UNKNOWN"; - var reason = r.TryGetProperty("reason", out var re) ? re.GetString() ?? "" : ""; - - StatusBadge.Text = mode; - StatusBadge.Foreground = mode switch - { - "ACTIVE" => ok, - "OFF" => neutral, - "CONFLICT" => warn, - "BLOCKED" => warn, - _ => bad - }; - - InterfaceText.Text = r.TryGetProperty("interface", out var i) ? i.GetString() ?? "—" : "—"; - IpText.Text = r.TryGetProperty("localIp", out var ip) ? ip.GetString() ?? "—" : "—"; - DnsText.Text = ReadStringList(r, "adapterDns"); - - int ula = r.TryGetProperty("ula", out var ue) && ue.ValueKind == JsonValueKind.Array ? ue.GetArrayLength() : 0; - int nrpt = r.TryGetProperty("nrpt", out var ne) && ne.ValueKind == JsonValueKind.Array ? ne.GetArrayLength() : 0; - int wd = r.TryGetProperty("winDivertCount", out var we) && we.TryGetInt32(out var wdv) ? wdv : 0; - int broad = r.TryGetProperty("broadRouteCount", out var be) && be.TryGetInt32(out var bv) ? bv : 0; - - OwnedText.Text = $"ULA {ula} · NRPT {nrpt} · DPI {wd}"; - if (broad > 0 && r.TryGetProperty("broadRoutes", out var routes) && routes.ValueKind == JsonValueKind.Array) - { - var owners = routes.EnumerateArray() - .Select(x => - { - if (x.TryGetProperty("description", out var de) && !string.IsNullOrWhiteSpace(de.GetString())) return de.GetString(); - return x.TryGetProperty("interface", out var ie) ? ie.GetString() : null; - }) - .Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(); - var suffix = string.Join(", ", owners!); - RouteText.Text = string.IsNullOrWhiteSpace(suffix) ? $"External tunnel routes: {broad}" : $"External tunnel routes: {broad} · {suffix}"; - } - else RouteText.Text = "External tunnel routes: 0"; - - bool ownedHealthy = r.TryGetProperty("ownedResourcesHealthy", out var oh) && oh.GetBoolean(); - YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy"; - AdultSiteText.Text = adultCheckEnabled ? (mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy") : "Disabled"; - if (ownedHealthy && mode == "ACTIVE" && !string.IsNullOrWhiteSpace(IpText.Text) && IpText.Text != "—") - { - var ytTask = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "200", "204" }); - var oaTask = ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" }); - var ghTask = ProbeAsync("https://github.com/", IpText.Text, new[] { "200", "301", "302" }); - var geminiTask = ProbeAsync("https://gemini.google.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }); - Task[]? adultTasks = adultCheckEnabled - ? new[] - { - ProbeAsync("https://www.pornhub.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), - ProbeAsync("https://www.xvideos.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), - ProbeAsync("https://www.xnxx.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), - ProbeAsync("https://xhamster.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }) - } - : null; - var tasks = new List { ytTask, oaTask, ghTask, geminiTask }; - if (adultTasks is not null) tasks.AddRange(adultTasks); - await Task.WhenAll(tasks); - YoutubeText.Text = FmtHttp(await ytTask); - OpenAiText.Text = FmtOpenAiApiProbe(await oaTask); - GithubText.Text = FmtHttp(await ghTask); - GeminiText.Text = FmtHttp(await geminiTask); - AdultSiteText.Text = adultTasks is null ? "Disabled" : FmtAdult(await Task.WhenAll(adultTasks)); - } - - HealthText.Text = mode switch - { - "ACTIVE" => "Direct path healthy", - "CONFLICT" => "External VPN/tunnel conflict", - "BLOCKED" => "Disconnect external VPN/tunnel to Start", - "DEGRADED" => "Recovery required", - "OFF" => "Ready", - _ => "Unknown state" - }; - if (!string.IsNullOrWhiteSpace(reason)) ToolTip = reason; - - StartButton.IsEnabled = mode == "OFF"; - StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED"; - RecoveryButton.IsEnabled = mode is "DEGRADED" or "CONFLICT"; - RefreshButton.IsEnabled = true; - } - catch (Exception ex) - { - HealthText.Text = "Status error"; - StatusBadge.Text = "ERROR"; - StatusBadge.Foreground = bad; - YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = ex.Message; - AdultSiteText.Text = adultCheckEnabled ? ex.Message : "Disabled"; - StartButton.IsEnabled = false; - StopButton.IsEnabled = false; - RecoveryButton.IsEnabled = true; - RefreshButton.IsEnabled = true; - } - } - - sealed record ActionResult(string Phase, int? ExitCode, string Message); - - async Task WaitForActionAsync(string action, DateTime previousWriteUtc, int timeoutMs = 130000) - { - var until = DateTime.UtcNow.AddMilliseconds(timeoutMs); - while (DateTime.UtcNow < until) - { - try - { - if (File.Exists(actionStatusPath) && File.GetLastWriteTimeUtc(actionStatusPath) > previousWriteUtc) - { - using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(actionStatusPath)); - var root = doc.RootElement; - var actual = root.TryGetProperty("action", out var ae) ? ae.GetString() ?? "" : ""; - var phase = root.TryGetProperty("phase", out var pe) ? pe.GetString() ?? "" : ""; - if (actual.Equals(action, StringComparison.OrdinalIgnoreCase) && - phase is "done" or "error" or "busy" or "handoff") - { - int? exit = root.TryGetProperty("exitCode", out var ee) && ee.ValueKind == JsonValueKind.Number ? ee.GetInt32() : null; - var message = root.TryGetProperty("message", out var me) ? me.GetString() ?? "" : ""; - return new ActionResult(phase, exit, message); - } - } - } - catch (IOException) { } - catch (JsonException) { } - await Task.Delay(200); - } - throw new TimeoutException($"{action} did not complete within {timeoutMs / 1000} seconds."); - } - - async Task RunActionAsync(string action, string label) - { - SetBusy(true, label + "…"); - try - { - var previousWriteUtc = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; - ServiceClient.Send(action); - var result = await WaitForActionAsync(action, previousWriteUtc); - if (result.Phase != "done" || result.ExitCode != 0) - throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) - ? $"{label} failed ({result.ExitCode?.ToString() ?? result.Phase})." - : result.Message); - } - catch (Exception ex) - { - HealthText.Text = ex.Message; - } - finally - { - await RefreshStatusAsync(); - SetBusy(false, ""); - } - } - - void SetBusy(bool busy, string message) - { - if (busy) - { - StartButton.IsEnabled = StopButton.IsEnabled = RefreshButton.IsEnabled = RecoveryButton.IsEnabled = UpdateButton.IsEnabled = false; - HealthText.Text = message; - } - else - { - UpdateButton.IsEnabled = true; - } - } - - async Task CheckForUpdatesAsync(bool userInitiated) - { - try - { - UpdateButton.IsEnabled = false; - UpdateButton.Content = "Checking…"; - availableUpdate = await UpdateClient.CheckAsync(CurrentVersion); - if (availableUpdate is null) - { - UpdateButton.Content = "Up to date"; - if (userInitiated) HealthText.Text = "No newer release is available"; - } - else - { - UpdateButton.Content = $"Update v{availableUpdate.Version}"; - ToolTip = $"Verified update available: {availableUpdate.Tag}"; - } - } - catch (Exception ex) - { - UpdateButton.Content = "Check Update"; - if (userInitiated) HealthText.Text = "Update check failed: " + ex.Message; - } - finally - { - UpdateButton.IsEnabled = true; - } - } - - async void Update_Click(object sender, RoutedEventArgs e) - { - if (availableUpdate is null) - { - await CheckForUpdatesAsync(true); - if (availableUpdate is null) return; - } - - try - { - SetBusy(true, $"Preparing direct update to v{availableUpdate.Version}…"); - - var recoveryWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; - ServiceClient.Send("recovery"); - var recovery = await WaitForActionAsync("recovery", recoveryWrite); - if (recovery.Phase != "done" || recovery.ExitCode != 0) - throw new InvalidOperationException(string.IsNullOrWhiteSpace(recovery.Message) - ? "Unable to prepare a clean OFF state for update." - : recovery.Message); - - HealthText.Text = $"Downloading and verifying v{availableUpdate.Version}…"; - var updateWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; - ServiceClient.Send("update"); - var result = await WaitForActionAsync("update", updateWrite, 300000); - - if (result.Phase == "handoff" && result.ExitCode == 0) - { - HealthText.Text = result.Message; - await Task.Delay(600); - Close(); - return; - } - - if (result.Phase == "done" && result.ExitCode == 0) - { - availableUpdate = null; - HealthText.Text = result.Message; - UpdateButton.Content = "Up to date"; - SetBusy(false, ""); - return; - } - - throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) - ? $"Direct update failed ({result.ExitCode?.ToString() ?? result.Phase})." - : result.Message); - } - catch (Exception ex) - { - HealthText.Text = "Update failed: " + ex.Message; - SetBusy(false, ""); - } - } - - async void AdultSiteCheckToggle_Changed(object sender, RoutedEventArgs e) - { - adultCheckEnabled = AdultSiteCheckToggle.IsChecked == true; - try - { - SaveAdultSiteCheckSetting(); - if (adultCheckEnabled) - { - AdultSiteText.Text = "Syncing coverage catalog…"; - var info = await EnsureAdultCatalogAsync(true); - AdultSiteText.Text = $"Enabled · {info.Count:N0} domains · Stop/Start to apply"; - } - else - { - AdultSiteText.Text = "Disabled"; - } - } - catch (Exception ex) - { - AdultSiteText.Text = adultCheckEnabled - ? "Fallback coverage enabled · catalog sync failed: " + ex.Message - : "Settings error: " + ex.Message; - } - await RefreshStatusAsync(); - } - - static string? NormalizeCustomSite(string raw) - { - var value = (raw ?? "").Trim(); - if (string.IsNullOrWhiteSpace(value) || value.StartsWith('#')) return null; - var exact = value.StartsWith('^'); - if (exact) value = value[1..].Trim(); - string host; - if (Uri.TryCreate(value, UriKind.Absolute, out var absolute) && !string.IsNullOrWhiteSpace(absolute.Host)) - host = absolute.Host; - else if (Uri.TryCreate("https://" + value, UriKind.Absolute, out var implied) && !string.IsNullOrWhiteSpace(implied.Host)) - host = implied.Host; - else - return null; - host = host.Trim('.').ToLowerInvariant(); - if (host.Length is < 3 or > 253 || !host.Contains('.')) return null; - if (host.Split('.').Any(x => !domainLabel.IsMatch(x))) return null; - return exact ? "^" + host : host; - } - - void CustomSites_Click(object sender, RoutedEventArgs e) - { - var editor = new Window - { - Title = "Custom Sites", Owner = this, Width = 570, Height = 470, MinWidth = 470, MinHeight = 360, - WindowStartupLocation = WindowStartupLocation.CenterOwner, - Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) - }; - var root = new DockPanel { Margin = new Thickness(18) }; - var note = new TextBlock - { - Text = "One site or URL per line. The saved domain and its subdomains use the existing Direct Method on the next Start. Prefix ^ for exact-host only. Maximum 256 entries.", - TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,12) - }; - DockPanel.SetDock(note, Dock.Top); root.Children.Add(note); - var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0,12,0,0) }; - DockPanel.SetDock(buttons, Dock.Bottom); - var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; - var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; - buttons.Children.Add(save); buttons.Children.Add(cancel); root.Children.Add(buttons); - var box = new TextBox - { - AcceptsReturn = true, TextWrapping = TextWrapping.NoWrap, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, - HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, FontFamily = new FontFamily("Consolas") - }; - try { if (File.Exists(customHostsPath)) box.Text = File.ReadAllText(customHostsPath); } catch { } - root.Children.Add(box); editor.Content = root; - save.Click += (_, _) => - { - try - { - var values = box.Text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None) - .Select(NormalizeCustomSite).Where(x => !string.IsNullOrWhiteSpace(x)) - .Distinct(StringComparer.OrdinalIgnoreCase).Take(256).ToArray(); - var dir = Path.GetDirectoryName(customHostsPath)!; Directory.CreateDirectory(dir); - var tmp = customHostsPath + ".tmp"; - File.WriteAllText(tmp, string.Join(Environment.NewLine, values!) + (values.Length > 0 ? Environment.NewLine : ""), new System.Text.UTF8Encoding(false)); - File.Move(tmp, customHostsPath, true); - editor.DialogResult = true; HealthText.Text = $"Custom sites saved: {values.Length}. Stop/Start to apply."; editor.Close(); - } - catch (Exception ex) - { - MessageBox.Show(editor, "Unable to save custom sites: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); - } - }; - editor.ShowDialog(); - } - void Strategy_Click(object sender, RoutedEventArgs e) - { - var current = "balanced"; - var allSites = false; - try - { - if (File.Exists(strategyPath)) - { - var raw = File.ReadAllText(strategyPath).Trim().ToLowerInvariant(); - if (raw is "balanced" or "compatibility" or "strong") current = raw; - } - } - catch { } - try - { - if (File.Exists(scopePath)) allSites = File.ReadAllText(scopePath).Trim().Equals("all-sites", StringComparison.OrdinalIgnoreCase); - } - catch { } - - var editor = new Window - { - Title = "Direct Strategy", Owner = this, Width = 540, Height = 390, ResizeMode = ResizeMode.NoResize, - WindowStartupLocation = WindowStartupLocation.CenterOwner, - Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) - }; - var root = new StackPanel { Margin = new Thickness(20) }; - root.Children.Add(new TextBlock - { - Text = "Choose the packet strategy used for built-in and Custom Sites. Balanced is the default. Compatibility uses split-only TCP handling. Strong is more aggressive and should be used only when Balanced still fails.", - TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,14) - }); - var combo = new ComboBox { MinWidth = 230, HorizontalAlignment = HorizontalAlignment.Left }; - combo.Items.Add("Balanced"); combo.Items.Add("Compatibility"); combo.Items.Add("Strong"); - combo.SelectedIndex = current switch { "compatibility" => 1, "strong" => 2, _ => 0 }; - root.Children.Add(combo); - var allSitesCheck = new CheckBox - { - Content = "Apply DPI strategy to all web sites (experimental)", - IsChecked = allSites, Foreground = Brushes.White, Margin = new Thickness(0,14,0,0) - }; - root.Children.Add(allSitesCheck); - root.Children.Add(new TextBlock - { - Text = "Targeted mode affects only built-in and Custom Sites. All Sites mode can help unknown blocked domains, but may reduce compatibility or speed on some sites. Neither mode creates a VPN, proxy, or default-route tunnel.", - TextWrapping = TextWrapping.Wrap, Foreground = neutral, Margin = new Thickness(0,12,0,16) - }); - var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right }; - var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; - var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; - buttons.Children.Add(cancel); buttons.Children.Add(save); root.Children.Add(buttons); editor.Content = root; - save.Click += (_, _) => - { - try - { - var value = combo.SelectedIndex switch { 1 => "compatibility", 2 => "strong", _ => "balanced" }; - var dir = Path.GetDirectoryName(strategyPath)!; Directory.CreateDirectory(dir); - var tmp = strategyPath + ".tmp"; - File.WriteAllText(tmp, value + Environment.NewLine, new System.Text.UTF8Encoding(false)); - File.Move(tmp, strategyPath, true); - var scopeValue = allSitesCheck.IsChecked == true ? "all-sites" : "targeted"; - var scopeTmp = scopePath + ".tmp"; - File.WriteAllText(scopeTmp, scopeValue + Environment.NewLine, new System.Text.UTF8Encoding(false)); - File.Move(scopeTmp, scopePath, true); - HealthText.Text = $"Strategy saved: {value}; scope: {scopeValue}. Stop/Start to apply."; - editor.DialogResult = true; editor.Close(); - } - catch (Exception ex) - { - MessageBox.Show(editor, "Unable to save strategy: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); - } - }; - editor.ShowDialog(); - } - - void RouterGateway_Click(object sender, RoutedEventArgs e) - { - var w = new RouterGatewayWindow { Owner = this }; - w.ShowDialog(); - } - - async void Start_Click(object sender, RoutedEventArgs e) => await RunActionAsync("start", "Start"); - async void Stop_Click(object sender, RoutedEventArgs e) => await RunActionAsync("stop", "Stop"); - async void Recovery_Click(object sender, RoutedEventArgs e) => await RunActionAsync("recovery", "Recovery"); - async void Refresh_Click(object sender, RoutedEventArgs e) => await RefreshStatusAsync(); -} +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace DirectInternetMethod; + +public partial class MainWindow : Window +{ + readonly string appDir = AppContext.BaseDirectory; + readonly string statePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectDnsDpiHarness", "state.json"); + readonly string actionStatusPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "action-status.json"); + readonly string bundledPwshPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "DirectInternetMethod", "Privileged", "runtime", "pwsh", "pwsh.exe"); + readonly Brush ok = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#68E8A6")); + readonly Brush warn = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFD166")); + readonly Brush bad = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF7A85")); + readonly Brush neutral = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9FB1C3")); + readonly string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DirectInternetMethod", "settings.json"); + readonly string customHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "custom-hosts.txt"); + readonly string adultEnabledPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-enabled.txt"); + readonly string adultHostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.txt"); + readonly string adultMetaPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "adult-hosts.meta.json"); + static readonly string[] AdultCatalogUrls = + { + "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/wildcard/nsfw-onlydomains.txt", + "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/nsfw-onlydomains.txt" + }; + static readonly Regex domainLabel = new("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", RegexOptions.CultureInvariant | RegexOptions.Compiled); + readonly string strategyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "strategy.txt"); + readonly string scopePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "DirectInternetMethod", "UserConfig", "scope.txt"); + UpdateInfo? availableUpdate; + bool adultCheckEnabled; + + public MainWindow() + { + InitializeComponent(); + adultCheckEnabled = LoadAdultSiteCheckSetting(); + AdultSiteCheckToggle.IsChecked = adultCheckEnabled; + AdultSiteCheckToggle.Checked += AdultSiteCheckToggle_Changed; + AdultSiteCheckToggle.Unchecked += AdultSiteCheckToggle_Changed; + Loaded += async (_, _) => + { + if (adultCheckEnabled) + { + try + { + SaveAdultSiteCheckSetting(); + await EnsureAdultCatalogAsync(false); + } + catch (Exception ex) + { + AdultSiteText.Text = "Coverage fallback active · catalog sync failed: " + ex.Message; + } + } + await RefreshStatusAsync(); + _ = CheckForUpdatesAsync(false); + }; + } + + Version CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 1, 0, 0); + + bool LoadAdultSiteCheckSetting() + { + try + { + if (File.Exists(adultEnabledPath)) + return File.ReadAllText(adultEnabledPath).Trim() == "1"; + if (!File.Exists(settingsPath)) return false; + using var doc = JsonDocument.Parse(File.ReadAllText(settingsPath)); + if (doc.RootElement.TryGetProperty("adultCoverageEnabled", out var coverage)) + return coverage.ValueKind == JsonValueKind.True; + return doc.RootElement.TryGetProperty("adultSiteLiveCheck", out var legacy) && legacy.ValueKind == JsonValueKind.True; + } + catch { return false; } + } + + void SaveAdultSiteCheckSetting() + { + var settingsDir = Path.GetDirectoryName(settingsPath)!; + Directory.CreateDirectory(settingsDir); + var settingsTemp = settingsPath + ".tmp"; + File.WriteAllText(settingsTemp, JsonSerializer.Serialize(new { adultCoverageEnabled = adultCheckEnabled, adultSiteLiveCheck = adultCheckEnabled })); + File.Move(settingsTemp, settingsPath, true); + + var configDir = Path.GetDirectoryName(adultEnabledPath)!; + Directory.CreateDirectory(configDir); + var enabledTemp = adultEnabledPath + ".tmp"; + File.WriteAllText(enabledTemp, adultCheckEnabled ? "1\r\n" : "0\r\n", new UTF8Encoding(false)); + File.Move(enabledTemp, adultEnabledPath, true); + if (!adultCheckEnabled) + { + try { if (File.Exists(adultHostsPath)) File.Delete(adultHostsPath); } catch { } + try { if (File.Exists(adultMetaPath)) File.Delete(adultMetaPath); } catch { } + } + } + + sealed record AdultCatalogInfo(int Count, string Sha256); + + static List ValidateAdultCatalog(string text) + { + var result = new List(); + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var raw in text.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None)) + { + var v = raw.Trim().ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(v) || v.StartsWith('#')) continue; + if (v.StartsWith('^')) v = v[1..]; + if (v.Length is < 3 or > 253 || !v.Contains('.')) continue; + if (v.Split('.').Any(x => !domainLabel.IsMatch(x))) continue; + if (seen.Add(v)) result.Add(v); + if (result.Count > 100000) throw new InvalidDataException("Adult catalog exceeds 100,000 validated domains."); + } + if (result.Count < 10000) throw new InvalidDataException("Adult catalog is unexpectedly small."); + foreach (var required in new[] { "xvideos.com", "xnxx.com", "xhamster.com", "pornhub.com", "redtube.com" }) + if (!seen.Contains(required)) throw new InvalidDataException("Adult catalog is missing a required coverage family."); + return result; + } + + async Task EnsureAdultCatalogAsync(bool force) + { + if (!force && File.Exists(adultHostsPath) && File.GetLastWriteTimeUtc(adultHostsPath) > DateTime.UtcNow.AddHours(-24)) + { + var existing = ValidateAdultCatalog(await File.ReadAllTextAsync(adultHostsPath)); + return new AdultCatalogInfo(existing.Count, Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(adultHostsPath)))); + } + + using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(20) }; + http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.5-rc1"); + Exception? lastError = null; + foreach (var sourceUrl in AdultCatalogUrls) + { + try + { + var bytes = await http.GetByteArrayAsync(sourceUrl); + if (bytes.Length is < 100000 or > 4 * 1024 * 1024) throw new InvalidDataException("Adult catalog size is outside the accepted range."); + var raw = Encoding.UTF8.GetString(bytes); + var domains = ValidateAdultCatalog(raw); + var dir = Path.GetDirectoryName(adultHostsPath)!; + Directory.CreateDirectory(dir); + var tmp = adultHostsPath + ".tmp"; + await File.WriteAllTextAsync(tmp, string.Join(Environment.NewLine, domains) + Environment.NewLine, new UTF8Encoding(false)); + File.Move(tmp, adultHostsPath, true); + var digest = Convert.ToHexString(SHA256.HashData(bytes)); + var metaTmp = adultMetaPath + ".tmp"; + await File.WriteAllTextAsync(metaTmp, JsonSerializer.Serialize(new + { + schema = 1, + source = sourceUrl, + fetchedUtc = DateTime.UtcNow.ToString("O"), + sourceSha256 = digest, + entries = domains.Count + }), new UTF8Encoding(false)); + File.Move(metaTmp, adultMetaPath, true); + return new AdultCatalogInfo(domains.Count, digest); + } + catch (Exception ex) + { + lastError = ex; + } + } + throw new InvalidOperationException("Adult catalog sync failed from all official upstream endpoints.", lastError); + } + + async Task RunPwshCaptureAsync(string script, string args = "") + { + if (!File.Exists(bundledPwshPath)) + throw new FileNotFoundException("Bundled PowerShell runtime is missing.", bundledPwshPath); + var psi = new ProcessStartInfo(bundledPwshPath, + $"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"{Path.Combine(appDir, script)}\" {args}") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = appDir + }; + using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start PowerShell 7."); + var output = await p.StandardOutput.ReadToEndAsync(); + var error = await p.StandardError.ReadToEndAsync(); + await p.WaitForExitAsync(); + if (p.ExitCode != 0) throw new InvalidOperationException(string.IsNullOrWhiteSpace(error) ? $"Status backend exited {p.ExitCode}." : error.Trim()); + return output; + } + + static string ReadStringList(JsonElement root, string property) + { + if (!root.TryGetProperty(property, out var v)) return "—"; + if (v.ValueKind == JsonValueKind.String) return v.GetString() ?? "—"; + if (v.ValueKind == JsonValueKind.Array) + { + var items = v.EnumerateArray().Select(x => x.ValueKind == JsonValueKind.String ? x.GetString() : x.ToString()) + .Where(x => !string.IsNullOrWhiteSpace(x)); + var text = string.Join(", ", items!); + return string.IsNullOrWhiteSpace(text) ? "—" : text; + } + return "—"; + } + + sealed record ProbeResult(bool Ok, bool Reached, string Code, string RemoteIp, long Ms); + + async Task ProbeAsync(string url, string localIp, string[] allowed) + { + var sw = Stopwatch.StartNew(); + try + { + var psi = new ProcessStartInfo("curl.exe", + $"-4 --interface {localIp} --noproxy * -A \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36\" -sS -o NUL -w \"%{{http_code}}|%{{remote_ip}}|%{{time_total}}\" --connect-timeout 5 --max-time 8 \"{url}\"") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + using var p = Process.Start(psi)!; + var stdout = await p.StandardOutput.ReadToEndAsync(); + await p.WaitForExitAsync(); + sw.Stop(); + var parts = stdout.Trim().Split('|'); + var code = parts.Length > 0 ? parts[0] : ""; + var remote = parts.Length > 1 ? parts[1] : ""; + var reached = p.ExitCode == 0 && !string.IsNullOrWhiteSpace(code) && code != "000"; + return new ProbeResult(reached && allowed.Contains(code), reached, code, remote, sw.ElapsedMilliseconds); + } + catch + { + sw.Stop(); + return new ProbeResult(false, false, "", "", sw.ElapsedMilliseconds); + } + } + + static string FmtHttp(ProbeResult p) => + p.Ok ? $"PASS · HTTP {p.Code} · {p.Ms} ms" : + p.Reached ? $"FAIL · HTTP {p.Code} · {p.Ms} ms" : + "FAIL · transport"; + + // OpenAI API is an endpoint transport probe, never evidence of a signed-in ChatGPT browser. + static string FmtOpenAiApiProbe(ProbeResult p) => p.Code switch + { + "403" when p.Reached => "API HTTP 403 forbidden · login unverified", + "401" when p.Reached => "API HTTP 401 auth required · login unverified", + "200" when p.Reached => "API HTTP 200 reachable · login unverified", + _ when p.Reached => $"API HTTP {p.Code} unexpected · login unverified", + _ => "API transport unavailable · login unverified" + }; + + static string FmtAdult(IReadOnlyCollection probes) + { + var okCount = probes.Count(x => x.Ok); + return okCount == probes.Count + ? $"PASS · full pages {okCount}/{probes.Count}" + : $"FAIL · full pages {okCount}/{probes.Count}"; + } + + async Task RefreshStatusAsync() + { + try + { + HealthText.Text = "Checking…"; + var raw = await RunPwshCaptureAsync("Status.ps1", "-Json"); + using var d = JsonDocument.Parse(raw); + var r = d.RootElement; + var mode = r.TryGetProperty("mode", out var me) ? me.GetString() ?? "UNKNOWN" : "UNKNOWN"; + var reason = r.TryGetProperty("reason", out var re) ? re.GetString() ?? "" : ""; + + StatusBadge.Text = mode; + StatusBadge.Foreground = mode switch + { + "ACTIVE" => ok, + "OFF" => neutral, + "CONFLICT" => warn, + "BLOCKED" => warn, + _ => bad + }; + + InterfaceText.Text = r.TryGetProperty("interface", out var i) ? i.GetString() ?? "—" : "—"; + IpText.Text = r.TryGetProperty("localIp", out var ip) ? ip.GetString() ?? "—" : "—"; + DnsText.Text = ReadStringList(r, "adapterDns"); + + int ula = r.TryGetProperty("ula", out var ue) && ue.ValueKind == JsonValueKind.Array ? ue.GetArrayLength() : 0; + int nrpt = r.TryGetProperty("nrpt", out var ne) && ne.ValueKind == JsonValueKind.Array ? ne.GetArrayLength() : 0; + int wd = r.TryGetProperty("winDivertCount", out var we) && we.TryGetInt32(out var wdv) ? wdv : 0; + int broad = r.TryGetProperty("broadRouteCount", out var be) && be.TryGetInt32(out var bv) ? bv : 0; + + OwnedText.Text = $"ULA {ula} · NRPT {nrpt} · DPI {wd}"; + if (broad > 0 && r.TryGetProperty("broadRoutes", out var routes) && routes.ValueKind == JsonValueKind.Array) + { + var owners = routes.EnumerateArray() + .Select(x => + { + if (x.TryGetProperty("description", out var de) && !string.IsNullOrWhiteSpace(de.GetString())) return de.GetString(); + return x.TryGetProperty("interface", out var ie) ? ie.GetString() : null; + }) + .Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(); + var suffix = string.Join(", ", owners!); + RouteText.Text = string.IsNullOrWhiteSpace(suffix) ? $"External tunnel routes: {broad}" : $"External tunnel routes: {broad} · {suffix}"; + } + else RouteText.Text = "External tunnel routes: 0"; + + bool ownedHealthy = r.TryGetProperty("ownedResourcesHealthy", out var oh) && oh.GetBoolean(); + YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy"; + AdultSiteText.Text = adultCheckEnabled ? (mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy") : "Disabled"; + if (ownedHealthy && mode == "ACTIVE" && !string.IsNullOrWhiteSpace(IpText.Text) && IpText.Text != "—") + { + var ytTask = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "200", "204" }); + var oaTask = ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" }); + var ghTask = ProbeAsync("https://github.com/", IpText.Text, new[] { "200", "301", "302" }); + var geminiTask = ProbeAsync("https://gemini.google.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }); + Task[]? adultTasks = adultCheckEnabled + ? new[] + { + ProbeAsync("https://www.pornhub.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), + ProbeAsync("https://www.xvideos.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), + ProbeAsync("https://www.xnxx.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }), + ProbeAsync("https://xhamster.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }) + } + : null; + var tasks = new List { ytTask, oaTask, ghTask, geminiTask }; + if (adultTasks is not null) tasks.AddRange(adultTasks); + await Task.WhenAll(tasks); + YoutubeText.Text = FmtHttp(await ytTask); + OpenAiText.Text = FmtOpenAiApiProbe(await oaTask); + GithubText.Text = FmtHttp(await ghTask); + GeminiText.Text = FmtHttp(await geminiTask); + AdultSiteText.Text = adultTasks is null ? "Disabled" : FmtAdult(await Task.WhenAll(adultTasks)); + } + + HealthText.Text = mode switch + { + "ACTIVE" => "Direct path healthy", + "CONFLICT" => "External VPN/tunnel conflict", + "BLOCKED" => "Disconnect external VPN/tunnel to Start", + "DEGRADED" => "Recovery required", + "OFF" => "Ready", + _ => "Unknown state" + }; + if (!string.IsNullOrWhiteSpace(reason)) ToolTip = reason; + + StartButton.IsEnabled = mode == "OFF"; + StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED"; + RecoveryButton.IsEnabled = mode is "DEGRADED" or "CONFLICT"; + RefreshButton.IsEnabled = true; + } + catch (Exception ex) + { + HealthText.Text = "Status error"; + StatusBadge.Text = "ERROR"; + StatusBadge.Foreground = bad; + YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = ex.Message; + AdultSiteText.Text = adultCheckEnabled ? ex.Message : "Disabled"; + StartButton.IsEnabled = false; + StopButton.IsEnabled = false; + RecoveryButton.IsEnabled = true; + RefreshButton.IsEnabled = true; + } + } + + sealed record ActionResult(string Phase, int? ExitCode, string Message); + + async Task WaitForActionAsync(string action, DateTime previousWriteUtc, int timeoutMs = 130000) + { + var until = DateTime.UtcNow.AddMilliseconds(timeoutMs); + while (DateTime.UtcNow < until) + { + try + { + if (File.Exists(actionStatusPath) && File.GetLastWriteTimeUtc(actionStatusPath) > previousWriteUtc) + { + using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(actionStatusPath)); + var root = doc.RootElement; + var actual = root.TryGetProperty("action", out var ae) ? ae.GetString() ?? "" : ""; + var phase = root.TryGetProperty("phase", out var pe) ? pe.GetString() ?? "" : ""; + if (actual.Equals(action, StringComparison.OrdinalIgnoreCase) && + phase is "done" or "error" or "busy" or "handoff") + { + int? exit = root.TryGetProperty("exitCode", out var ee) && ee.ValueKind == JsonValueKind.Number ? ee.GetInt32() : null; + var message = root.TryGetProperty("message", out var me) ? me.GetString() ?? "" : ""; + return new ActionResult(phase, exit, message); + } + } + } + catch (IOException) { } + catch (JsonException) { } + await Task.Delay(200); + } + throw new TimeoutException($"{action} did not complete within {timeoutMs / 1000} seconds."); + } + + async Task RunActionAsync(string action, string label) + { + SetBusy(true, label + "…"); + try + { + var previousWriteUtc = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; + ServiceClient.Send(action); + var result = await WaitForActionAsync(action, previousWriteUtc); + if (result.Phase != "done" || result.ExitCode != 0) + throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) + ? $"{label} failed ({result.ExitCode?.ToString() ?? result.Phase})." + : result.Message); + } + catch (Exception ex) + { + HealthText.Text = ex.Message; + } + finally + { + await RefreshStatusAsync(); + SetBusy(false, ""); + } + } + + void SetBusy(bool busy, string message) + { + if (busy) + { + StartButton.IsEnabled = StopButton.IsEnabled = RefreshButton.IsEnabled = RecoveryButton.IsEnabled = UpdateButton.IsEnabled = false; + HealthText.Text = message; + } + else + { + UpdateButton.IsEnabled = true; + } + } + + async Task CheckForUpdatesAsync(bool userInitiated) + { + try + { + UpdateButton.IsEnabled = false; + UpdateButton.Content = "Checking…"; + availableUpdate = await UpdateClient.CheckAsync(CurrentVersion); + if (availableUpdate is null) + { + UpdateButton.Content = "Up to date"; + if (userInitiated) HealthText.Text = "No newer release is available"; + } + else + { + UpdateButton.Content = $"Update v{availableUpdate.Version}"; + ToolTip = $"Verified update available: {availableUpdate.Tag}"; + } + } + catch (Exception ex) + { + UpdateButton.Content = "Check Update"; + if (userInitiated) HealthText.Text = "Update check failed: " + ex.Message; + } + finally + { + UpdateButton.IsEnabled = true; + } + } + + async void Update_Click(object sender, RoutedEventArgs e) + { + if (availableUpdate is null) + { + await CheckForUpdatesAsync(true); + if (availableUpdate is null) return; + } + + try + { + SetBusy(true, $"Preparing direct update to v{availableUpdate.Version}…"); + + var recoveryWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; + ServiceClient.Send("recovery"); + var recovery = await WaitForActionAsync("recovery", recoveryWrite); + if (recovery.Phase != "done" || recovery.ExitCode != 0) + throw new InvalidOperationException(string.IsNullOrWhiteSpace(recovery.Message) + ? "Unable to prepare a clean OFF state for update." + : recovery.Message); + + HealthText.Text = $"Downloading and verifying v{availableUpdate.Version}…"; + var updateWrite = File.Exists(actionStatusPath) ? File.GetLastWriteTimeUtc(actionStatusPath) : DateTime.MinValue; + ServiceClient.Send("update"); + var result = await WaitForActionAsync("update", updateWrite, 300000); + + if (result.Phase == "handoff" && result.ExitCode == 0) + { + HealthText.Text = result.Message; + await Task.Delay(600); + Close(); + return; + } + + if (result.Phase == "done" && result.ExitCode == 0) + { + availableUpdate = null; + HealthText.Text = result.Message; + UpdateButton.Content = "Up to date"; + SetBusy(false, ""); + return; + } + + throw new InvalidOperationException(string.IsNullOrWhiteSpace(result.Message) + ? $"Direct update failed ({result.ExitCode?.ToString() ?? result.Phase})." + : result.Message); + } + catch (Exception ex) + { + HealthText.Text = "Update failed: " + ex.Message; + SetBusy(false, ""); + } + } + + async void AdultSiteCheckToggle_Changed(object sender, RoutedEventArgs e) + { + adultCheckEnabled = AdultSiteCheckToggle.IsChecked == true; + try + { + SaveAdultSiteCheckSetting(); + if (adultCheckEnabled) + { + AdultSiteText.Text = "Syncing coverage catalog…"; + var info = await EnsureAdultCatalogAsync(true); + AdultSiteText.Text = $"Enabled · {info.Count:N0} domains · Stop/Start to apply"; + } + else + { + AdultSiteText.Text = "Disabled"; + } + } + catch (Exception ex) + { + AdultSiteText.Text = adultCheckEnabled + ? "Fallback coverage enabled · catalog sync failed: " + ex.Message + : "Settings error: " + ex.Message; + } + await RefreshStatusAsync(); + } + + static string? NormalizeCustomSite(string raw) + { + var value = (raw ?? "").Trim(); + if (string.IsNullOrWhiteSpace(value) || value.StartsWith('#')) return null; + var exact = value.StartsWith('^'); + if (exact) value = value[1..].Trim(); + string host; + if (Uri.TryCreate(value, UriKind.Absolute, out var absolute) && !string.IsNullOrWhiteSpace(absolute.Host)) + host = absolute.Host; + else if (Uri.TryCreate("https://" + value, UriKind.Absolute, out var implied) && !string.IsNullOrWhiteSpace(implied.Host)) + host = implied.Host; + else + return null; + host = host.Trim('.').ToLowerInvariant(); + if (host.Length is < 3 or > 253 || !host.Contains('.')) return null; + if (host.Split('.').Any(x => !domainLabel.IsMatch(x))) return null; + return exact ? "^" + host : host; + } + + void CustomSites_Click(object sender, RoutedEventArgs e) + { + var editor = new Window + { + Title = "Custom Sites", Owner = this, Width = 570, Height = 470, MinWidth = 470, MinHeight = 360, + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) + }; + var root = new DockPanel { Margin = new Thickness(18) }; + var note = new TextBlock + { + Text = "One site or URL per line. The saved domain and its subdomains use the existing Direct Method on the next Start. Prefix ^ for exact-host only. Maximum 256 entries.", + TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,12) + }; + DockPanel.SetDock(note, Dock.Top); root.Children.Add(note); + var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0,12,0,0) }; + DockPanel.SetDock(buttons, Dock.Bottom); + var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; + var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; + buttons.Children.Add(save); buttons.Children.Add(cancel); root.Children.Add(buttons); + var box = new TextBox + { + AcceptsReturn = true, TextWrapping = TextWrapping.NoWrap, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, FontFamily = new FontFamily("Consolas") + }; + try { if (File.Exists(customHostsPath)) box.Text = File.ReadAllText(customHostsPath); } catch { } + root.Children.Add(box); editor.Content = root; + save.Click += (_, _) => + { + try + { + var values = box.Text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None) + .Select(NormalizeCustomSite).Where(x => !string.IsNullOrWhiteSpace(x)) + .Distinct(StringComparer.OrdinalIgnoreCase).Take(256).ToArray(); + var dir = Path.GetDirectoryName(customHostsPath)!; Directory.CreateDirectory(dir); + var tmp = customHostsPath + ".tmp"; + File.WriteAllText(tmp, string.Join(Environment.NewLine, values!) + (values.Length > 0 ? Environment.NewLine : ""), new System.Text.UTF8Encoding(false)); + File.Move(tmp, customHostsPath, true); + editor.DialogResult = true; HealthText.Text = $"Custom sites saved: {values.Length}. Stop/Start to apply."; editor.Close(); + } + catch (Exception ex) + { + MessageBox.Show(editor, "Unable to save custom sites: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); + } + }; + editor.ShowDialog(); + } + void Strategy_Click(object sender, RoutedEventArgs e) + { + var current = "balanced"; + var allSites = false; + try + { + if (File.Exists(strategyPath)) + { + var raw = File.ReadAllText(strategyPath).Trim().ToLowerInvariant(); + if (raw is "balanced" or "compatibility" or "strong") current = raw; + } + } + catch { } + try + { + if (File.Exists(scopePath)) allSites = File.ReadAllText(scopePath).Trim().Equals("all-sites", StringComparison.OrdinalIgnoreCase); + } + catch { } + + var editor = new Window + { + Title = "Direct Strategy", Owner = this, Width = 540, Height = 390, ResizeMode = ResizeMode.NoResize, + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#091725")) + }; + var root = new StackPanel { Margin = new Thickness(20) }; + root.Children.Add(new TextBlock + { + Text = "Choose the packet strategy used for built-in and Custom Sites. Balanced is the default. Compatibility uses split-only TCP handling. Strong is more aggressive and should be used only when Balanced still fails.", + TextWrapping = TextWrapping.Wrap, Foreground = Brushes.White, Margin = new Thickness(0,0,0,14) + }); + var combo = new ComboBox { MinWidth = 230, HorizontalAlignment = HorizontalAlignment.Left }; + combo.Items.Add("Balanced"); combo.Items.Add("Compatibility"); combo.Items.Add("Strong"); + combo.SelectedIndex = current switch { "compatibility" => 1, "strong" => 2, _ => 0 }; + root.Children.Add(combo); + var allSitesCheck = new CheckBox + { + Content = "Apply DPI strategy to all web sites (experimental)", + IsChecked = allSites, Foreground = Brushes.White, Margin = new Thickness(0,14,0,0) + }; + root.Children.Add(allSitesCheck); + root.Children.Add(new TextBlock + { + Text = "Targeted mode affects only built-in and Custom Sites. All Sites mode can help unknown blocked domains, but may reduce compatibility or speed on some sites. Neither mode creates a VPN, proxy, or default-route tunnel.", + TextWrapping = TextWrapping.Wrap, Foreground = neutral, Margin = new Thickness(0,12,0,16) + }); + var buttons = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right }; + var cancel = new Button { Content = "Cancel", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsCancel = true }; + var save = new Button { Content = "Save", MinWidth = 90, Margin = new Thickness(8,0,0,0), IsDefault = true }; + buttons.Children.Add(cancel); buttons.Children.Add(save); root.Children.Add(buttons); editor.Content = root; + save.Click += (_, _) => + { + try + { + var value = combo.SelectedIndex switch { 1 => "compatibility", 2 => "strong", _ => "balanced" }; + var dir = Path.GetDirectoryName(strategyPath)!; Directory.CreateDirectory(dir); + var tmp = strategyPath + ".tmp"; + File.WriteAllText(tmp, value + Environment.NewLine, new System.Text.UTF8Encoding(false)); + File.Move(tmp, strategyPath, true); + var scopeValue = allSitesCheck.IsChecked == true ? "all-sites" : "targeted"; + var scopeTmp = scopePath + ".tmp"; + File.WriteAllText(scopeTmp, scopeValue + Environment.NewLine, new System.Text.UTF8Encoding(false)); + File.Move(scopeTmp, scopePath, true); + HealthText.Text = $"Strategy saved: {value}; scope: {scopeValue}. Stop/Start to apply."; + editor.DialogResult = true; editor.Close(); + } + catch (Exception ex) + { + MessageBox.Show(editor, "Unable to save strategy: " + ex.Message, "Direct Internet Method", MessageBoxButton.OK, MessageBoxImage.Error); + } + }; + editor.ShowDialog(); + } + + void RouterGateway_Click(object sender, RoutedEventArgs e) + { + var w = new RouterGatewayWindow { Owner = this }; + w.ShowDialog(); + } + + async void Start_Click(object sender, RoutedEventArgs e) => await RunActionAsync("start", "Start"); + async void Stop_Click(object sender, RoutedEventArgs e) => await RunActionAsync("stop", "Stop"); + async void Recovery_Click(object sender, RoutedEventArgs e) => await RunActionAsync("recovery", "Recovery"); + async void Refresh_Click(object sender, RoutedEventArgs e) => await RefreshStatusAsync(); +} From 4c8199c04fdc6f0d1d28dffd30f42569a6aa47a1 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:55:19 +0330 Subject: [PATCH 011/188] fix(ci): normalize all remaining Windows text blobs to indexed LF (R535) Apply .gitattributes-aware canonical LF blob storage to private Windows RELEASE.json, manifest.json, and Service.csproj. Windows checkout still materializes CRLF as appropriate. No source logic or product payload content changes; preserve special mixed-line-ending Inno source with text=-text. --- windows/RELEASE.json | 296 +- windows/manifest.json | 9572 ++++++++--------- .../DirectInternetMethod.Service.csproj | 28 +- 3 files changed, 4948 insertions(+), 4948 deletions(-) diff --git a/windows/RELEASE.json b/windows/RELEASE.json index 6d38b73e..3fad5819 100644 --- a/windows/RELEASE.json +++ b/windows/RELEASE.json @@ -1,148 +1,148 @@ -{ - "schema": 1, - "product": "Direct Internet Method", - "version": "1.5.5-rc1", - "status": "PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", - "date": "2026-10-13", - "previousAccepted": { - "version": "1.5.4", - "tag": "v1.5.4", - "tagCommit": "51009372e3c2d069d7016b97773f35ee5e1e0631", - "status": "PUBLIC_STABLE_UNCHANGED" - }, - "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", - "features": { - "directDnsDpi": true, - "directMethods": [ - "encrypted-dns-doh", - "http-host-split-tcp80", - "tls-sni-desync-tcp443", - "quic-desync-udp443", - "custom-hostlist", - "adult-catalog", - "strategy-profile" - ], - "noVpn": true, - "noProxy": true, - "noDefaultRouteTunnel": true, - "noPhysicalAdapterDnsMutation": true, - "targetCoverage": [ - "YouTube family", - "Gemini", - "Adult site/CDN", - "OpenAI/ChatGPT", - "GitHub", - "user Custom Sites" - ], - "liveChecks": [ - "YouTube", - "OpenAI", - "GitHub", - "Gemini", - "Adult coverage representative matrix" - ], - "routerGateway": true, - "routerGatewayProtocols": [ - "L2TP/IPsec", - "PPTP" - ], - "routerGatewayProfileCount": 9, - "routerGatewayIndependentProviders": 3, - "preferredStartupWindow": "1289x632 DIP", - "responsiveFooterWrap": true, - "secureDnsTransports": [ - "DoH" - ], - "secureDnsLeakToOsResolver": false, - "secureDnsPrimary": "DoH direct IP 76.76.10.11/p0", - "researchedButNotEnabledDnsTransports": [ - "DoH3", - "DoQ", - "DoT" - ], - "hostlistScopedByDefault": true, - "scopeModes": { - "default": "targeted", - "available": [ - "targeted", - "all-sites" - ], - "allSitesExperimental": true - }, - "strategyProfiles": { - "default": "balanced", - "available": [ - "balanced", - "compatibility", - "strong" - ] - }, - "customSites": { - "enabled": true, - "maxEntries": 256, - "normalizesUrlsToDomains": true, - "exactHostPrefix": "^" - }, - "adultCoverage": { - "enabledByDefault": false, - "optIn": true, - "toggleable": true, - "persistedPerUser": true, - "uiLabel": "Adult coverage", - "maintainedCatalog": true, - "catalogSources": [ - "HaGeZi NSFW only-domains raw GitHub", - "HaGeZi NSFW only-domains jsDelivr fallback" - ], - "offlineFallback": true, - "minimumValidatedDomains": 10000, - "maximumValidatedDomains": 100000, - "usablePageAcceptance": "2xx/3xx only" - } - }, - "usage": { - "chatgptRequired": false, - "offlineRuntime": true, - "adminRequiredAtInstallOrPrivilegedUpgrade": true, - "adminRequiredForNormalRun": false, - "controlPanel": "app/DirectInternetMethod.exe", - "privilegedService": "DirectInternetMethodSvc", - "externalPowerShellRequired": false, - "bundledPowerShell": "7.6.6" - }, - "acceptance": { - "windowsContract": "PENDING_V154_REVERIFICATION", - "serviceSelftest": "PENDING_V154_REVERIFICATION", - "guiCompile": "PENDING_V154_REVERIFICATION", - "startupLayout": "PENDING_V154_REVERIFICATION", - "targetHostlistContract": "PENDING_V154_REVERIFICATION", - "siteCoverageLive": "PENDING_V154_REVERIFICATION", - "publication": "NOT_PUBLISHED", - "strategyProfiles": "PENDING_V154_REVERIFICATION", - "allSitesScope": "PENDING_V154_REVERIFICATION", - "securityRejectedComponentGate": "PENDING_V154_REVERIFICATION", - "artifactIdentity": "PENDING_V154_REVERIFICATION", - "windowsInstalledLive": "PENDING_V154_REVERIFICATION", - "startupHealth": "PENDING_V154_REVERIFICATION", - "adultCoverage": "PENDING_V154_REVERIFICATION", - "gemini": "FAIL_HTTP_403_PREVIOUS_EGRESS_UNCHANGED" - }, - "validationBoundary": [ - "Direct Internet Method does not change public egress IP; remote country/account/product restrictions are outside direct DNS/DPI control.", - "Gemini returns HTTP 403 on the validated IR egress in both curl and real headless Chrome. A usable Gemini page requires a separate egress-changing architecture.", - "Adult coverage uses a maintained opt-in catalog plus bounded host-specific profiles. Windows stable-state representative probes return HTTP 200 for Pornhub, RedTube, XVideos, XNXX and XHamster.", - "XHamster can resolve to multiple Cloudflare edges. Windows exact-installed balanced/targeted normal-client representative testing is HTTP 200; release claims do not assert universal availability for every remote CDN edge.", - "Linux exact-installed normal-client representative matrix passes Pornhub, XVideos, XNXX, XHamster and Reddit; forced edge 104.16.3.81 is unusable on the validated Linux path, while normal clients demonstrated failover to 104.16.4.81 with 5/5 successful restart cycles.", - "HTTP 4xx/5xx is not accepted as usable-page PASS.", - "Targeted remains the default scope; All Sites is explicit opt-in.", - "Windows installer remains not Authenticode-signed." - ], - "exactNextAction": "Windows 1.5.5-rc1 isolated build and staged-hosted install/upgrade rollback with byte-pinned public v1.5.4; no public promotion before native acceptance.", - "buildIdentityNote": "Installer and manifest hashes are intentionally not embedded here because this file is itself part of the packaged payload. Authoritative artifact identity lives in root RELEASE.json, SHA256SUMS.txt and evidence records.", - "publication": { - "tag": null, - "tagCommit": null, - "releaseUrl": null, - "publishedAt": null - } -} +{ + "schema": 1, + "product": "Direct Internet Method", + "version": "1.5.5-rc1", + "status": "PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", + "date": "2026-10-13", + "previousAccepted": { + "version": "1.5.4", + "tag": "v1.5.4", + "tagCommit": "51009372e3c2d069d7016b97773f35ee5e1e0631", + "status": "PUBLIC_STABLE_UNCHANGED" + }, + "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", + "features": { + "directDnsDpi": true, + "directMethods": [ + "encrypted-dns-doh", + "http-host-split-tcp80", + "tls-sni-desync-tcp443", + "quic-desync-udp443", + "custom-hostlist", + "adult-catalog", + "strategy-profile" + ], + "noVpn": true, + "noProxy": true, + "noDefaultRouteTunnel": true, + "noPhysicalAdapterDnsMutation": true, + "targetCoverage": [ + "YouTube family", + "Gemini", + "Adult site/CDN", + "OpenAI/ChatGPT", + "GitHub", + "user Custom Sites" + ], + "liveChecks": [ + "YouTube", + "OpenAI", + "GitHub", + "Gemini", + "Adult coverage representative matrix" + ], + "routerGateway": true, + "routerGatewayProtocols": [ + "L2TP/IPsec", + "PPTP" + ], + "routerGatewayProfileCount": 9, + "routerGatewayIndependentProviders": 3, + "preferredStartupWindow": "1289x632 DIP", + "responsiveFooterWrap": true, + "secureDnsTransports": [ + "DoH" + ], + "secureDnsLeakToOsResolver": false, + "secureDnsPrimary": "DoH direct IP 76.76.10.11/p0", + "researchedButNotEnabledDnsTransports": [ + "DoH3", + "DoQ", + "DoT" + ], + "hostlistScopedByDefault": true, + "scopeModes": { + "default": "targeted", + "available": [ + "targeted", + "all-sites" + ], + "allSitesExperimental": true + }, + "strategyProfiles": { + "default": "balanced", + "available": [ + "balanced", + "compatibility", + "strong" + ] + }, + "customSites": { + "enabled": true, + "maxEntries": 256, + "normalizesUrlsToDomains": true, + "exactHostPrefix": "^" + }, + "adultCoverage": { + "enabledByDefault": false, + "optIn": true, + "toggleable": true, + "persistedPerUser": true, + "uiLabel": "Adult coverage", + "maintainedCatalog": true, + "catalogSources": [ + "HaGeZi NSFW only-domains raw GitHub", + "HaGeZi NSFW only-domains jsDelivr fallback" + ], + "offlineFallback": true, + "minimumValidatedDomains": 10000, + "maximumValidatedDomains": 100000, + "usablePageAcceptance": "2xx/3xx only" + } + }, + "usage": { + "chatgptRequired": false, + "offlineRuntime": true, + "adminRequiredAtInstallOrPrivilegedUpgrade": true, + "adminRequiredForNormalRun": false, + "controlPanel": "app/DirectInternetMethod.exe", + "privilegedService": "DirectInternetMethodSvc", + "externalPowerShellRequired": false, + "bundledPowerShell": "7.6.6" + }, + "acceptance": { + "windowsContract": "PENDING_V154_REVERIFICATION", + "serviceSelftest": "PENDING_V154_REVERIFICATION", + "guiCompile": "PENDING_V154_REVERIFICATION", + "startupLayout": "PENDING_V154_REVERIFICATION", + "targetHostlistContract": "PENDING_V154_REVERIFICATION", + "siteCoverageLive": "PENDING_V154_REVERIFICATION", + "publication": "NOT_PUBLISHED", + "strategyProfiles": "PENDING_V154_REVERIFICATION", + "allSitesScope": "PENDING_V154_REVERIFICATION", + "securityRejectedComponentGate": "PENDING_V154_REVERIFICATION", + "artifactIdentity": "PENDING_V154_REVERIFICATION", + "windowsInstalledLive": "PENDING_V154_REVERIFICATION", + "startupHealth": "PENDING_V154_REVERIFICATION", + "adultCoverage": "PENDING_V154_REVERIFICATION", + "gemini": "FAIL_HTTP_403_PREVIOUS_EGRESS_UNCHANGED" + }, + "validationBoundary": [ + "Direct Internet Method does not change public egress IP; remote country/account/product restrictions are outside direct DNS/DPI control.", + "Gemini returns HTTP 403 on the validated IR egress in both curl and real headless Chrome. A usable Gemini page requires a separate egress-changing architecture.", + "Adult coverage uses a maintained opt-in catalog plus bounded host-specific profiles. Windows stable-state representative probes return HTTP 200 for Pornhub, RedTube, XVideos, XNXX and XHamster.", + "XHamster can resolve to multiple Cloudflare edges. Windows exact-installed balanced/targeted normal-client representative testing is HTTP 200; release claims do not assert universal availability for every remote CDN edge.", + "Linux exact-installed normal-client representative matrix passes Pornhub, XVideos, XNXX, XHamster and Reddit; forced edge 104.16.3.81 is unusable on the validated Linux path, while normal clients demonstrated failover to 104.16.4.81 with 5/5 successful restart cycles.", + "HTTP 4xx/5xx is not accepted as usable-page PASS.", + "Targeted remains the default scope; All Sites is explicit opt-in.", + "Windows installer remains not Authenticode-signed." + ], + "exactNextAction": "Windows 1.5.5-rc1 isolated build and staged-hosted install/upgrade rollback with byte-pinned public v1.5.4; no public promotion before native acceptance.", + "buildIdentityNote": "Installer and manifest hashes are intentionally not embedded here because this file is itself part of the packaged payload. Authoritative artifact identity lives in root RELEASE.json, SHA256SUMS.txt and evidence records.", + "publication": { + "tag": null, + "tagCommit": null, + "releaseUrl": null, + "publishedAt": null + } +} diff --git a/windows/manifest.json b/windows/manifest.json index 35d6b7d1..2da6b41e 100644 --- a/windows/manifest.json +++ b/windows/manifest.json @@ -1,4786 +1,4786 @@ -{ - "schema": 2, - "product": "Direct Internet Method", - "version": "1.5.5-rc1", - "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", - "offlineRuntime": true, - "normalRunRequiresAdmin": false, - "installOrPrivilegedUpgradeRequiresAdmin": true, - "directUpdate": { - "transport": "GitHub latest release HTTPS", - "privilegeModel": "DirectInternetMethodSvc performs download/verification/installer handoff", - "userUacRequired": false, - "integrity": "GitHub asset SHA-256 digest must equal SHA256SUMS.txt and downloaded installer SHA-256" - }, - "bundledPowerShellVersion": "7.6.6", - "bundledPowerShellArchiveSha256": "02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860", - "files": [ - { - "scope": "user", - "file": "app/DirectInternetMethod.exe", - "source": "app/DirectInternetMethod.exe", - "bytes": 139900030, - "sha256": "FBC67BE3B361308953CD07899A160F1A79A8B20124E54B90585BE00C8B391513" - }, - { - "scope": "user", - "file": "app/DirectInternetMethod.ico", - "source": "app/DirectInternetMethod.ico", - "bytes": 33478, - "sha256": "00D25A881AB5CA731437F5C631C031FD0FC0A8223F45181ACB6240EAA28AB01F" - }, - { - "scope": "user", - "file": "app/Status.ps1", - "source": "app/Status.ps1", - "bytes": 6394, - "sha256": "EC4B00233C1A0BD462D7AD674E54254F02BF462A6D3D53ACED47888CEE4A3447" - }, - { - "scope": "user", - "file": "app/router_gateway/providers.json", - "source": "../router_gateway/providers.json", - "bytes": 7111, - "sha256": "D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD" - }, - { - "scope": "user", - "file": "docs/README-FA.md", - "source": "docs/README-FA.md", - "bytes": 2191, - "sha256": "81C4242367FB6E15EBB2E03F0B4424EFD173091A46FE724AAEB1E9F22DD1B491" - }, - { - "scope": "user", - "file": "docs/ARCHITECTURE.md", - "source": "docs/ARCHITECTURE.md", - "bytes": 1863, - "sha256": "E4AC239D27A060B5CA0EF55C46B99FC9196725B44E07AB16F67C35E162C86BB8" - }, - { - "scope": "user", - "file": "RELEASE.json", - "source": "RELEASE.json", - "bytes": 5851, - "sha256": "AFB3CC3ED64FD3D1E4D9460C1171899FAE0B9233AB96337498BAABF0CF353C5F" - }, - { - "scope": "privileged", - "file": "app/DirectInternetMethod.Service.exe", - "source": "app/DirectInternetMethod.Service.exe", - "bytes": 73627467, - "sha256": "EE02C5188BAF3997DDB56787A671AA7947D2BE895C208271D4AE5ECFE309FF46" - }, - { - "scope": "privileged", - "file": "app/Start-Direct.ps1", - "source": "app/Start-Direct.ps1", - "bytes": 26860, - "sha256": "B186872EDE733AE876FF1463E030988E9149A2CC88188F4B6C1FA5B564AEFC05" - }, - { - "scope": "privileged", - "file": "app/Stop-Direct.ps1", - "source": "app/Stop-Direct.ps1", - "bytes": 10426, - "sha256": "4E113237768506F940870CA7550EB3DD6A1B41C51879DD01D944A5E77BD96327" - }, - { - "scope": "privileged", - "file": "app/Recovery.ps1", - "source": "app/Recovery.ps1", - "bytes": 6157, - "sha256": "A406F1B23EF95C77C6B2FB4ECDB6337E582AB06D9A7927DEB31E916F06C870E1" - }, - { - "scope": "privileged", - "file": "app/SafeLegacyProviderRollback.ps1", - "source": "app/SafeLegacyProviderRollback.ps1", - "bytes": 13397, - "sha256": "522D78CB2A40A917B4E86DD7F98F4D3B076FD8A551B97C4B0C211FDFC7D9B322" - }, - { - "scope": "privileged", - "file": "bin/ctrld/ctrld.exe", - "source": "bin/ctrld/ctrld.exe", - "bytes": 7602136, - "sha256": "FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD" - }, - { - "scope": "privileged", - "file": "bin/ctrld/ctrld.toml", - "source": "bin/ctrld/ctrld.toml", - "bytes": 540, - "sha256": "B056AD8A51078208AED39744D48C4F4369AE5BA1E76F7D6F84B027C34CE1314B" - }, - { - "scope": "privileged", - "file": "bin/ctrld/LICENSE.txt", - "source": "bin/ctrld/LICENSE.txt", - "bytes": 1122, - "sha256": "5B166F4937F5D1ADED9C6D90675524ABAB6A6F0EBA1E226FA30119F37DBAB754" - }, - { - "scope": "privileged", - "file": "bin/zapret/winws.exe", - "source": "bin/zapret/winws.exe", - "bytes": 223232, - "sha256": "A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8" - }, - { - "scope": "privileged", - "file": "bin/zapret/WinDivert.dll", - "source": "bin/zapret/WinDivert.dll", - "bytes": 47616, - "sha256": "C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2" - }, - { - "scope": "privileged", - "file": "bin/zapret/WinDivert64.sys", - "source": "bin/zapret/WinDivert64.sys", - "bytes": 94144, - "sha256": "8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2" - }, - { - "scope": "privileged", - "file": "bin/zapret/cygwin1.dll", - "source": "bin/zapret/cygwin1.dll", - "bytes": 2954293, - "sha256": "103104A52E5293CE418944725DF19E2BF81AD9269B9A120D71D39028E821499B" - }, - { - "scope": "privileged", - "file": "bin/zapret/hosts.txt", - "source": "bin/zapret/hosts.txt", - "bytes": 287, - "sha256": "48EF43491901AB4336DDDFE6324C5820AC8B90F59511F138A7202DBCC8B033A3" - }, - { - "scope": "privileged", - "file": "bin/zapret/adult-fallback-hosts.txt", - "source": "bin/zapret/adult-fallback-hosts.txt", - "bytes": 448, - "sha256": "25BA72F6D56EF1C405115C7AA5D6ED1985E8F6D56F306A95D5AD800602A75288" - }, - { - "scope": "privileged", - "file": "bin/zapret/strong-override-hosts.txt", - "source": "bin/zapret/strong-override-hosts.txt", - "bytes": 99, - "sha256": "A371B754B201D1E87A9C0474BDEA1821539D7DAF20B5DA6C8F5105A1F85C8506" - }, - { - "scope": "privileged", - "file": "bin/zapret/LICENSE.txt", - "source": "bin/zapret/LICENSE.txt", - "bytes": 1090, - "sha256": "1FACB290FF6425CF6AB61FC882E71FFB0DF50FDA527FEF90D4098935097BD984" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Accessibility.dll", - "source": "vendor/pwsh/Accessibility.dll", - "bytes": 20816, - "sha256": "7E2CA83155D9F4B80909036B964461E1D020D25E107E378161AE7A16F52102A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/build.manifest", - "source": "vendor/pwsh/build.manifest", - "bytes": 3874, - "sha256": "58A354C02F0674CF951CEFF4E780F7532EAEE9C814B1FC75BDD3B943DD109F7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/build.manifest.sig", - "source": "vendor/pwsh/build.manifest.sig", - "bytes": 3277, - "sha256": "5E8F9EBCEE16A53E65BF204C4CFF40CF137D567229143126C9BA789E5B75FCA1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clretwrc.dll", - "source": "vendor/pwsh/clretwrc.dll", - "bytes": 317776, - "sha256": "557B5E6D5CC6C0BAECA172B987B023BEDB8B0C0C0ADF1352186768A926F671DD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clrgc.dll", - "source": "vendor/pwsh/clrgc.dll", - "bytes": 674088, - "sha256": "1AD35FC3FC5D8E26E007CFA21377579C748F1EB78BC7AC820813B505398E6403" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clrgcexp.dll", - "source": "vendor/pwsh/clrgcexp.dll", - "bytes": 711464, - "sha256": "2271063DED2A3A5A520380147300B483CA30403F0ACADCF03BAF8FBA0B53E23F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/clrjit.dll", - "source": "vendor/pwsh/clrjit.dll", - "bytes": 2092328, - "sha256": "8B3E57D21CC9914A0F2DF5DF3B0FA5FC426D91282846EB88DA07736CFB7416AA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/coreclr.dll", - "source": "vendor/pwsh/coreclr.dll", - "bytes": 4614952, - "sha256": "20EA763D5AD8BE8656A3123C4AD5AFDB4C714A009BFC63BB8F4906D6678130DB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/createdump.exe", - "source": "vendor/pwsh/createdump.exe", - "bytes": 71512, - "sha256": "6A1B3A1AF1AFC2B756E2E45C36A3AC0D05D3134B14D8C66BCCFDBCC1F308B4E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/D3DCompiler_47_cor3.dll", - "source": "vendor/pwsh/D3DCompiler_47_cor3.dll", - "bytes": 4751568, - "sha256": "2D3B1B3057234778DCEBA837FF082355E6F6625AA5B68C4E69DCDF21106CEB4D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/DirectWriteForwarder.dll", - "source": "vendor/pwsh/DirectWriteForwarder.dll", - "bytes": 505640, - "sha256": "0384825B584E93D197040E7199E1C3D990190BBEDDBF65F327282D1CDA3FAE7F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/en-US/default.help.txt", - "source": "vendor/pwsh/en-US/default.help.txt", - "bytes": 3420, - "sha256": "5AD33AD117ECD78AD5E84AD745E69D45E6A6A10E68A0C7333C2CE95E102FD602" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/getfilesiginforedist.dll", - "source": "vendor/pwsh/getfilesiginforedist.dll", - "bytes": 67104, - "sha256": "153EEFB2EAFA8B2B909854CC1F941350EFB1170E179A299DE8836B8EC5CE6A7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/getfilesiginforedistwrapper.dll", - "source": "vendor/pwsh/getfilesiginforedistwrapper.dll", - "bytes": 30528, - "sha256": "7643100BB96726F8603975EAC7074174BF06F05AC3351CF8FCE0B8AAEF90ADD0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/hostfxr.dll", - "source": "vendor/pwsh/hostfxr.dll", - "bytes": 379688, - "sha256": "6D2D8B595846BFCBB5EF7B1CB1A3156AFF4930D2002B6AC813910B848C918855" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/hostpolicy.dll", - "source": "vendor/pwsh/hostpolicy.dll", - "bytes": 378664, - "sha256": "400E8547169E8D4405ADFD79510AE429F76F7A580932AC8A602EF5D88E4FEB2E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Humanizer.dll", - "source": "vendor/pwsh/Humanizer.dll", - "bytes": 845656, - "sha256": "E6AD30F638466744B2F64006AB48E8712B35D95269C08B64E9C9E6044150B612" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Install-PowerShellRemoting.ps1", - "source": "vendor/pwsh/Install-PowerShellRemoting.ps1", - "bytes": 23219, - "sha256": "A7BB6A752B186E225AB5080F0E5D26CD8598D827322D5927444508AEE937E285" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/InstallPSCorePolicyDefinitions.ps1", - "source": "vendor/pwsh/InstallPSCorePolicyDefinitions.ps1", - "bytes": 17206, - "sha256": "0B392011A5B57C05B7A8974406B20F8654CE1B32E11972DB6BABEB2765AE8EF8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Json.More.dll", - "source": "vendor/pwsh/Json.More.dll", - "bytes": 145224, - "sha256": "A34CF6FDCA5028DAC2D51F97951A13AEC724F172718568708CB9EBE2767813E6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/JsonPointer.Net.dll", - "source": "vendor/pwsh/JsonPointer.Net.dll", - "bytes": 75592, - "sha256": "D570D76B007F563490074D4BA738904F2886F9BD8F44D4C3F365D6AE51A90311" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/JsonSchema.Net.dll", - "source": "vendor/pwsh/JsonSchema.Net.dll", - "bytes": 812848, - "sha256": "262C0DFCFE5E1B2E99BA4D915E00B18C1094195643B19F04559DFDA1A8218A23" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/LICENSE.txt", - "source": "vendor/pwsh/LICENSE.txt", - "bytes": 1095, - "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Markdig.Signed.dll", - "source": "vendor/pwsh/Markdig.Signed.dll", - "bytes": 1361728, - "sha256": "17E508A3B13A1F52C463D0126004095032E4F0614CC275628F55BA267D281F56" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.ApplicationInsights.dll", - "source": "vendor/pwsh/Microsoft.ApplicationInsights.dll", - "bytes": 923480, - "sha256": "EA8B0774AFFEEC16ADFD24FC55CECCE39F778CA55E89A2FF6B2B457AFCCF44BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", - "source": "vendor/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", - "bytes": 30536, - "sha256": "53029EFFB9F6426939C5B702B4B2B322E4A5ACD35CF4A1ACE8E90F38B5BDEC15" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.CodeAnalysis.CSharp.dll", - "source": "vendor/pwsh/Microsoft.CodeAnalysis.CSharp.dll", - "bytes": 18536312, - "sha256": "78533CFBF2E43A374E58F05EF2C495ED56BF93EAA63BE0EC3F2B35FC621D008E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.CodeAnalysis.dll", - "source": "vendor/pwsh/Microsoft.CodeAnalysis.dll", - "bytes": 8128336, - "sha256": "14856FD1CDB7F425323C82D276A9ADD64454EDF0325A28B588C9825838938200" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.CSharp.dll", - "source": "vendor/pwsh/Microsoft.CSharp.dll", - "bytes": 980776, - "sha256": "C412EB8721B14AFC5BAE7C8D26B4239D1DB11486F0192FA39C3A8B4F6F23A1D2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Extensions.ObjectPool.dll", - "source": "vendor/pwsh/Microsoft.Extensions.ObjectPool.dll", - "bytes": 42824, - "sha256": "68AB071B3E6031888BD4944C2CED33B4973CB4C04877D9202781F6BD41F23EAE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", - "source": "vendor/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", - "bytes": 493376, - "sha256": "3C3FEDB396CFB166288A4ED6726F3F7E9CC4057A1052830573BE4B3313146965" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/microsoft.management.infrastructure.dll", - "source": "vendor/pwsh/microsoft.management.infrastructure.dll", - "bytes": 309040, - "sha256": "A5138F7B4EA083FA297E744D26AF2254EE3B593186CAA750A6E4963C00F3D778" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/microsoft.management.infrastructure.native.dll", - "source": "vendor/pwsh/microsoft.management.infrastructure.native.dll", - "bytes": 362288, - "sha256": "FFA5E12A8A10634F88796D20711CA0585C42BB22652157A2D776965AEBD34EAE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", - "source": "vendor/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", - "bytes": 28192, - "sha256": "9BEE4E35576355156F00E2E47EF57AA2C8CA64390C112F083A772F2219026293" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", - "bytes": 333624, - "sha256": "50F7A79D52316975A92AAC3996248C4B842EE6CE5EB41A6D0B25667B4791E336" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Management.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Management.dll", - "bytes": 1124152, - "sha256": "556085D8CDCF6D442F0F59776DE3D74535BBFA9FACF33D1358D90BB948F6BC92" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Utility.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Utility.dll", - "bytes": 1652536, - "sha256": "D156B47A70718A54D1B5691D0FF58663D11270BC94911D870AB2895648C1057D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.ConsoleHost.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.ConsoleHost.dll", - "bytes": 603960, - "sha256": "456BE117016C7B7E474C66E48439C1917D4E9717C2EE247B6DD4E19B4B7A674A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", - "bytes": 231224, - "sha256": "9EF54EB74EDA1C44BB1E7B71218DE2887A0490A526A3BAC967DC319ED3A74D3A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll", - "bytes": 993048, - "sha256": "D0CAC79C0CAFC557A981B1B7F3AB79544E764056B5CAF9EAC16A0095009E421E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", - "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", - "bytes": 4904, - "sha256": "04EF1B37822130AE6AC35D2CA9CB5224C87FBEB3AD2B0C48F953C87B083CC26C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.MarkdownRender.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.MarkdownRender.dll", - "bytes": 55104, - "sha256": "08A7A5C37E857A0197C710B8D9B52A4269AFE12F00374C077389E3C7DBD584E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.SDK.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.SDK.dll", - "bytes": 142624, - "sha256": "8637EC9E121C43B3280ECAB0AD12DDC76424AE08EE6A84A47F10548BDF076232" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.PowerShell.Security.dll", - "source": "vendor/pwsh/Microsoft.PowerShell.Security.dll", - "bytes": 345904, - "sha256": "2944F4A3EA39EE33FA783A751037CCC7DA4B4A6AEC57DB7F652FEB544BE5D082" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.VisualBasic.Core.dll", - "source": "vendor/pwsh/Microsoft.VisualBasic.Core.dll", - "bytes": 1193808, - "sha256": "788558DDB1196D021FE17CCEF9DA03CC1C8C95174CC7A6CD56D0049AE65C2227" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.VisualBasic.dll", - "source": "vendor/pwsh/Microsoft.VisualBasic.dll", - "bytes": 18728, - "sha256": "4B8D9F34CC136D4F5FE156D3C0CCFA526231D042BE68DD0F412388EF7E0191CE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.VisualBasic.Forms.dll", - "source": "vendor/pwsh/Microsoft.VisualBasic.Forms.dll", - "bytes": 247592, - "sha256": "44A8EBEBEDC5746BBC22411BF34A5C869E3428908DB2D12B89270D1C762264FE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.Primitives.dll", - "source": "vendor/pwsh/Microsoft.Win32.Primitives.dll", - "bytes": 15656, - "sha256": "0327272BF4555EBD266751DFB3AC42463C8C0F266CDFD352263D4C7AA8FBAF04" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.Registry.AccessControl.dll", - "source": "vendor/pwsh/Microsoft.Win32.Registry.AccessControl.dll", - "bytes": 34600, - "sha256": "EA85FD3A01751393A2C4EB8D816EE50A0443C96C91CA2AEAD1F2AA15C52939B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.Registry.dll", - "source": "vendor/pwsh/Microsoft.Win32.Registry.dll", - "bytes": 116520, - "sha256": "FAD9AF3A76A4B89BEE820381FDE9CE06DFAADC3D90859953503F4BE81C74EE31" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.Win32.SystemEvents.dll", - "source": "vendor/pwsh/Microsoft.Win32.SystemEvents.dll", - "bytes": 116520, - "sha256": "75DA025453C94EE0D959A8077ADC2A69B05E28B987BC5EDF93887DF6C1CB393E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.WSMan.Management.dll", - "source": "vendor/pwsh/Microsoft.WSMan.Management.dll", - "bytes": 542512, - "sha256": "B95BB3EBAA7F562BA3EEA65B78429E267819D65A71F5839ADFD498EA695BB09F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Microsoft.WSMan.Runtime.dll", - "source": "vendor/pwsh/Microsoft.WSMan.Runtime.dll", - "bytes": 157472, - "sha256": "D51427C022C1CEB37DE10991BD712F491B15D9BF8E0D42A3270ABAC050DFA0E3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", - "source": "vendor/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", - "bytes": 15219, - "sha256": "5417424A8746E0024526C21D09DD309F8B04B25BE711F99DF4F1507E8F335D70" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", - "bytes": 12873, - "sha256": "1C4EE42E1FA99A2768FA8FEDDAADF70A84F8EA8F273234061A6781D41DA0FAD4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", - "bytes": 17109, - "sha256": "15C2C0874890E5C715AA513C744CA99803A2629466CBB3C62E41AE3CFC857D66" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", - "bytes": 14859, - "sha256": "95E193E52B66E19C8ACD007AB2958B5020B3FDFF52227162B09C5110F01E2685" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", - "bytes": 77022, - "sha256": "48F095C3A48F95AAB0714C06A6EFDB798C77F957948AAF02CAA16CF50692D2F7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", - "bytes": 18243, - "sha256": "01EE5A86A4E774DBBACD911B7CF9070C72C6EF47699C1B94D70C8DF5FA9F0FA9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", - "bytes": 20849, - "sha256": "BD36F3F1242E75599579D6E3F805AF77663767C4DBF3750413FC6794B2EA5D12" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", - "bytes": 20298, - "sha256": "DD35B80DA89FF364BBA431A26223DE85252B3320C06D6489C5ECAE33A4EA8320" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", - "bytes": 14887, - "sha256": "F511D03F0A312CB4C74D1FFAC6E777190A37FDF057388F0DA3AC0DE8CE9CCEFF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", - "bytes": 14721, - "sha256": "BB209A688D755B76351364FE9B4ABB224F5BF881D9281383C7D0A62ACCFFCE63" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", - "bytes": 16092, - "sha256": "4BFCB08F3F388A74BE6DA9B765585974BE9AA37EE4EEBFFE52FC1BF762C25DB8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", - "bytes": 12873, - "sha256": "882CEF83D74AD2226EC51479280913B0B6C919DA35BC7365438D6EC540FB1018" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", - "bytes": 429624, - "sha256": "0C85C8CC3856D5BEB91895D1A8E00F785453F2AA538D85EE9ACE030B537F0FC2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", - "bytes": 357944, - "sha256": "B960AB88C55A7EDF0FF7A659CBC88FDA2DC8219221D46B7FCB92245B8E8EEFB4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", - "bytes": 26696, - "sha256": "FD587EF95461251B8D4B3D493D77102120622B1FD32B87BB98E032E1365480A1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", - "bytes": 22048, - "sha256": "FA1FECF3C081A0EF366100169CD539AD03EE10CD18BCF89DE56E9CB8D3659D4F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", - "bytes": 45088, - "sha256": "152BEBA2D6F82210F17696B2BE9498ADC9C38D32B9E2FD03BA87810B838F811D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", - "bytes": 44064, - "sha256": "7C6BC24A7D6A1C3C1FB4FC521174CF43E931A19F8BB88754FB738A6402848E20" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", - "bytes": 1664544, - "sha256": "924340C47CBC74B1B811059860DEFA3B136500BB97EF8B6DB95631F2391741D0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", - "bytes": 65568, - "sha256": "B084586684B03456481A6AA0AC0B149F87EBBC6FB01735D8005F987031BD0343" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", - "bytes": 18976, - "sha256": "7A77F7894D7F925C2E806737B8F4F036CD0D8B7E7843F824BA1139C10A0B3E25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", - "bytes": 732328, - "sha256": "1DC3D1DB10368906573D443667CED170FD7A01F3349D93906A8B60F1D914B6BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", - "bytes": 613824, - "sha256": "762988B531247A8AC282B85E0BC81004FA9D02B9118A24C3B18B3A97F1DADE48" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", - "bytes": 122800, - "sha256": "E8932AC0C5D7E306551BC4B585F582BAEF7651F21C88D4976445C0BFDF29F227" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", - "bytes": 170424, - "sha256": "0BB37C45575CBCCCC279A4250FB7F0D87DE5C0A3DDC16565542A6ED0E5682C6B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", - "bytes": 67616, - "sha256": "E5F44DDC086A8F40989C70460E22E25095D75AC402FAF6FF436E6A08C9CEC71F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", - "bytes": 93728, - "sha256": "077951FEA107ED42180AA5EAAA0BE047070A0EFF32A62999A8280535CB5D4F3D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", - "bytes": 131104, - "sha256": "C79E07959989FE7E99F31171EF712F9665474E652B4C16BC729160114697F8D4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", - "bytes": 56760, - "sha256": "EAF71D1D5DD771DAC1510C8779C2FDC98D539C8C07934EE114679D453FA60C21" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", - "bytes": 689088, - "sha256": "877E82582D7802ACA0B2FD53D34175B42D01BCF713A77D6D0A9E8A73C47EE2A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", - "bytes": 219056, - "sha256": "275A531DE109D62F71D745DB656D295511DD53CF0FD141CC0153AD357BFC8163" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", - "bytes": 786368, - "sha256": "AB3FCF87ABA83AC429FB040D5C36A4B7E9AF334C923F8F27041C93046D2AF9CB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", - "bytes": 65984, - "sha256": "77B3FAD9D3D369538F765035DD142D3D91785CE1308C40C5CA13BF84C9E78ECD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", - "bytes": 41240, - "sha256": "11568C79856612F9B5AF07FA8EF0A38E524F912D98C188E2510FD7E5ED520EC3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", - "bytes": 189104, - "sha256": "1887592615FC08BBC91AD62307C8C11DFC14C45BF120D271D66BE4A0D1A149A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", - "bytes": 28552, - "sha256": "E6026501AD4056FF2F1655B0AFDFE8923BC6E8FBAD67E1E9EF56E3002F49FBB9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", - "bytes": 31016, - "sha256": "6CF06482F4FD82AF659C2EB4B7BDEAE51EC171AB1CA3B3CBF0328C6A4B882242" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", - "bytes": 162624, - "sha256": "2728C8F0D153C72D3F50363FA1762536675C303F4CF4E7502EE3F099B3474554" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", - "bytes": 126152, - "sha256": "E5B1E3DEFA97A3AA29CC20BE0C9D525C86BB9C716F3B85F9A82CE1F30FF6310E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", - "bytes": 38424, - "sha256": "2154F3A7C647BD58C9DA47650E10CC42F906B7F7F1080211023917CA9684839F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", - "bytes": 33672, - "sha256": "1FDA491EEBDB19EA0A83CF6C16AB5DD004A1BFDFC845EDE017EBE0945BEB927F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", - "bytes": 23696, - "sha256": "A79AD1E9E7205BD790F40D6C1900BAF1A9C8CAA06089878D24ED3599AC3228A9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", - "bytes": 18312, - "sha256": "B4D8E15ADC235D0E858E39B5133E5D00A4BAA8C94F4F39E3B5E791B0F9C0C806" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", - "bytes": 99408, - "sha256": "A58026527BED9F3098CA3D016A7B6C93BD6F0134092F484B099970EC3D839AB9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", - "bytes": 644904, - "sha256": "0CF5A9763C98F09E94DFCDAA4437E9508EABE2E50B408BA421D5FAF734C5556A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", - "bytes": 25984, - "sha256": "4F81FFD0DC7204DB75AFC35EA4291769B07C440592F28894260EEA76626A23C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", - "bytes": 25232, - "sha256": "E905D102585B22C6DF04F219AF5CBDBFA7BC165979E9788B62DF6DCC165E10F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", - "bytes": 17567, - "sha256": "7429201A7A4BE49FEB0456DEDBD47E9E753DDA4EE871A8E6B977A0917F96745D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", - "bytes": 1095, - "sha256": "BE5723395260D4F07283D596F4B375810EB695653D48D52A5062C0F7070573A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", - "bytes": 433184, - "sha256": "C9ECCB0E5313CED30E7DA3903C27F1A97C6E8B0793A0A91160C4BEE346F3EE02" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", - "bytes": 138856, - "sha256": "A8890484E98D618F366A5AADCF34F9AB8D9D2C51AC08E7E4789E7C733D62B653" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", - "bytes": 20644, - "sha256": "13A0C3EA11F35E08DD1EE06A15EE9EE793062752EF5DA63B43E7C4BC64976328" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", - "bytes": 17978, - "sha256": "303A5B8DD3749DA6A7530E2F040DDC1ECFCCA280120DCEDA473EBB2285661CE8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", - "bytes": 158819, - "sha256": "CB3714C1F7FC609FEE9264C5F7E87DF89121D09242A4DCCEFE4815E9D784AA65" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", - "bytes": 23783, - "sha256": "097FE2BBE4C6BC7829C64114E46E51479574B5B9E4B2F84D148F29B2FAE0BE9A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", - "bytes": 1364, - "sha256": "1C8163F8163797C4B91466A94207E356256B8F770A0C591C68CD1FADD015A79A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", - "bytes": 1839, - "sha256": "37BCB0428C05343483E7B5AA2F76AD3F804BA87830526EC42E8F8A91736D4AA9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "bytes": 15415, - "sha256": "9CBBDDBC38465378D4FDF3529B74C39161B728EF295974B5B9D90FD5A87806C3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", - "bytes": 20141, - "sha256": "B226CC25C17CDD19CE028EA22B3B596EF9A3A8C8E3C58BF3A9E76E45D9F38514" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", - "bytes": 12812, - "sha256": "3DB2F058A8F27F3E7BE0E22E8258944F9A6412A379DFFC66F99F3E592899DEC4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", - "bytes": 19596, - "sha256": "6E5AA422BFDE0133D9FC12CAB32C404557CA7CDDDA1FF888BF69395595928003" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", - "bytes": 1095, - "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", - "bytes": 34840, - "sha256": "F89AB468EC1329474288FB0FAAD81600005BE3DA88DC2B8A2631CFBDD64F394A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", - "bytes": 18088, - "sha256": "4E9CB7A6D8A94560F0025B807162BAAA9C4A6AF4254D3FA7E71B23E0DCC1FB25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", - "bytes": 1979, - "sha256": "296FF3E4A93BC8C20CA5C20D0E1A70B8C4622FADF0351A37845A63ED52585279" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", - "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", - "bytes": 16830, - "sha256": "BB9BD6F3EAB1CF227E1AFAFEC89934E6EB10C6C8530908C6436849120F631D9C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", - "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", - "bytes": 15033, - "sha256": "8E06AA0AB008337C59AEBB32FF672BCD1646FA927DDF4E4EFE82AC454AAB30B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", - "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", - "bytes": 24000, - "sha256": "6A47415133889D1D676C6170DD14DC43C2C983DC6ADAFE73DC0F66891E5150FE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", - "bytes": 89504, - "sha256": "5A140EB9BFB2EBB42F02E75DA9A408A86BA6034EAA27AC57A10A534011EFD509" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", - "bytes": 72080, - "sha256": "50CB7269866EC2E3663221662FD1649A0C1B2736525D65231E29B39B25BB2467" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", - "bytes": 318352, - "sha256": "23689DD357312F0924305C612AA41BE0BD592AD9712FED70A3DA72A9C40EB712" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", - "bytes": 83864, - "sha256": "8F5BFD7FFFFE4930AD035194447B8FB24257C8C799302F04879CD5D5211A38C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", - "bytes": 290192, - "sha256": "C0579B5BCA0ADFE99DC17C253F4F00D669AD008A60854D2508BF6D79A886B4B2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", - "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", - "bytes": 189328, - "sha256": "10D3F4E3AA47CFC3AAC0E94C3B20000EA73CB0062E5037A79FCBE932112EB0A1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", - "bytes": 28469, - "sha256": "B7CD11228923E8747B04AE8A03F246F1490B4D51C8A54B41D26CCB20164FE558" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", - "bytes": 31676, - "sha256": "8D9E6819AF5265B47DC514F82EEEF657449C2F3DC72CE4F4F8531FC2FCCDECF0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", - "bytes": 31412, - "sha256": "50EE9087694A8325E0BC6FF4E5AE008984D46FF84CB9BCC6ED655CFF1A8547A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", - "bytes": 53524, - "sha256": "56BD33242E6D59F02514EB3B902F65B83A3EA8EA7E44099F57892AB640823C25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", - "bytes": 31566, - "sha256": "ACD658401A6A84F29E091F6549F4AD6BC6E16B71005F542E4BE558C40B50EAFA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", - "bytes": 32494, - "sha256": "AB2DD00EE1AB028EF4C506CAC98E5407FB866DA40720C232A3426ADBB57EA67A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", - "bytes": 24136, - "sha256": "CA8EA0F3E2208FDEDD51C2F282EA9E9A50BE7D8A38C56EEF0141191F7E484776" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", - "bytes": 31104, - "sha256": "24E1182E6F6E3EDDF2528EC6D335E0D584CE37995B0D849B5DD5F5EE75A558BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", - "bytes": 32443, - "sha256": "6CC6DA35A87357E213FA7A2CD0FAF9B1DB1BB83BDAAF9F73C952FE9AD2BB4596" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psd1", - "bytes": 17889, - "sha256": "EC633E756164C26D1154FAB722648E2737F3650399856D29C089F0C25B190D33" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psm1", - "bytes": 16422, - "sha256": "D484E31593FE26C8E670F8A0D72D4400A3E8EB75F587DF4B11554B8C000E128A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", - "bytes": 14984, - "sha256": "734BFFF81999A08B4145399A6284B00E46F070A7038BB4C82AB79165FF15F543" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", - "source": "vendor/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", - "bytes": 25115, - "sha256": "481FDED09BF8A376EC4BD53CF34C0D66C8D8B1D31AB2012A768227F92B099156" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", - "bytes": 15939, - "sha256": "4081ABD182F805AB3B9705279478A2A8AF17A0E43F07887E3C68962BCD09C9E8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", - "bytes": 38882, - "sha256": "6C724F3B86D5D5233C2906FE41206D6EB2279B6D053164F51AAF83B4891619EB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", - "bytes": 1766, - "sha256": "CF126CC6FD7AFAEC54F07942F346F379725795625B80EC5FC32F547AB7B49BFC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", - "bytes": 14446, - "sha256": "2107051FAB99F1E8545CC30F72FB089899B19A5BD4D33A6891FBF0DF794F573A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", - "bytes": 14171, - "sha256": "7520000BE81F281384AF99B787A4629E55269EF73D8FA162CF29390DC273A017" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", - "bytes": 23645, - "sha256": "627EDB8150EC8C52B33E363652D27E4FD71C1374D03C5B56D98AF244D3C1CC10" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", - "bytes": 1578, - "sha256": "B9F9A62A3D2D0497BF81CC69A456B9E0BDBF9D1F34909A0283F088940EFFFC34" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", - "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", - "bytes": 14325, - "sha256": "6AA312F0A73494723FE4CAA98BD74CCF3724226BEE4BFE4EC0A7229BFAD7141C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", - "bytes": 59328, - "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", - "bytes": 20118, - "sha256": "14A35134461ABCFAA7DFE393C7B3A16605032719F5EA0EE062B4655A181C0C76" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", - "bytes": 15108, - "sha256": "1302F09514E514B4A2C5A2F8D4DFCFC5087070E211357319FB516E27994C88B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", - "bytes": 27387, - "sha256": "640240A2632C22E75AE372414BEE7136C2ADC932F92A0F656C436BE616B19CDA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", - "bytes": 23683, - "sha256": "B1315712ADE77AF6C5115D8C9AE2F9A5D8CCBFA5E9A5F00C19AB283A52057F2B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", - "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", - "bytes": 23680, - "sha256": "4F12DD41E0FAB7B1A97AE83A84BDDB567CE9876F9E6C3496D833CFBB8E52BD7F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", - "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", - "bytes": 59328, - "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PowerShellGet/PSModule.psm1", - "source": "vendor/pwsh/Modules/PowerShellGet/PSModule.psm1", - "bytes": 1368306, - "sha256": "573D4A3861CB967E52EAF6155A41F930FCD4168368A6946F65D985F30DC357EC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", - "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", - "bytes": 14978, - "sha256": "1DCFF05CD03BEC2F6117BB7267DADDB9770CB10FA6FE4AB9D892E87DA15349F7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", - "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", - "bytes": 26145, - "sha256": "2697A9EC7792940DA5F3D91CCAC24B1A3948CFF4E5901D5B98B7689A1C4A9DBF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/.signature.p7s", - "source": "vendor/pwsh/Modules/PSReadLine/.signature.p7s", - "bytes": 12873, - "sha256": "ECDF2C3BC44D32AF003FD81BEF96E83F19D7EFC9658C07F7D78687F105F721A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/Changes.txt", - "source": "vendor/pwsh/Modules/PSReadLine/Changes.txt", - "bytes": 41347, - "sha256": "E18C4D35A40A544877378B36B7D86B5C79DAED0C6E7A55C3027C4F0F998426C0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/License.txt", - "source": "vendor/pwsh/Modules/PSReadLine/License.txt", - "bytes": 1324, - "sha256": "25C2FDFCDC653F65629233F5EF217A83287733D3C0299C207B0BCA865508C463" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", - "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", - "bytes": 16784, - "sha256": "451994C3D3E38D939B4FA5F8594D0207840F45E7A210AA75F81AEF85AA17C592" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", - "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", - "bytes": 339528, - "sha256": "F8E3A5B7E3E8CAD2130CE10647564A2A0EA15D98DB8A0CC8D589F80154C108E2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "source": "vendor/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "bytes": 14920, - "sha256": "6D33EA289B405C64BF5DAA9C59E459596CAC59ECE399D0D215EE9F3D46AFD675" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "source": "vendor/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", - "bytes": 16928, - "sha256": "2E63DC86D9240243CCFB07DA5D0B4FCE2BC5CE5F9952658A7CE68C26BCD3E14C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", - "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", - "bytes": 26820, - "sha256": "1CA887463598D38AA18236A97A4193B2228D1D8434FA73CC8C7932F42A0565CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psd1", - "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psd1", - "bytes": 15471, - "sha256": "DD8766BD4DB0C1D17111B81D145D32448F61F48451D51C780A79BE9CA98D837E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psm1", - "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psm1", - "bytes": 15076, - "sha256": "9BC0252F616067CA5EF1D0328EC0B97C7D31C757F9FC80ADEE234EA6132A9C7E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", - "source": "vendor/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", - "bytes": 41594, - "sha256": "53B518B3D72AFEB6C8A3F4ABF079C93CEBAF7100A12C93668B83A4D4BCD45795" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscordaccore.dll", - "source": "vendor/pwsh/mscordaccore.dll", - "bytes": 1366744, - "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", - "source": "vendor/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", - "bytes": 1366744, - "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscordbi.dll", - "source": "vendor/pwsh/mscordbi.dll", - "bytes": 1247992, - "sha256": "1875AB683E30F79B39FF861F440A246561B72247C22366615B97CC14537B4808" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscorlib.dll", - "source": "vendor/pwsh/mscorlib.dll", - "bytes": 59688, - "sha256": "E7BE6C67FA82F5F0C10C6553D82CCC2854DF3B49CF8DA706629F80A34A1AE309" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/mscorrc.dll", - "source": "vendor/pwsh/mscorrc.dll", - "bytes": 133928, - "sha256": "11917AEAEC22F310A26A466E7D57CDCAB40F6ED1233B286E39AC675E881A7DAE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/msquic.dll", - "source": "vendor/pwsh/msquic.dll", - "bytes": 536344, - "sha256": "B7158D427B01C70483BB3D80C5211E387DD98E9471C8A70E4484D449DE9157E8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/netstandard.dll", - "source": "vendor/pwsh/netstandard.dll", - "bytes": 100648, - "sha256": "42388B4A19485ADC2A7D384F5257BB9D806F3BAC6F8871DDE42AFD944BBFC0C5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Newtonsoft.Json.dll", - "source": "vendor/pwsh/Newtonsoft.Json.dll", - "bytes": 1824584, - "sha256": "2B6FDDE54CC2D2E9277D458A42FDF9FCA30FC4299652E97144A31C90FEC91829" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PenImc_cor3.dll", - "source": "vendor/pwsh/PenImc_cor3.dll", - "bytes": 157992, - "sha256": "962CC2EB9DBBC45680649AC084685FB0E57DA4DE09D3DD5762188D10B76F598C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/powershell.config.json", - "source": "vendor/pwsh/powershell.config.json", - "bytes": 190, - "sha256": "98C0E5B6EE17EB8B8F4E4940C2B2528689CEC8470DB4BDE427AD16D90D6A52D4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShell.Core.Instrumentation.dll", - "source": "vendor/pwsh/PowerShell.Core.Instrumentation.dll", - "bytes": 106808, - "sha256": "AEEB8062AAFF1057E90E9485F610B508B1179AAC4DF7FC465090BDEE49E3CFF3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShell.Core.Instrumentation.man", - "source": "vendor/pwsh/PowerShell.Core.Instrumentation.man", - "bytes": 227126, - "sha256": "8B2585E50EDE697905DFDB8E3878DA2AB42039135BA23E377207CC8D96E8125F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.adml", - "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.adml", - "bytes": 9675, - "sha256": "2B8C4B7D5BD585431FBF918F223A9B932D80EB9703A1976A1CE3492D4DD26966" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.admx", - "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.admx", - "bytes": 6198, - "sha256": "EE8EB99C1E66009D95AF46E4F6C8F5C13563C9A0AD827E631149176A1B8D34D2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationCore.dll", - "source": "vendor/pwsh/PresentationCore.dll", - "bytes": 8324904, - "sha256": "7A610FD470016E5C74E2BFC403D2E08DDFF10F1EEFCBBF3AA7B07577855EE746" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemCore.dll", - "source": "vendor/pwsh/PresentationFramework-SystemCore.dll", - "bytes": 38696, - "sha256": "12DDD4029181D0AA27D32A69777760037A85F06BC57D56478669E098417E9990" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemData.dll", - "source": "vendor/pwsh/PresentationFramework-SystemData.dll", - "bytes": 34600, - "sha256": "0075922DB11FDE4BE1E8668BB9ABF5596DA8A40367E1D7264FE44B30B83F2228" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemDrawing.dll", - "source": "vendor/pwsh/PresentationFramework-SystemDrawing.dll", - "bytes": 34640, - "sha256": "953D322E29718C113DB240EB73396E65BA5B9AA55AD25C8A796B7BEB2E11573E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemXml.dll", - "source": "vendor/pwsh/PresentationFramework-SystemXml.dll", - "bytes": 34600, - "sha256": "EBDDF88BDA059E766772C7F3BB073C4660A48FE7CAB1238B971FE6DDBA4C01CA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework-SystemXmlLinq.dll", - "source": "vendor/pwsh/PresentationFramework-SystemXmlLinq.dll", - "bytes": 30504, - "sha256": "8D5290BF6787A161D2689C03C1936BC9D28CB22292197B2EED3A57ADD05DC81A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Aero.dll", - "source": "vendor/pwsh/PresentationFramework.Aero.dll", - "bytes": 440104, - "sha256": "D64069A4E059D5F74EFAE734541A5BC54B13E00A087B7D7A0CE224B4F9F7B3C3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Aero2.dll", - "source": "vendor/pwsh/PresentationFramework.Aero2.dll", - "bytes": 444200, - "sha256": "092E068D4F0117CF5020A072A53A3010EEB54F125DA3BC8D4EA1913A3370E4E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.AeroLite.dll", - "source": "vendor/pwsh/PresentationFramework.AeroLite.dll", - "bytes": 239440, - "sha256": "B7B8BF2214F9653CA2257D1A5E1DD0D978FAF15BFDE826F0ED3EE57B6A1A9A90" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Classic.dll", - "source": "vendor/pwsh/PresentationFramework.Classic.dll", - "bytes": 272168, - "sha256": "CB733EA566618B52415EB32FC32C86A3FE6BC915D471D63DED3A46AAF3071AEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.dll", - "source": "vendor/pwsh/PresentationFramework.dll", - "bytes": 15816528, - "sha256": "6A3C5B98ED693C577FF25827315D7EEE89F73DEDF0088B80B43B4AFB337018C4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Fluent.dll", - "source": "vendor/pwsh/PresentationFramework.Fluent.dll", - "bytes": 980816, - "sha256": "E33301B7F7A38B9A5065127F79EC3860A6827B30BD1C85A669E3C3AE191C4258" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Luna.dll", - "source": "vendor/pwsh/PresentationFramework.Luna.dll", - "bytes": 669480, - "sha256": "F9030C4D064B7F097B6F73FAA9C7683BEFB7A1A4C18C2D6DD741EB34A2325658" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationFramework.Royale.dll", - "source": "vendor/pwsh/PresentationFramework.Royale.dll", - "bytes": 333648, - "sha256": "118F34B2213AFE8075BE78D66FDB5B5B0C099A89F70AEE9CA09A9DC09BF07102" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationNative_cor3.dll", - "source": "vendor/pwsh/PresentationNative_cor3.dll", - "bytes": 1235240, - "sha256": "42E2E543C17E1B37499BAF00D956433B8AFB655348EC0FAE89C926FB7D149B6E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/PresentationUI.dll", - "source": "vendor/pwsh/PresentationUI.dll", - "bytes": 1259304, - "sha256": "5DE46036ACAB98C8B970BFB4234F66438EE102039F9A59A72053A9818C3ED1F0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/preview/pwsh-preview.cmd", - "source": "vendor/pwsh/preview/pwsh-preview.cmd", - "bytes": 25, - "sha256": "7A24CB96B62009CFFB38C85B6ACBF48A5DB5746459B85FF3E727BD952FFD8832" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwrshplugin.dll", - "source": "vendor/pwsh/pwrshplugin.dll", - "bytes": 159032, - "sha256": "FBF2C8FC0D8A3CD5F2DEE9E0F15315F92862B6DF26BF1B02209588A07CC0D429" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.deps.json", - "source": "vendor/pwsh/pwsh.deps.json", - "bytes": 99499, - "sha256": "DFC3A3FB00F53720610F389E2525FB268928B381E55F899B84B478502D5736A2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.dll", - "source": "vendor/pwsh/pwsh.dll", - "bytes": 157472, - "sha256": "91F9892F15A7E2DCF440AFC3A497A7D839F9D38D1AB4A50BD36080D982A01449" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.exe", - "source": "vendor/pwsh/pwsh.exe", - "bytes": 301368, - "sha256": "BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.profile.dsc.resource.json", - "source": "vendor/pwsh/pwsh.profile.dsc.resource.json", - "bytes": 5024, - "sha256": "378030311FCDD7A8D6C401142275BAEB6C031307EC2E12F51399B84851906380" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.profile.resource.ps1", - "source": "vendor/pwsh/pwsh.profile.resource.ps1", - "bytes": 20443, - "sha256": "BA90CDBF60C48C05343A7C4A451BEC2A5A133DEB10F1F7CD763E76F5D2BA7944" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/pwsh.runtimeconfig.json", - "source": "vendor/pwsh/pwsh.runtimeconfig.json", - "bytes": 790, - "sha256": "6C1779B71C67B5F40B2D0041BACAE0873320DD55F0DC3D5DF76D88C8D486275D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ReachFramework.dll", - "source": "vendor/pwsh/ReachFramework.dll", - "bytes": 1570600, - "sha256": "E0EE50A412ABA6DC2FAAD0655D5AEBCFF33587A113984CAC2777ED7B763BD193" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.CSharp.dll", - "source": "vendor/pwsh/ref/Microsoft.CSharp.dll", - "bytes": 18216, - "sha256": "AE0ECD8F42EA3EFACC2612ADE56F29FEEA9A123FCE9FAA455126EC9F4A4540CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.VisualBasic.Core.dll", - "source": "vendor/pwsh/ref/Microsoft.VisualBasic.Core.dll", - "bytes": 59216, - "sha256": "7A7E44D7AC1F870E0B7D8F3F7598CCA79E35CAB12E77C58ED46E9B6B3C78BCFB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.VisualBasic.dll", - "source": "vendor/pwsh/ref/Microsoft.VisualBasic.dll", - "bytes": 17192, - "sha256": "E192508ADDE6A758138A5A8E28998DCCB64E7F45EAF30D8AB3564F87749028B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.Win32.Primitives.dll", - "source": "vendor/pwsh/ref/Microsoft.Win32.Primitives.dll", - "bytes": 16168, - "sha256": "AB197DEC717C52F5A72C8F2366C33E2FC5149BFA7CF49ACD59BBFB563DEFCD05" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/Microsoft.Win32.Registry.dll", - "source": "vendor/pwsh/ref/Microsoft.Win32.Registry.dll", - "bytes": 21288, - "sha256": "E8EB7F780B9CECA6DAE35E74A11FA85E26A1B8B4A7EDD5237CCCC6B63B4E4B7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/mscorlib.dll", - "source": "vendor/pwsh/ref/mscorlib.dll", - "bytes": 55592, - "sha256": "894D95D82126559637417B4F14C48ED20BB1168B13A0E65BD1337CDC2A981D1B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/netstandard.dll", - "source": "vendor/pwsh/ref/netstandard.dll", - "bytes": 100648, - "sha256": "3DC81DBC9DACD28261632C94410859EDA24C2DB50CD6C14309CBB2A434B50C57" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.AppContext.dll", - "source": "vendor/pwsh/ref/System.AppContext.dll", - "bytes": 15144, - "sha256": "D9DD9FA44E019F27172D6B2662E2B6038F48C270912AC8AAE53D4D909DA7E9BE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Buffers.dll", - "source": "vendor/pwsh/ref/System.Buffers.dll", - "bytes": 15144, - "sha256": "A1A35F5524E2F831D08CA55F702FBDDCB8C6DFECD264F0F563D81A1C8B8280BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.Concurrent.dll", - "source": "vendor/pwsh/ref/System.Collections.Concurrent.dll", - "bytes": 27432, - "sha256": "27B446A401163BAA325F9CD82F787DFD32D0E205011093E0AB3057E5DA609555" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.dll", - "source": "vendor/pwsh/ref/System.Collections.dll", - "bytes": 55080, - "sha256": "130FCDD915C3FF8D8279FEFD9A198B784D37693F4C1E96D1F2CFF3EDC96B638D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.Immutable.dll", - "source": "vendor/pwsh/ref/System.Collections.Immutable.dll", - "bytes": 75048, - "sha256": "D4145735395753187965A6F0A8BF6BB6F301563C4D4080249614B8487B0604AC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.NonGeneric.dll", - "source": "vendor/pwsh/ref/System.Collections.NonGeneric.dll", - "bytes": 22864, - "sha256": "99ADF08652A16E5AF30797B98742851B94604AA44BAB677DDC38C7826BD529A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Collections.Specialized.dll", - "source": "vendor/pwsh/ref/System.Collections.Specialized.dll", - "bytes": 25896, - "sha256": "17EF2B9433A1F1E9FC956786067F8D60672437D3FEB76AF84175F2B3BEE4DED1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.Annotations.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.Annotations.dll", - "bytes": 31568, - "sha256": "43C467051FEAFAA27144CC672836F38108377439245833FC8D99DE97D3667C37" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.DataAnnotations.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.DataAnnotations.dll", - "bytes": 16720, - "sha256": "D40E8C7062E6B9CE11BA83EAB3D26BE0EEC8066B99970EA88B8E87FB75F411BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.dll", - "bytes": 15656, - "sha256": "8811E7CC4288C435F54B0350AFBFFCBB13F77618CF9C0D3436CAD89A83D29B23" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", - "bytes": 19240, - "sha256": "B96D6D2F159EE5E192B2406A0EC993C8AD470A847F2E7ACD2AA92CBC0A4C6A58" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.Primitives.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.Primitives.dll", - "bytes": 25936, - "sha256": "09A575C38BD010F3131704E9294941FF9740A0BA70BE0FD6AB8FA54C44DBFCA7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ComponentModel.TypeConverter.dll", - "source": "vendor/pwsh/ref/System.ComponentModel.TypeConverter.dll", - "bytes": 104744, - "sha256": "98A3E34140052C828AB7BDB81040421616FA4E6356C27F7970DF141EF5681B42" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Configuration.dll", - "source": "vendor/pwsh/ref/System.Configuration.dll", - "bytes": 19240, - "sha256": "3952A72E1F18774F16952B46C20E80ED2BC601A06ECA01F33FA924C738022A9C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Console.dll", - "source": "vendor/pwsh/ref/System.Console.dll", - "bytes": 26408, - "sha256": "783E5EA6D049218EC30316598654E031415F0D1EC6CE6932C2D1A9CBD97A77E0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Core.dll", - "source": "vendor/pwsh/ref/System.Core.dll", - "bytes": 23336, - "sha256": "AC23FAF1B37FA49F1B117BAAD951A65609E799A6E669A6DF06D3552677E0C086" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Data.Common.dll", - "source": "vendor/pwsh/ref/System.Data.Common.dll", - "bytes": 154448, - "sha256": "E563226DCE5E8D2F1ECF24B13A37D8948E42208D7B93BC9812475074382F2303" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Data.DataSetExtensions.dll", - "source": "vendor/pwsh/ref/System.Data.DataSetExtensions.dll", - "bytes": 15656, - "sha256": "AEEAE0755C8C7F6747765CDF82FA43182E320E97F2035E3A0A19F21905E6C113" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Data.dll", - "source": "vendor/pwsh/ref/System.Data.dll", - "bytes": 23848, - "sha256": "8BEE1D4675FEE975B5454E7407827AA3C6D4A2E53B9EFB40BE5409AC50900EDB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Contracts.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Contracts.dll", - "bytes": 19752, - "sha256": "4366CCD79A875FB64E38813A81BDF2F61915B4C838CB74572D87919C2E7DEE41" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Debug.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Debug.dll", - "bytes": 15656, - "sha256": "8B160FFE7518588BF64E13C01C6DCB2EF504D64F38BBF0F198D31E7B698DB92C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", - "bytes": 43304, - "sha256": "6CAA24C3FE3819A6F78AEDB0E8F718A6BD83EAB4D9A786C609A228E330557BB0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", - "bytes": 16720, - "sha256": "AE73589F3288FC3648A718797361F0AB37606CC18667C8AAEBEA2787F62736A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Process.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Process.dll", - "bytes": 31568, - "sha256": "CD121308CB25B34EBC2549D72DA361E56A093B29EF5AB0A85870D13FB91DE052" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.StackTrace.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.StackTrace.dll", - "bytes": 22824, - "sha256": "E287630ABE5753B658E64E947616C8BC8291BCD27BFCADC745A33818A43AD30D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", - "bytes": 17704, - "sha256": "2C567AD9A2EFE67D171E0AB06D0C7035383508588CAE62B3C3D08F579A064595" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Tools.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Tools.dll", - "bytes": 15144, - "sha256": "841C5E2B6C171FA9AAFF0FC6295F3FBC93DC5DCAB44118B62F1C942BB674AFF2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.TraceSource.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.TraceSource.dll", - "bytes": 27984, - "sha256": "ECFE5B354EA6B7BF3736D8C4B02AE322C766F2BCCF260182632A492F3309D883" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Diagnostics.Tracing.dll", - "source": "vendor/pwsh/ref/System.Diagnostics.Tracing.dll", - "bytes": 28968, - "sha256": "E0F72905EB1B1CFD6DB130E6B2F18424788769D79FAE486054A242615D713A50" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.dll", - "source": "vendor/pwsh/ref/System.dll", - "bytes": 49448, - "sha256": "FF11E2AB9009EF141E315187254F8327DD1E2591E44B8BF158730869028F1F0E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Drawing.dll", - "source": "vendor/pwsh/ref/System.Drawing.dll", - "bytes": 20304, - "sha256": "585A8DA9952D715F8DE7A7B1879B59D68FF8608B92F6ED1C5F0E1D251180D3C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Drawing.Primitives.dll", - "source": "vendor/pwsh/ref/System.Drawing.Primitives.dll", - "bytes": 35616, - "sha256": "8FFDA23FAFB73BDAC7B6C8DFCAC1C06894C729F65157E218980DB9A4B74C7427" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Dynamic.Runtime.dll", - "source": "vendor/pwsh/ref/System.Dynamic.Runtime.dll", - "bytes": 16168, - "sha256": "C0EAC30C791DB8C1A2B50B61C50D029F4DDD0EADFDD35433745001688A3E7466" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Formats.Asn1.dll", - "source": "vendor/pwsh/ref/System.Formats.Asn1.dll", - "bytes": 26408, - "sha256": "C6F8CB70A20321AC4C93219CCB5264F50D21D7F7CDD0BD61C02360FF87CD38F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Formats.Tar.dll", - "source": "vendor/pwsh/ref/System.Formats.Tar.dll", - "bytes": 20264, - "sha256": "C6D0AA4D2E3ED3AB6C1949669D79BFC763FC9E7B04830AB7174D4CC55569757C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Globalization.Calendars.dll", - "source": "vendor/pwsh/ref/System.Globalization.Calendars.dll", - "bytes": 15656, - "sha256": "E0F3F3F6E4ACC7B6755782FED5CB075ECF70BEEE4FC5D404579AE99BC0883EF2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Globalization.dll", - "source": "vendor/pwsh/ref/System.Globalization.dll", - "bytes": 15696, - "sha256": "66902CFFD057673B4B77AA68FAF80D33FFA4C94510E61DB2A067943CC53B7702" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Globalization.Extensions.dll", - "source": "vendor/pwsh/ref/System.Globalization.Extensions.dll", - "bytes": 15144, - "sha256": "BE0390557CC72078A7217CF94FD44B9AB66AA30838C00A92AA78C62469C44AA0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.Brotli.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.Brotli.dll", - "bytes": 18256, - "sha256": "1401375A39B7BA5E65E0B49274C0168B6C8B7E5455AFEFD269C1418FD019E997" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.dll", - "bytes": 21800, - "sha256": "AC573888BA94E958243674D82E51E88692E5BEA6FD94BB68D87D30C28CDAECF5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.FileSystem.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.FileSystem.dll", - "bytes": 15184, - "sha256": "4AEEC2C0189F73BA8122F05C0D759008DDAE1A8873B167CC8A558D53F2AF80E1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Compression.ZipFile.dll", - "source": "vendor/pwsh/ref/System.IO.Compression.ZipFile.dll", - "bytes": 18728, - "sha256": "C34EF5004F535F10ADCEB66B3F20F743B15656ED4C7DCF5946A8B83A6CDEE329" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.dll", - "source": "vendor/pwsh/ref/System.IO.dll", - "bytes": 15656, - "sha256": "388400CCB189B819D6EC5F1EF70710A39641D93ED5B870FC9324866B75DFB78B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.AccessControl.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.AccessControl.dll", - "bytes": 20264, - "sha256": "38EB787FD20B30F36E207364387FD34A5C68207BAFFDDF318986216F4B6CC6B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.dll", - "bytes": 15656, - "sha256": "C251F33B29382C47B773D907463EDD9FE978A06E39EFD5670EF3B3B1D4F8C2B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", - "bytes": 17192, - "sha256": "5CE6349A752189B3BCAA553FEF38F9C0CDD1FB2E39E7CABA67C966F52EF66391" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.Primitives.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.Primitives.dll", - "bytes": 15144, - "sha256": "D3B21B8B38E48C6B4D1CB5CFE9ADFE4ABAD7B3353601FEBD3C7E4027F9EF9C6B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.FileSystem.Watcher.dll", - "source": "vendor/pwsh/ref/System.IO.FileSystem.Watcher.dll", - "bytes": 20776, - "sha256": "07FC2B07B7D53173C695E60E93F2DA4325B68C797C4351FFF571923F01F8054C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.IsolatedStorage.dll", - "source": "vendor/pwsh/ref/System.IO.IsolatedStorage.dll", - "bytes": 22312, - "sha256": "6720027218D12697D30640FB97ACDB02438649CCCB038FFDB3AD80DC27F6F736" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.MemoryMappedFiles.dll", - "source": "vendor/pwsh/ref/System.IO.MemoryMappedFiles.dll", - "bytes": 18256, - "sha256": "028850F2CEBBCC3CCB48FCEF97751522EA6455C05FF9234686A16CC088FAC5A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Pipelines.dll", - "source": "vendor/pwsh/ref/System.IO.Pipelines.dll", - "bytes": 20304, - "sha256": "08344A31693477A61B45DFF21739BF576C7482B394FAB2FA26D2D8BA87B2CB91" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Pipes.AccessControl.dll", - "source": "vendor/pwsh/ref/System.IO.Pipes.AccessControl.dll", - "bytes": 18216, - "sha256": "80FDA558926F7D7A085A6A210596000D281E4AF2FB235E4C8DC9CBEF8F1A87D0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.Pipes.dll", - "source": "vendor/pwsh/ref/System.IO.Pipes.dll", - "bytes": 22312, - "sha256": "0B7AAA8FEA2A9483857B2AC918814AD1D8FF3303C98D6EF71D4593B16ADEE9BA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", - "source": "vendor/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", - "bytes": 15696, - "sha256": "AD6A5F859589E1951103A00F45566DA079CF87BD8B03F0C6AD985A6F39F19C5A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.AsyncEnumerable.dll", - "source": "vendor/pwsh/ref/System.Linq.AsyncEnumerable.dll", - "bytes": 36176, - "sha256": "3367B7D6D2BE9D171E158B544979131E8B97328B293D6E77917234A2A7279507" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.dll", - "source": "vendor/pwsh/ref/System.Linq.dll", - "bytes": 33616, - "sha256": "9CB73D57C035503BDC70FDC422DEE5BF4F9DE97B812CD0DCB379D652C6F91205" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.Expressions.dll", - "source": "vendor/pwsh/ref/System.Linq.Expressions.dll", - "bytes": 63312, - "sha256": "772112C3F1C10FB713CCC3AD62CC313C2CAD1CBAF122EF6D25120AA7B65B0447" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.Parallel.dll", - "source": "vendor/pwsh/ref/System.Linq.Parallel.dll", - "bytes": 31016, - "sha256": "B4B597374FFEBCCF8A46793F03D5E6D5E477DFC5047157217D79449F9729E761" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Linq.Queryable.dll", - "source": "vendor/pwsh/ref/System.Linq.Queryable.dll", - "bytes": 32040, - "sha256": "5BAF83B98F103D9304EAC427C4296C9DD501D320B7B5495ED8EDE892AAF1076D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Memory.dll", - "source": "vendor/pwsh/ref/System.Memory.dll", - "bytes": 57168, - "sha256": "D7E674A1FEFF0546C9932B0FCFFA9D055976736342A4B1904F1F27894F3C0ED6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.dll", - "source": "vendor/pwsh/ref/System.Net.dll", - "bytes": 17192, - "sha256": "481B38C912F8779B3510CD00F079E45096EA22B6DEFDAAB66780D5AC3AE6986A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Http.dll", - "source": "vendor/pwsh/ref/System.Net.Http.dll", - "bytes": 60200, - "sha256": "3FF081221D0ADEB99B5CF40788E20DAB1D0D706618044B34E68A47529C761601" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Http.Json.dll", - "source": "vendor/pwsh/ref/System.Net.Http.Json.dll", - "bytes": 23336, - "sha256": "BF97FCFA0D20ADB597878F902B761480FB02FA0F9F81521F16F2455DDC6A1ED2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.HttpListener.dll", - "source": "vendor/pwsh/ref/System.Net.HttpListener.dll", - "bytes": 25384, - "sha256": "03CA98ABEB9EA2C7E372C31AA460D2A9E526617C22FA97C9D8750977167A9634" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Mail.dll", - "source": "vendor/pwsh/ref/System.Net.Mail.dll", - "bytes": 32040, - "sha256": "8193E3652B1ECB1AA46258087BB5F44BECD72693552DA120E1274EF1AD443E53" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.NameResolution.dll", - "source": "vendor/pwsh/ref/System.Net.NameResolution.dll", - "bytes": 17704, - "sha256": "97E0CC175BF13E4861E1508820F08561B38F28E98EBC9C5ADDB28C58FA7C344D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.NetworkInformation.dll", - "source": "vendor/pwsh/ref/System.Net.NetworkInformation.dll", - "bytes": 33576, - "sha256": "B90B729446F4ED3FA470B5C96E9C5ADECBF4E438EAA91D0548832C58673A4C18" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Ping.dll", - "source": "vendor/pwsh/ref/System.Net.Ping.dll", - "bytes": 20264, - "sha256": "F623594E3309D40D36A7DD63DCB5251F72EB94EB2E74246805B4A0D23FC8C6B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Primitives.dll", - "source": "vendor/pwsh/ref/System.Net.Primitives.dll", - "bytes": 37160, - "sha256": "129E7B324FBC12F8C54B89E6D6706B08455532FB64A8913038ADD92BE2924244" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Quic.dll", - "source": "vendor/pwsh/ref/System.Net.Quic.dll", - "bytes": 23848, - "sha256": "F9651DCDE17F5823C7D42BE080059FD9F88AED81D91C64E58F95AFCA44A2E24A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Requests.dll", - "source": "vendor/pwsh/ref/System.Net.Requests.dll", - "bytes": 41256, - "sha256": "D45910C06D2C2FCD6039E8B4076B5878FD195D0FC09D27C4CEB4009AD9491672" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Security.dll", - "source": "vendor/pwsh/ref/System.Net.Security.dll", - "bytes": 53544, - "sha256": "C4AF7099D2077E34C7074E94EFEB57AC5707AF128EE6E5E0E75D398B8E5843BA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.ServerSentEvents.dll", - "source": "vendor/pwsh/ref/System.Net.ServerSentEvents.dll", - "bytes": 17192, - "sha256": "87BA81CD8FD6AB3A9A3F4DCE922849AEA832928F52495788D371DDA66E57DA01" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.ServicePoint.dll", - "source": "vendor/pwsh/ref/System.Net.ServicePoint.dll", - "bytes": 15144, - "sha256": "84FA83CD96E62FDA02991B8A8E99FA1C7B3AC08807B04F7486A896CFE6B4A760" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.Sockets.dll", - "source": "vendor/pwsh/ref/System.Net.Sockets.dll", - "bytes": 47440, - "sha256": "8B7CEBAFD550083A8D242DBCFB7501384D17142AD6C2BBB047DD37A524762179" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebClient.dll", - "source": "vendor/pwsh/ref/System.Net.WebClient.dll", - "bytes": 27944, - "sha256": "E4E6CC79E23892C8E67A4CB594BB304A26A0328507C45DC2D1998BC6836311A0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebHeaderCollection.dll", - "source": "vendor/pwsh/ref/System.Net.WebHeaderCollection.dll", - "bytes": 19240, - "sha256": "C4EE735A52B966F3805CFBD5C172F151FFE013BD70C080F15C2F68BE628CD357" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebProxy.dll", - "source": "vendor/pwsh/ref/System.Net.WebProxy.dll", - "bytes": 18216, - "sha256": "42C8EB8BF1563806069E1DD1C6C651159DAA5589B7F69FF817CDEAB3BD02915F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebSockets.Client.dll", - "source": "vendor/pwsh/ref/System.Net.WebSockets.Client.dll", - "bytes": 19240, - "sha256": "5AC14D45C1B92E9C831B37591941EA01F3097A39D130BC292506DD099AEB7260" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Net.WebSockets.dll", - "source": "vendor/pwsh/ref/System.Net.WebSockets.dll", - "bytes": 23848, - "sha256": "C16043D3FFA90A8FDAF193F0DF91F99D6C23518D8432B4CAA2D70A92D4C3CFA7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Numerics.dll", - "source": "vendor/pwsh/ref/System.Numerics.dll", - "bytes": 15184, - "sha256": "35ADC9360421B3B7A3D50399CB2C651C160158D7E8C302907ACBCE5F61F4A4F7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Numerics.Vectors.dll", - "source": "vendor/pwsh/ref/System.Numerics.Vectors.dll", - "bytes": 58152, - "sha256": "CC06324F279C581D8F76C0C8F6B011CF8D05EFD0C95E998D68F9243DA124EB52" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ObjectModel.dll", - "source": "vendor/pwsh/ref/System.ObjectModel.dll", - "bytes": 23336, - "sha256": "3A591E043CD443761537580084F714392BB3278176C9A19F1BE8700A29EF6F56" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.DispatchProxy.dll", - "source": "vendor/pwsh/ref/System.Reflection.DispatchProxy.dll", - "bytes": 15656, - "sha256": "0D46DFB9C4FFC27D05A1C089D8F60A1CB365855140A9B30972133208270DD08B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.dll", - "source": "vendor/pwsh/ref/System.Reflection.dll", - "bytes": 16168, - "sha256": "95059346D6271DD6D0322CC321E5FB8CCEAB662F550C67AEE7615059E41739BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Emit.dll", - "source": "vendor/pwsh/ref/System.Reflection.Emit.dll", - "bytes": 44368, - "sha256": "29021699E4128964365A86C22AFB3B0A8368DA7A3F7463C9C1A8FC0A1402A39F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", - "source": "vendor/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", - "bytes": 20776, - "sha256": "0201069B706D397C573B9342902613A71879A2701994A48E2821C32D43101AE4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Emit.Lightweight.dll", - "source": "vendor/pwsh/ref/System.Reflection.Emit.Lightweight.dll", - "bytes": 19240, - "sha256": "EEF97E1049D8DD7ECC695080DD22C199EE8CA82B1CFCB3E33866CF7125DBD1A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Extensions.dll", - "source": "vendor/pwsh/ref/System.Reflection.Extensions.dll", - "bytes": 15144, - "sha256": "CEB1B81E1EB488D87BD43D16DC54450B2A78E0B653C44C988AAE2A20ABC162BC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Metadata.dll", - "source": "vendor/pwsh/ref/System.Reflection.Metadata.dll", - "bytes": 122152, - "sha256": "92EC811516B768220CC41FB6A7197F7BA735F97B694716DFD1BFDF274078A90A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.Primitives.dll", - "source": "vendor/pwsh/ref/System.Reflection.Primitives.dll", - "bytes": 21840, - "sha256": "FA52402A7B4096EF109853F6BF03ADEF0E1DE67E3652BB31F413D197F19F9B22" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Reflection.TypeExtensions.dll", - "source": "vendor/pwsh/ref/System.Reflection.TypeExtensions.dll", - "bytes": 19752, - "sha256": "78178074BDB0F7714C634E4A1B425570264B13B2CA5B9AD6880622BFF0BDA58E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Resources.Reader.dll", - "source": "vendor/pwsh/ref/System.Resources.Reader.dll", - "bytes": 15144, - "sha256": "20BDB5D302B5E28F6042C9F281CFA3AE2F2358544CC40783052FE5ABF6AA859B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Resources.ResourceManager.dll", - "source": "vendor/pwsh/ref/System.Resources.ResourceManager.dll", - "bytes": 15696, - "sha256": "F709EB5CF16E26679782784C428825019785FCC7085562092425D4999BECE03D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Resources.Writer.dll", - "source": "vendor/pwsh/ref/System.Resources.Writer.dll", - "bytes": 16208, - "sha256": "7E772363E799C78D8D1D37FE1D412723DA9350496A6D7BACD37491F2BB777CC4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", - "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", - "bytes": 15184, - "sha256": "6E685EC0477B21A688CD38BFCAFC673888CDF186C99DF3C96FAB352E96D6FDC8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", - "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", - "bytes": 17192, - "sha256": "1E5EB23824055D6AF5BABB6A7CE47E314F78B0B464466DB32B60F543304A9140" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.dll", - "source": "vendor/pwsh/ref/System.Runtime.dll", - "bytes": 862504, - "sha256": "10CEAA9B398F512B46DEF6FD16FB5374C82AC03A7495859ADBAAA70868FE70AA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Extensions.dll", - "source": "vendor/pwsh/ref/System.Runtime.Extensions.dll", - "bytes": 17704, - "sha256": "3D7B517D4FD89EE58C53E0F642CC9BE42E7A8669B51A85E861DAFE118067CBFD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Handles.dll", - "source": "vendor/pwsh/ref/System.Runtime.Handles.dll", - "bytes": 15656, - "sha256": "985457A3E6A67E96A445EE0FB69B2D58BCDADC4C964253FB63E2D5061A36B050" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.InteropServices.dll", - "source": "vendor/pwsh/ref/System.Runtime.InteropServices.dll", - "bytes": 101160, - "sha256": "8CED47E6A2A717C459C3FA13F76A521B35917E123A9B1EE7ED1B5DE13DB80709" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", - "source": "vendor/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", - "bytes": 25896, - "sha256": "CB551CC19DF1067DD8897D14BF02ECFA63D8ECF96067A21902406791D68CE226" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", - "source": "vendor/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", - "bytes": 15656, - "sha256": "C26D9F94329422BD000F28F6C65FDC162BF9397A6D29E418A189D4CA49C5C3EC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Intrinsics.dll", - "source": "vendor/pwsh/ref/System.Runtime.Intrinsics.dll", - "bytes": 466216, - "sha256": "DF8F497F64B6A3AC02941FAE92E057FADDF93F3F135F2F6444F725617C38B619" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Loader.dll", - "source": "vendor/pwsh/ref/System.Runtime.Loader.dll", - "bytes": 19752, - "sha256": "42884BCDC89EBD4EE509D5FCB4A679B1D7EEBD6DDD9B66CB9E118063DDD30482" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Numerics.dll", - "source": "vendor/pwsh/ref/System.Runtime.Numerics.dll", - "bytes": 36136, - "sha256": "4AFC84F3E73B63FF6683941FCFDA44E8A6AA5227B6AFC128DCB979865E23D183" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.dll", - "bytes": 17192, - "sha256": "D565FC261CA98CBD14833821F777860291E8A0F775DE7AEE91778A9F6647F6F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Formatters.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Formatters.dll", - "bytes": 23848, - "sha256": "EF24BB5669B54B31DA46EB13238FB2B7F880DD780DC9B34A224A69DD59D93AF8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Json.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Json.dll", - "bytes": 20776, - "sha256": "DE7EECA7DF9F6BB664383FBEAD141719BD08D88B6AEF43C0605528719F607E62" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Primitives.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Primitives.dll", - "bytes": 19752, - "sha256": "A547B0D205CEBE84508C2DDEFD56D063C48CDFEAE5B0C44BDE6BAE9F3D72F98A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Runtime.Serialization.Xml.dll", - "source": "vendor/pwsh/ref/System.Runtime.Serialization.Xml.dll", - "bytes": 39760, - "sha256": "117BCB0DD828EAF6BFAD06D8380BA32D32729B45879D907D7023FE73E9655BD9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.AccessControl.dll", - "source": "vendor/pwsh/ref/System.Security.AccessControl.dll", - "bytes": 37160, - "sha256": "2BD23D6AA92B920DAFD374E1AB47ECB597EF3B1CF1652A4EC4944A0DEFB1A4AC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Claims.dll", - "source": "vendor/pwsh/ref/System.Security.Claims.dll", - "bytes": 32552, - "sha256": "693A61DC317AB5A651EF598578A4B55912AC39342A0842FEA003761F861B0CAF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Algorithms.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Algorithms.dll", - "bytes": 17232, - "sha256": "EB99AE7DBE058A1B015ABA6769CE1F65155B7FF51FF9A7203FCF6D7CDEA5FEF7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Cng.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Cng.dll", - "bytes": 16168, - "sha256": "7319F97896D92A7D0729CF28DC6C4D1F797C1520B656EE01A780EEC726EC2C16" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Csp.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Csp.dll", - "bytes": 16208, - "sha256": "4002AF141E9CB29875B677841ADF64A9681E5672C2595492DCBC161460D4E121" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.dll", - "bytes": 163112, - "sha256": "A700A7F6C32169E62BE879088AA2218BDD5450C3945ED6C6D0ACB3D25DB0B1BF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Encoding.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Encoding.dll", - "bytes": 15656, - "sha256": "994494AEC7760F40FADE2264AA7C13EEBAE36360F0B1399A7134BB19C9D58FDA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", - "bytes": 15656, - "sha256": "1A8902BBAD1B577A98BEC62A8472105DC1E9A253E390F69FBE3D167E93206968" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.Primitives.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.Primitives.dll", - "bytes": 15656, - "sha256": "194CCCEA9354BB248E44C954B5373F9069163F8F21524B3EE4B3185FE436BC15" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", - "source": "vendor/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", - "bytes": 16680, - "sha256": "7439C3B90BC2EE49A9275B28ED29052B4D48B45999AC1B7D25593033B1BB8DD6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.dll", - "source": "vendor/pwsh/ref/System.Security.dll", - "bytes": 18256, - "sha256": "D8EFD304765E859369698EFEF9B3CA707DEFA8E142721FE5605D7A2DE7D38375" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Principal.dll", - "source": "vendor/pwsh/ref/System.Security.Principal.dll", - "bytes": 15144, - "sha256": "1D3E351058BE3A33214B23B308FE57F78CD6FD4D8FAD448FC84CBBE7376129F9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.Principal.Windows.dll", - "source": "vendor/pwsh/ref/System.Security.Principal.Windows.dll", - "bytes": 26920, - "sha256": "4B8EAB6E832E99E8C08DB2B9649F95AE8298E1105DEEA45A62E7D3B68C6593B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Security.SecureString.dll", - "source": "vendor/pwsh/ref/System.Security.SecureString.dll", - "bytes": 15696, - "sha256": "73AAC97072B7DEEFA4DE9647BF822162224F1452AD467841B989FF89D4D94595" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ServiceModel.Web.dll", - "source": "vendor/pwsh/ref/System.ServiceModel.Web.dll", - "bytes": 16680, - "sha256": "CE91013EE3A9A9D69A85E258F7749E6ECE488295D7CB66F7C1079F2E8678FC5E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ServiceProcess.dll", - "source": "vendor/pwsh/ref/System.ServiceProcess.dll", - "bytes": 15696, - "sha256": "63FC498C9D04E3C4345DFDB8016AD0639D54562DD7D8C707C072792C854DF944" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encoding.CodePages.dll", - "source": "vendor/pwsh/ref/System.Text.Encoding.CodePages.dll", - "bytes": 15656, - "sha256": "2D66F9AE5E7439756EBA063C0DF86D6D3A0DC571F4BB07ADD18C59D6F10138CE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encoding.dll", - "source": "vendor/pwsh/ref/System.Text.Encoding.dll", - "bytes": 15696, - "sha256": "5E347DB6B4F9B2746D50E1AC85BE2D9CC7DC4038FDE98AC4295BC7030C83CDA9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encoding.Extensions.dll", - "source": "vendor/pwsh/ref/System.Text.Encoding.Extensions.dll", - "bytes": 20776, - "sha256": "D6224360B0E91DFE544EC3BAF0D5DED78A13349F6378493C96344A310C8CAF08" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Encodings.Web.dll", - "source": "vendor/pwsh/ref/System.Text.Encodings.Web.dll", - "bytes": 26408, - "sha256": "F9F6120753FC9FAEAA5B09E10EF309594DBD4C20C8CDF1717EB910455549793A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.Json.dll", - "source": "vendor/pwsh/ref/System.Text.Json.dll", - "bytes": 82216, - "sha256": "4133A8597A10EEB99D56A962FF581DB9FD3AC85506EE3A20A2D23119C73A4346" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Text.RegularExpressions.dll", - "source": "vendor/pwsh/ref/System.Text.RegularExpressions.dll", - "bytes": 36648, - "sha256": "347C497CF2BD60A298EFB59FE47156909CA854FDAB4DABB944AF7D6F8C270CEF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.AccessControl.dll", - "source": "vendor/pwsh/ref/System.Threading.AccessControl.dll", - "bytes": 20776, - "sha256": "8449A1EE09EDF8BDC69C61605BEBEC602C7B28A9CA401CA463512E4D369A3B7B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Channels.dll", - "source": "vendor/pwsh/ref/System.Threading.Channels.dll", - "bytes": 19240, - "sha256": "C72028E4423FB04F3A644A76E1807E3651191AEB606B8F7B15B3650E4714CDCA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.dll", - "source": "vendor/pwsh/ref/System.Threading.dll", - "bytes": 33576, - "sha256": "0C2A3B4FDDB2D732B8EBD0BCA9AC57A8C66C9D4F5FBCB55FCD17E3CBCF678A4B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Overlapped.dll", - "source": "vendor/pwsh/ref/System.Threading.Overlapped.dll", - "bytes": 18768, - "sha256": "7837D3BD05E28F95749029C832FB1D0CE5BF403A17BEF7BF95DF522BA93DAC3A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.Dataflow.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.Dataflow.dll", - "bytes": 32040, - "sha256": "5D605D4C470C38BC37A04D58B4023A9C60179172790A29F4F7CB92ABA6251648" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.dll", - "bytes": 16680, - "sha256": "F80B21F7DAE04377B73945456CABE25E2AB1F4B564095AED546C2AA571644567" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.Extensions.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.Extensions.dll", - "bytes": 15696, - "sha256": "F148E3A1414B7C2763AE21529AD79AD759F7CFD3C885256B64B0D2209DEC2613" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Tasks.Parallel.dll", - "source": "vendor/pwsh/ref/System.Threading.Tasks.Parallel.dll", - "bytes": 20264, - "sha256": "97F9CCB69D9F1D4D86DF2869E1EF667C4C68285B04F336B1188E633C8F814D05" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Thread.dll", - "source": "vendor/pwsh/ref/System.Threading.Thread.dll", - "bytes": 23888, - "sha256": "FFF2281F039C07E564BCB198E041627082702BEDA24DCCC8DF39683701EA25A0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.ThreadPool.dll", - "source": "vendor/pwsh/ref/System.Threading.ThreadPool.dll", - "bytes": 18216, - "sha256": "12FEDF2DE6449BFEC4C1A08D762493BC0188E1B9B0A58028289105ED81B983F5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Threading.Timer.dll", - "source": "vendor/pwsh/ref/System.Threading.Timer.dll", - "bytes": 15144, - "sha256": "9E4620846572F49AEB476AA99405071B4BBD0DF4323E4E232787ED73642B1EE5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Transactions.dll", - "source": "vendor/pwsh/ref/System.Transactions.dll", - "bytes": 16720, - "sha256": "56ABFCA0350C111D077B3D86A2469EA3DA4FCCC1871F7C51A8B4FDB664BB0EDE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Transactions.Local.dll", - "source": "vendor/pwsh/ref/System.Transactions.Local.dll", - "bytes": 25896, - "sha256": "C9FAFFC186D677DE7800FF7D4D1B0D204AE7614E6B8518ADD582C840D7532A3B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.ValueTuple.dll", - "source": "vendor/pwsh/ref/System.ValueTuple.dll", - "bytes": 15696, - "sha256": "07A5347F6A597E5E0D72109EFA1A525697E8C3F74FB0029005500F8EBBF9187A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Web.dll", - "source": "vendor/pwsh/ref/System.Web.dll", - "bytes": 15144, - "sha256": "BC260B3DB247AD9A6E4A82A275DA85C3691A41272CC745CB7EAEFCF1F31CC817" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Web.HttpUtility.dll", - "source": "vendor/pwsh/ref/System.Web.HttpUtility.dll", - "bytes": 17192, - "sha256": "F26B0586048FDF472E0FB33CA9435B163C91325DE4281FCE8684251CACC304CC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Windows.dll", - "source": "vendor/pwsh/ref/System.Windows.dll", - "bytes": 15696, - "sha256": "716EEE98CEEE87D38F0B301224462F8442AA793F4B1B9C3FFAC315753320AB37" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.dll", - "source": "vendor/pwsh/ref/System.Xml.dll", - "bytes": 23376, - "sha256": "277A84BA155FE7808A514F32694A8157EC9F0EEEEF00EF9643E5250004054FF0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.Linq.dll", - "source": "vendor/pwsh/ref/System.Xml.Linq.dll", - "bytes": 15696, - "sha256": "AE04903B25717FA4695E7E63489091F0D461E791E0BD264C6C85250D668F540D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.ReaderWriter.dll", - "source": "vendor/pwsh/ref/System.Xml.ReaderWriter.dll", - "bytes": 116008, - "sha256": "942964D32F72BA10288593F212E3A52C4811B65CA4D5F1A3979C92D2D526AA4C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.Serialization.dll", - "source": "vendor/pwsh/ref/System.Xml.Serialization.dll", - "bytes": 16208, - "sha256": "489694401C60905CB6DCA8EF0BF738CB609CD45B337126A01C825B913086BA43" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XDocument.dll", - "source": "vendor/pwsh/ref/System.Xml.XDocument.dll", - "bytes": 34088, - "sha256": "05F6380411A81245960714A9FD81F6134F7BD279CD70B248A1E1586977798F1A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XmlDocument.dll", - "source": "vendor/pwsh/ref/System.Xml.XmlDocument.dll", - "bytes": 15656, - "sha256": "A50B9B3A03596E36E73E7A7D54F4D018E8DCFAA7FE79E8CF900AF8838EF3EC36" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XmlSerializer.dll", - "source": "vendor/pwsh/ref/System.Xml.XmlSerializer.dll", - "bytes": 50984, - "sha256": "F957611B80E082A453264A13BF27B5B251FAF598F377F948195123A374B2E8F6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XPath.dll", - "source": "vendor/pwsh/ref/System.Xml.XPath.dll", - "bytes": 16680, - "sha256": "0E68260B4C36D00C9A77C30A270100461123C0E7E1A37402BB7F67174051B8F3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/System.Xml.XPath.XDocument.dll", - "source": "vendor/pwsh/ref/System.Xml.XPath.XDocument.dll", - "bytes": 16168, - "sha256": "B8F74E198C3C2CD00CCC90549579F1B69C6AE1655F8B348ACE414FD9075C921F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ref/WindowsBase.dll", - "source": "vendor/pwsh/ref/WindowsBase.dll", - "bytes": 16208, - "sha256": "0EC46A38F29A234B9A4D0AE9364C8322F661A47C831C5B948F2787B7CA12DBBD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/RegisterManifest.ps1", - "source": "vendor/pwsh/RegisterManifest.ps1", - "bytes": 17502, - "sha256": "13D50727D810BABE2A8FCBC77A0A4EA967A6C78DE4960469116DF9D8EEF53EC0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/RegisterMicrosoftUpdate.ps1", - "source": "vendor/pwsh/RegisterMicrosoftUpdate.ps1", - "bytes": 16907, - "sha256": "8DD5BF144D53B976B3F414973FB0F6C886F754E515427A2E8FD58B9C3B3E1B52" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/base.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/base.xsd", - "bytes": 6434, - "sha256": "B860EF30B01C57486A56948800E9B2DA116EC473CBECBB33FA9F71B8CEAB90AE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/baseConditional.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/baseConditional.xsd", - "bytes": 5297, - "sha256": "D26D4EF29124B2F9107B167D73E611F649FB2021D30C48A8616C6CC96230DE81" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/block.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/block.xsd", - "bytes": 917, - "sha256": "9A8B3C184394711480C38D71DB4850659B87729DF9EAC5A2235BDCF37A2020CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/blockCommon.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/blockCommon.xsd", - "bytes": 3872, - "sha256": "928717B190B014F271C09C2FFB7633C1E660B26F0ECBDD926662121914795789" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/blockSoftware.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/blockSoftware.xsd", - "bytes": 1282, - "sha256": "88AA3462598B7C394350F83EF366E5F83885C50A9C6966F02179228277358334" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/command.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/command.xsd", - "bytes": 13061, - "sha256": "A3DA7F8F2260A826CE253C33BDA8576246B4298794765EDC4F80A433357C6B87" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/conditionSet.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/conditionSet.xsd", - "bytes": 4279, - "sha256": "506415CD77EDE25B33C3E28FB89012B282E90B5D01F266323A83FEA9625F8EAB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developer.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developer.xsd", - "bytes": 11403, - "sha256": "AB9A7DDB85084F331ED79CB34374509767D9A64C510B0264682D5E682DC59497" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.rld", - "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.rld", - "bytes": 115907, - "sha256": "CD8A9A6B113DB5005F2A5C8321E52368411691B5F0C5EB90595E2A97B0FDEE89" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.xsd", - "bytes": 14520, - "sha256": "BBC9B2DD6D4473F09CE95A4E44B7F654A8D34A70197BEABACD1C9FF5233C3DEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerDscResource.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerDscResource.xsd", - "bytes": 2896, - "sha256": "7C22D9A1393062734E4923657C48D9117F6AC824D4E3F85EF88D8DE23F8C7BDF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManaged.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManaged.xsd", - "bytes": 10238, - "sha256": "5585FD25ED4FA8D39B75E9A83082EAB1D305B00839E7BCF05A24530E3D91D6EA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedClass.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedClass.xsd", - "bytes": 3693, - "sha256": "4CB1C23D86D22B4BCC5ACA98CD309C37F54A30358D673115D460FEEACB78DF9A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", - "bytes": 2557, - "sha256": "0CA55A744AD968B537742ED37CD62852DF01CD96533D4E17874AD4F75271C8DC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", - "bytes": 2558, - "sha256": "961DAA1C4B3513B83CF2E37FDB1361BE0D123E8B2CFE7F32510AD534FEA98E3D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", - "bytes": 2306, - "sha256": "3AB7329914D7A9A6D711A62405089E736CE714EE4DE72F3493222887A72FDD39" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", - "bytes": 3108, - "sha256": "93EBD3AF5A078830839B10C9DB753CD445DC2D1A78A5449C90AC605E7FFC397C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedField.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedField.xsd", - "bytes": 2803, - "sha256": "B17F73ADD6678C4BC259065ADD380A9070531BC24A66AF3C4CAD3CF3D811F3A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", - "bytes": 2882, - "sha256": "B851D8BE146F1F51E5857045C28726585CC96A29C5A5A57DFC86CF7C509D8234" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", - "bytes": 2544, - "sha256": "36EB2BC76B2CDBFD908DF62E16CA915F7490CE75CF7BC386974EF89AA77B32FF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", - "bytes": 3359, - "sha256": "68D68D8CEC8181C0C3046B419426EE1BD4D0371ABFDBF9A6789755B0F5764D91" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", - "bytes": 1896, - "sha256": "053C18557BE353860222E14D7CE5E5844F5A746BA45347A9E08DA1D5A2B29F3B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", - "bytes": 2573, - "sha256": "7D2695D2F6AB6EB3C0BAB6125EEE415545C3EABE0923B1B37519F2D4A8B9251D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", - "bytes": 2843, - "sha256": "832A25485EAA9060868E9258AA1F93626804ED439EE2B43B2D74F9CB0DC438C4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", - "bytes": 2951, - "sha256": "B7D1F7DDBCFEF43BD171FD78D5A255089A97762AD67C116B6A6998815905901F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerReference.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerReference.xsd", - "bytes": 12604, - "sha256": "42C585D48C418C7EB47CBA298A147923597B967E236997BB815301B57E31CB92" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerStructure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerStructure.xsd", - "bytes": 4112, - "sha256": "D1C0E02BB7207095E49EFC216A574B596A82E97B88E686E1C7A9717362623C44" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/developerXaml.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/developerXaml.xsd", - "bytes": 6746, - "sha256": "83ED2EE26CF6AB4C95A880F1424F7D5E09385B71239B84B3CFFB58288EEFF3B2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/endUser.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/endUser.xsd", - "bytes": 2144, - "sha256": "24CAEE5D0536ED81EB68D6FD836CF08849E056497B6064971A145397A04EB853" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/hierarchy.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/hierarchy.xsd", - "bytes": 10070, - "sha256": "D50D8096A11350A8E3B6873CF9CBC173B8A4322324B16F949538B1B98857C5B4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inline.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inline.xsd", - "bytes": 7644, - "sha256": "5A1A2D4586203BB3D6A4E6EC93E4D09D0AB66D33539B88A2550A36100D1E16FD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inlineCommon.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inlineCommon.xsd", - "bytes": 8860, - "sha256": "02071402B963966AD574AB09BB42719BDB1CE6CD7E9AF260A13FAF9EBB580C55" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inlineSoftware.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inlineSoftware.xsd", - "bytes": 8016, - "sha256": "C444CD1121A8B83B0FFE5A8467CC10D2D810C5A7765F4B4406D1EB329CD8901D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/inlineUi.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/inlineUi.xsd", - "bytes": 6400, - "sha256": "E7FF025576E482632878709E208B95916CCDCA2AF108C1C7B9218FB8472C1AE3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ITPro.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ITPro.xsd", - "bytes": 1208, - "sha256": "767328AD7B14EC90731E52D04EF27873EA674D34DD30297CB0FBA2A11242E96E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.rld", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.rld", - "bytes": 115835, - "sha256": "A711A691FDFDBEEEF09CB23543A8AC3D6BBF92AABEEC05939FCA51665229E710" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.tbr", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.tbr", - "bytes": 30575, - "sha256": "0FFD2E7097B51E0C6E1C1A18867658312D39AC68D8179049521F9ECAEB2CBF08" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsd", - "bytes": 1571, - "sha256": "FDA94A92FE41A7280D7F5ECE13CFC0042D4E1049E8FF961717628412317F6AE2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsx", - "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsx", - "bytes": 211, - "sha256": "639E5ED55BB9FD20672C74C20020C6C96B5FF721E45E9A337133AF858998B08C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML.xsl", - "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML.xsl", - "bytes": 363, - "sha256": "BDA0F195C928E6C18E226A83C77A1477DD88B70025D78F12D92605BBD10723BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", - "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", - "bytes": 14422, - "sha256": "99E1AD569C30396EFC7F37ACD08A8F0F41B20FF82E513D738E331C0EAC5DDC1C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", - "bytes": 14517, - "sha256": "3B89CB9A72549C46A56D9EA2EA12AC53CBC7DDD3392012FDD9D69EF261961AD2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", - "bytes": 3391, - "sha256": "0FA8EACF615F4A726F846D2938E18E84BB641BB28579BD9E2AB83F3D46D505FA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/ProviderHelp.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/ProviderHelp.xsd", - "bytes": 6766, - "sha256": "0E6ADBE5B896F075BF55AE6F2C1966E0E5596F79BC282C2D17CC36CCF41C30CF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/shellExecute.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/shellExecute.xsd", - "bytes": 2250, - "sha256": "52F48B0830346E30C782AA5C379DC547D0F40265FF1504276C904B819446409E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structure.xsd", - "bytes": 5973, - "sha256": "F35E24830E9E1A6AC79DEDFBC3F2EF03EFF8D41EF8C2414E8BA1C1A52E527391" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureGlossary.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureGlossary.xsd", - "bytes": 4186, - "sha256": "4425E365BA3B646B11E5625644D5F98B0151F60ED2BA0A28316570D7D68F6FA5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureList.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureList.xsd", - "bytes": 4173, - "sha256": "176D55D7715DDECCCBE51330E9C012E09972AE3E4A9C9AC8D1C5869B9510540A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureProcedure.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureProcedure.xsd", - "bytes": 1994, - "sha256": "D323AEB14F98A01583E538DD92EF445E1094DB861DC7205D5D2062976DE539B3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureTable.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureTable.xsd", - "bytes": 6137, - "sha256": "63D397B018E5D3783AE3057658D7ADCB44F968BFD81329D2FCEA1ABB91F2C7B8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", - "bytes": 6794, - "sha256": "C37308C8CDB9AC988C94A850A848FC79707FB5D268F7E17072D6D2FB8E83E852" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/task.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/task.xsd", - "bytes": 22391, - "sha256": "808A13CEEEBDA94C0152B93A52A7825E20B5C01780A3EC6C2AE0C8AC0230ED74" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/Schemas/PSMaml/troubleshooting.xsd", - "source": "vendor/pwsh/Schemas/PSMaml/troubleshooting.xsd", - "bytes": 3239, - "sha256": "FBA82FDA979C33074968ED01AD95970FCF1151F3E7C298955BD501D5CA554633" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/sni.dll", - "source": "vendor/pwsh/sni.dll", - "bytes": 160040, - "sha256": "68705C4EF9AB818F2956A78E05F3FEFCE501A1448793B073B46110BEB49B47D6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.AppContext.dll", - "source": "vendor/pwsh/System.AppContext.dll", - "bytes": 15184, - "sha256": "6CD5D7FAE26815702D087EADBCE804B157CFDE3A57A8D897016822183FA2190E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Buffers.dll", - "source": "vendor/pwsh/System.Buffers.dll", - "bytes": 15184, - "sha256": "91F7DD9DBBEF1A7A2BD7C83EAB8110F8C1915DEB38723B05E7F65A5FFA491220" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.CodeDom.dll", - "source": "vendor/pwsh/System.CodeDom.dll", - "bytes": 476968, - "sha256": "28B49CE532AF31CA14A28C9B823E628457D2F3D62F96FDF0E54F206E5B1B7169" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.Concurrent.dll", - "source": "vendor/pwsh/System.Collections.Concurrent.dll", - "bytes": 231208, - "sha256": "EFD70AF76FE0182D974DDC1520F335E9764ECFDD6A517168944B76C07A31E7A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.dll", - "source": "vendor/pwsh/System.Collections.dll", - "bytes": 296744, - "sha256": "DC868C8736277084D26726D726C6CE07FFF9EAB3ACA2D3476CFACE8FC3B2403F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.Immutable.dll", - "source": "vendor/pwsh/System.Collections.Immutable.dll", - "bytes": 976680, - "sha256": "CDEFF36E2129E3A1CBF08698C4D692A13F2C25CD1C04CED0120CAEF982461354" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.NonGeneric.dll", - "source": "vendor/pwsh/System.Collections.NonGeneric.dll", - "bytes": 104272, - "sha256": "D84C613DA29BAF7D8DFD7E8AB7CD806E1086EB16B350C8377947B5EDB77B6EEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Collections.Specialized.dll", - "source": "vendor/pwsh/System.Collections.Specialized.dll", - "bytes": 104232, - "sha256": "E203E92897552D5109F4F096EE7019443F83DAC9DC7190217359255CEED1E3A3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Annotations.dll", - "source": "vendor/pwsh/System.ComponentModel.Annotations.dll", - "bytes": 198480, - "sha256": "62C92AA7C62EB1CF7B506F0F3A9A1D63A4F6941EAAF9F6D8180A6C68E05BCD9B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Composition.dll", - "source": "vendor/pwsh/System.ComponentModel.Composition.dll", - "bytes": 718656, - "sha256": "7294AB56FC43F92EBB60700463262EFFE7CCD5BF36777CC78648E5D57F683240" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Composition.Registration.dll", - "source": "vendor/pwsh/System.ComponentModel.Composition.Registration.dll", - "bytes": 108336, - "sha256": "0CB63A3465A5C2A21C2B63B37586E9E61CA6A5454363905F3283707286F2354E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.DataAnnotations.dll", - "source": "vendor/pwsh/System.ComponentModel.DataAnnotations.dll", - "bytes": 16680, - "sha256": "6D8DD0135A19828A0B4F8F62FB79AEE094ADD47EBFFA6D2F3BCDC8CC2CC2425D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.dll", - "source": "vendor/pwsh/System.ComponentModel.dll", - "bytes": 30544, - "sha256": "283C355A88E039F5A49F791F89F1AF721FB82576447E23859A4CB269085A69D1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.EventBasedAsync.dll", - "source": "vendor/pwsh/System.ComponentModel.EventBasedAsync.dll", - "bytes": 46888, - "sha256": "6B5D96FB4CF007AA303FE4EFA2AEEB345A9AD2E93DABD668EB234DC793453CD4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.Primitives.dll", - "source": "vendor/pwsh/System.ComponentModel.Primitives.dll", - "bytes": 79696, - "sha256": "A6268FE9416484303A678B72837B461AAA686D4CD4509430C1E4C5203397E63B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ComponentModel.TypeConverter.dll", - "source": "vendor/pwsh/System.ComponentModel.TypeConverter.dll", - "bytes": 767784, - "sha256": "9FC839CC0C4138DEEEE0080C72D29069D4EA22A4385FB78CDB4FEA6A911C30C0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Configuration.ConfigurationManager.dll", - "source": "vendor/pwsh/System.Configuration.ConfigurationManager.dll", - "bytes": 1046312, - "sha256": "68265C15B59229C842C7239D9456926096DDAB61AA39C2C29F90D98B12ADC609" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Configuration.dll", - "source": "vendor/pwsh/System.Configuration.dll", - "bytes": 19280, - "sha256": "87876603DABB331C85986D8AC56F0B317C4769051F369FB37409158B924F3FD0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Console.dll", - "source": "vendor/pwsh/System.Console.dll", - "bytes": 169768, - "sha256": "53B599B15B7EB4DB618946B943B00229D998D9BF5450CF23274B0F66BCDCB4EA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Core.dll", - "source": "vendor/pwsh/System.Core.dll", - "bytes": 23336, - "sha256": "FFFC038ABE34BB1BCDC13B57C146CD121B91D8BB2F8C80D2292F50C2563D8FAA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.Common.dll", - "source": "vendor/pwsh/System.Data.Common.dll", - "bytes": 2770728, - "sha256": "1CBEF4B075D6F36E914666010CE93FC831858622E775ABE9F73966B24C816AEE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.DataSetExtensions.dll", - "source": "vendor/pwsh/System.Data.DataSetExtensions.dll", - "bytes": 15656, - "sha256": "3FECFE3CAA4A21FF3BADC04354C036FDBE20636AD40A899B0F3CCF30471D39DE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.dll", - "source": "vendor/pwsh/System.Data.dll", - "bytes": 25384, - "sha256": "B801A07AD2B340F4808F1B683997A9D1AF9A70622FF104B1E89E37405403AC49" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.Odbc.dll", - "source": "vendor/pwsh/System.Data.Odbc.dll", - "bytes": 612144, - "sha256": "F609F522D9668A3761C21FD74D4C5EF8B1C76CF21E67377E550630110B5C66AB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.OleDb.dll", - "source": "vendor/pwsh/System.Data.OleDb.dll", - "bytes": 853808, - "sha256": "C6524C47A9584C10B7359A88D6ED5462E9BDFE4B1085129296E82F8C9A2F7BB0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Data.SqlClient.dll", - "source": "vendor/pwsh/System.Data.SqlClient.dll", - "bytes": 2434864, - "sha256": "C37A712C5E0B4F2D388611B0CEB9BCECA902FE8751075ABA4F5DCDCC291EDA03" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Design.dll", - "source": "vendor/pwsh/System.Design.dll", - "bytes": 23376, - "sha256": "3ED1B23FBA9A8E1B61F5036CC84058335F34BE254EACDFAE83F20FF484A422D8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Contracts.dll", - "source": "vendor/pwsh/System.Diagnostics.Contracts.dll", - "bytes": 16168, - "sha256": "E529CEFF3E677722C0779EFB90F464325C37F4598D2275684B6F3E44475DCA54" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Debug.dll", - "source": "vendor/pwsh/System.Diagnostics.Debug.dll", - "bytes": 15656, - "sha256": "97A2FAD35E9C07C9BB37B627FF5517A969421A0CA6C5D31DE665B9B72FB7F1CA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.DiagnosticSource.dll", - "source": "vendor/pwsh/System.Diagnostics.DiagnosticSource.dll", - "bytes": 501544, - "sha256": "1F3297FFFDFD0C97EC4C3AB7B628839B1F43FCB44193E44E96D4480631C5F07B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.EventLog.dll", - "source": "vendor/pwsh/System.Diagnostics.EventLog.dll", - "bytes": 366376, - "sha256": "47100510AA1489B538D0D8F58FF6D97AC79DBE3F09C541CB3E49B2CB8168E3F1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.EventLog.Messages.dll", - "source": "vendor/pwsh/System.Diagnostics.EventLog.Messages.dll", - "bytes": 800552, - "sha256": "5BF5DE8C874F06129A9A016F25A2532F323098F669EC766BEE212354F05ADB13" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.FileVersionInfo.dll", - "source": "vendor/pwsh/System.Diagnostics.FileVersionInfo.dll", - "bytes": 46888, - "sha256": "1A0073C382851890010F74B559F27A033EB783EEA1E38205782E6C8DACB5D10E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.PerformanceCounter.dll", - "source": "vendor/pwsh/System.Diagnostics.PerformanceCounter.dll", - "bytes": 300880, - "sha256": "B4E97299CB6BA71B5F4AB79774F1D5AFB72061CE7866188486770C9DC7D4F10B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Process.dll", - "source": "vendor/pwsh/System.Diagnostics.Process.dll", - "bytes": 329512, - "sha256": "7F70EA57A7868DFEEEA304D5DA3843B9B52D98767BC41A81162031BAA9361405" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.StackTrace.dll", - "source": "vendor/pwsh/System.Diagnostics.StackTrace.dll", - "bytes": 46888, - "sha256": "D696C411324CF024A89D830C57CB713C487950DB5FCD7A3F8790559675A02CE4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.TextWriterTraceListener.dll", - "source": "vendor/pwsh/System.Diagnostics.TextWriterTraceListener.dll", - "bytes": 67408, - "sha256": "308FA04540B284354DC0DB843B88EFCC562B43CB3EC5ABCE2ACE437521A31AEB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Tools.dll", - "source": "vendor/pwsh/System.Diagnostics.Tools.dll", - "bytes": 15144, - "sha256": "3EF95CD1BE21128929FF5A59EB656B79E2F1CF620E7EE8518AD54EF15642ABCC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.TraceSource.dll", - "source": "vendor/pwsh/System.Diagnostics.TraceSource.dll", - "bytes": 141136, - "sha256": "C707B27E877FB0C7D38848828AB08637915E2B7A05563AA1A08327DC69EEA911" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Diagnostics.Tracing.dll", - "source": "vendor/pwsh/System.Diagnostics.Tracing.dll", - "bytes": 16168, - "sha256": "72F5F814B5E142C87F514A38AF6C08D103FF14DAA22160867F987151C7F26C39" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.DirectoryServices.AccountManagement.dll", - "source": "vendor/pwsh/System.DirectoryServices.AccountManagement.dll", - "bytes": 710472, - "sha256": "809BF2787752D82E9B75760FB742E32901470013066E193E5A3C5D21654CD0C7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.DirectoryServices.dll", - "source": "vendor/pwsh/System.DirectoryServices.dll", - "bytes": 1021736, - "sha256": "18A53AF12ED4CDFC3F6FCBE0787E53101ACA6B5C28B730ABEC789FBBE040801A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.DirectoryServices.Protocols.dll", - "source": "vendor/pwsh/System.DirectoryServices.Protocols.dll", - "bytes": 366408, - "sha256": "B5AE31C1B36D24149561B5DD47F3D6E4124CCD3CA7DC94DB21405D945F84BD00" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.dll", - "source": "vendor/pwsh/System.dll", - "bytes": 50472, - "sha256": "2F5F3CCB0BB4E730F8EEDE5585E64AA4164CFDA1EF0C07B3E6C20DD4036F11B1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.Common.dll", - "source": "vendor/pwsh/System.Drawing.Common.dll", - "bytes": 923432, - "sha256": "924F083442617CADED6A137E8926791461D8735F2ECDF4821C56B5F836283155" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.Design.dll", - "source": "vendor/pwsh/System.Drawing.Design.dll", - "bytes": 15656, - "sha256": "3ECF8AD363EBA3E9DEDF01C63698BA257C0F743F734FF92B8FD03C5E7A2F5F26" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.dll", - "source": "vendor/pwsh/System.Drawing.dll", - "bytes": 20816, - "sha256": "E3FEC0493ED82D0F8BA9AB365428EA2008C5344184634D737DAD005F5FC4F1CE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Drawing.Primitives.dll", - "source": "vendor/pwsh/System.Drawing.Primitives.dll", - "bytes": 132944, - "sha256": "00CB49E97C49A30C2282904EB851B5C8D3F3E1B240C7DFF1BB850AC8E424BDAA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Dynamic.Runtime.dll", - "source": "vendor/pwsh/System.Dynamic.Runtime.dll", - "bytes": 16168, - "sha256": "D2A82A14EE1DFCE78D7B4F1F3D161935C56CB4C07EBC8A7DAFA8260745FA3771" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Formats.Asn1.dll", - "source": "vendor/pwsh/System.Formats.Asn1.dll", - "bytes": 239440, - "sha256": "027EC42F8A81D9999425FD9B75FDA1D20F610F2FC2F57C2B55D4D37B2A1FE75C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Formats.Nrbf.dll", - "source": "vendor/pwsh/System.Formats.Nrbf.dll", - "bytes": 137040, - "sha256": "9852CE3FD4EAE4B7638F910860E382BA8EDE1CF51628C3619691B7F24CD4DACA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Formats.Tar.dll", - "source": "vendor/pwsh/System.Formats.Tar.dll", - "bytes": 272168, - "sha256": "D4E9806746831BDE8670F8AF3A0DF7AA370678CDDB7950B28F4FAD0BA3353727" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Globalization.Calendars.dll", - "source": "vendor/pwsh/System.Globalization.Calendars.dll", - "bytes": 15656, - "sha256": "48F3B8DB0B6AF61E000C70C29C977B9CEE6D4C6FA0CF794418DFF29D1BA4E709" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Globalization.dll", - "source": "vendor/pwsh/System.Globalization.dll", - "bytes": 15656, - "sha256": "769DDE445009C78BE8D4CDB28DCC6603DF2218DAFE9491F5F1B915B1945A3F6E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Globalization.Extensions.dll", - "source": "vendor/pwsh/System.Globalization.Extensions.dll", - "bytes": 15144, - "sha256": "D3497DFC648BF299FCE08FC992918C542D1ED66EBD19EF726595369B0CD513B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.Brotli.dll", - "source": "vendor/pwsh/System.IO.Compression.Brotli.dll", - "bytes": 83752, - "sha256": "A14E6E6B390BF8F96A6EF0AA4450C2B0A8455202309C589D1BA8995C0D586611" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.dll", - "source": "vendor/pwsh/System.IO.Compression.dll", - "bytes": 419664, - "sha256": "9362497FA6D4ABE5B8D3DD9F2256564E9A5C84BFB8E9C709A741503DC602EBAA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.FileSystem.dll", - "source": "vendor/pwsh/System.IO.Compression.FileSystem.dll", - "bytes": 15184, - "sha256": "C2635E40545F18B1D0B15887C1AE5DE5504BB21A407E9371BEE83CE8974EA84D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.Native.dll", - "source": "vendor/pwsh/System.IO.Compression.Native.dll", - "bytes": 937296, - "sha256": "9FBB2CE965AE5F0AFB8601F9190326C6FC7A345F4B6E3FD4AF6740A4F6CF2548" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Compression.ZipFile.dll", - "source": "vendor/pwsh/System.IO.Compression.ZipFile.dll", - "bytes": 100136, - "sha256": "79409DEBB2680FF86B7513FBDF622CAAEB791328358F4290EC6A78B403FD4F48" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.dll", - "source": "vendor/pwsh/System.IO.dll", - "bytes": 15656, - "sha256": "5329ADEC3B4B1C869E9814912B80E50E0094F795EFF2A7E360FCCB81A1BC55C9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.AccessControl.dll", - "source": "vendor/pwsh/System.IO.FileSystem.AccessControl.dll", - "bytes": 104232, - "sha256": "1C54898A3EC1EB02A9DFF3BA726C6328D1D0F306926A9A370C0C468E065BAB17" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.dll", - "source": "vendor/pwsh/System.IO.FileSystem.dll", - "bytes": 15696, - "sha256": "CB1CF391EF106BEECB0C7D835E2B24D03101AA7B6D4B279B98C235654ED521C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.DriveInfo.dll", - "source": "vendor/pwsh/System.IO.FileSystem.DriveInfo.dll", - "bytes": 55120, - "sha256": "AD0FA0F61602BABDB65280CFB5295D7F3905E7739431CA64D531919703729D2E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.Primitives.dll", - "source": "vendor/pwsh/System.IO.FileSystem.Primitives.dll", - "bytes": 15184, - "sha256": "2673EA5084171A31D826A8D93C91C7F34798385EAC41155932192A3C9066C20B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.FileSystem.Watcher.dll", - "source": "vendor/pwsh/System.IO.FileSystem.Watcher.dll", - "bytes": 87888, - "sha256": "7B5E2B5E413C7A234F971FE2C1E0A04063380018336F0C22F0C52955B6E7578F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.IsolatedStorage.dll", - "source": "vendor/pwsh/System.IO.IsolatedStorage.dll", - "bytes": 87848, - "sha256": "0494E0FC71AECF0AC89EA062E0887BCB7075DFEA986B2D87E88DEEF2ADDEB5B0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.MemoryMappedFiles.dll", - "source": "vendor/pwsh/System.IO.MemoryMappedFiles.dll", - "bytes": 83752, - "sha256": "8D17D7A790D463C15C3D683A92D449A1D02C423DA680D2DC5B809C9CDE1B1C2F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Packaging.dll", - "source": "vendor/pwsh/System.IO.Packaging.dll", - "bytes": 321320, - "sha256": "6B178F0D551444A502A66055EBDF3FBB65FCDE25056C43866055C2B5B759F2AA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Pipelines.dll", - "source": "vendor/pwsh/System.IO.Pipelines.dll", - "bytes": 190248, - "sha256": "40A3244F2A4EBA94430C8813F7FD4778E15E1D957DCE9034D8DFA17725A0FCF0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Pipes.AccessControl.dll", - "source": "vendor/pwsh/System.IO.Pipes.AccessControl.dll", - "bytes": 15656, - "sha256": "1FA1F01D59F34DD66398931BBC51A4D1C981CE422FC149E7EEC914991A8588F6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Pipes.dll", - "source": "vendor/pwsh/System.IO.Pipes.dll", - "bytes": 165672, - "sha256": "A1858A82360321B757B41F8F7C33CB793D626A712CC29409E5E8D77493F62062" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.Ports.dll", - "source": "vendor/pwsh/System.IO.Ports.dll", - "bytes": 165680, - "sha256": "378B0F569EAF4BA9E6B8DE64186A9CEAEE127FE0BFB13B7D3E832050F0A8D233" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.IO.UnmanagedMemoryStream.dll", - "source": "vendor/pwsh/System.IO.UnmanagedMemoryStream.dll", - "bytes": 15656, - "sha256": "A18AC4B8B1DE6DFC0FA4F9CDA77EF9763E1721A52731B7AD8000BF733FA9FE8E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.AsyncEnumerable.dll", - "source": "vendor/pwsh/System.Linq.AsyncEnumerable.dll", - "bytes": 1292112, - "sha256": "7648125B74F53ABFE7E6F627E4D56D555BCF1AB1A0BFE4912C21969D47110C78" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.dll", - "source": "vendor/pwsh/System.Linq.dll", - "bytes": 698152, - "sha256": "F170005154FE77B1A5AF72179A454CA1D24FF3F805E6DC1A629EF1CF82D3E7AD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.Expressions.dll", - "source": "vendor/pwsh/System.Linq.Expressions.dll", - "bytes": 3639080, - "sha256": "E2627EA364EC3868E20B9F84D2C2A3ECF5FAAD4DC548F53A9D0C6A2D6F9FB282" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.Parallel.dll", - "source": "vendor/pwsh/System.Linq.Parallel.dll", - "bytes": 776016, - "sha256": "75459D9290969BC7E0BE2A352F7E4BC910FAD343F51C01359570CDACE014755D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Linq.Queryable.dll", - "source": "vendor/pwsh/System.Linq.Queryable.dll", - "bytes": 182056, - "sha256": "DFCCADFD6219CE61D1C1263191C919FF2D095395E1B796B82ECF15C486A2B70C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Management.Automation.dll", - "source": "vendor/pwsh/System.Management.Automation.dll", - "bytes": 19658592, - "sha256": "ED8D39663E05435AAB59F1C7FB0AD20292E22B0CB12CF47AAD4B62B31E4D4126" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Management.dll", - "source": "vendor/pwsh/System.Management.dll", - "bytes": 833352, - "sha256": "AD07E0C250FE08CE6B23D63B7A564DA19E128D0A5A7ABA9CE49810ED7752A3DF" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Memory.dll", - "source": "vendor/pwsh/System.Memory.dll", - "bytes": 161576, - "sha256": "A714F1A2B4B8D2695BEA4E5D2A85E62621CED26A7E4EBC5FCD61E85EE4838B77" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.dll", - "source": "vendor/pwsh/System.Net.dll", - "bytes": 17192, - "sha256": "D16A0494E92F84355FB5526A4E2F0587AB4F3C296E9971903E17523169E58E7E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Http.dll", - "source": "vendor/pwsh/System.Net.Http.dll", - "bytes": 1746728, - "sha256": "ED96D8A3995CF09E84981DB6EE8A5199E41FC2A2599749F70CF84ADA7D4CCE53" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Http.Json.dll", - "source": "vendor/pwsh/System.Net.Http.Json.dll", - "bytes": 128848, - "sha256": "DC58C398BAEF3AFFB78A9F2440E153D66BD8DC5F78079ECA71AD1BF18C726F19" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Http.WinHttpHandler.dll", - "source": "vendor/pwsh/System.Net.Http.WinHttpHandler.dll", - "bytes": 304960, - "sha256": "EA7676CB7C3FBA9167FD3B4308D422DBEC961282319267F924306744C2BC12F2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.HttpListener.dll", - "source": "vendor/pwsh/System.Net.HttpListener.dll", - "bytes": 534312, - "sha256": "5EF8C9B43FEDE96449D0AE2C99423658C1549121F31A260078E00173A330CAE4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Mail.dll", - "source": "vendor/pwsh/System.Net.Mail.dll", - "bytes": 481064, - "sha256": "B0C2C70BA2035E683E2232699E0547DF7AB4F361DAF4A5418C8F3B45EA147C13" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.NameResolution.dll", - "source": "vendor/pwsh/System.Net.NameResolution.dll", - "bytes": 124712, - "sha256": "EB7AC4D71D4DDD3A05129221F35C4C02926DAC2A9A664EBBC442B1307D6A74E4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.NetworkInformation.dll", - "source": "vendor/pwsh/System.Net.NetworkInformation.dll", - "bytes": 157480, - "sha256": "2A85A3E70D84F237E10A9BEAE7D4B1FDFE05CE0DE135D5CA45B46DE48491CDE3" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Ping.dll", - "source": "vendor/pwsh/System.Net.Ping.dll", - "bytes": 91944, - "sha256": "1269783428EFC8C8A430C20ECEE9F931AA40D790B953E1D6C97FAAA71AC660B7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Primitives.dll", - "source": "vendor/pwsh/System.Net.Primitives.dll", - "bytes": 210768, - "sha256": "6CA45C7299B6B76A5650F01E63A3290AC50432C6301CEA135C89FFC54AF72E25" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Quic.dll", - "source": "vendor/pwsh/System.Net.Quic.dll", - "bytes": 358184, - "sha256": "11F53F8A10F14C789953C82CBEE8BEF8A9F0DDF3F86527125FBC6797E8E3860D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Requests.dll", - "source": "vendor/pwsh/System.Net.Requests.dll", - "bytes": 374568, - "sha256": "663A98F7F7A8082FA47A7197F362041E1479BF0A50D57A00872A2F128BD97121" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Security.dll", - "source": "vendor/pwsh/System.Net.Security.dll", - "bytes": 665424, - "sha256": "2CA204D66FE544FEF6D92E0EA1CDD2DF2F80160E6FBA6D04E3BA80382C69694D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.ServerSentEvents.dll", - "source": "vendor/pwsh/System.Net.ServerSentEvents.dll", - "bytes": 83792, - "sha256": "BF227E04A1D642D83211A4A1136FB31946B8B7148BBA5DDE2310D7ABCBCADE71" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.ServicePoint.dll", - "source": "vendor/pwsh/System.Net.ServicePoint.dll", - "bytes": 15144, - "sha256": "C77F8868279BF5F0D7129FDF5240ABC2527437B8928E8ABF06BC774438416C64" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.Sockets.dll", - "source": "vendor/pwsh/System.Net.Sockets.dll", - "bytes": 530216, - "sha256": "B0386EBC2A62CDF0BA4D36973B3347C4372D9BFBCEFABD8E3CDD1B0194753653" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebClient.dll", - "source": "vendor/pwsh/System.Net.WebClient.dll", - "bytes": 165712, - "sha256": "52A615FD624996B513848D1CA1BDA1D02D49991AF0A000909779DDBF35FB17EC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebHeaderCollection.dll", - "source": "vendor/pwsh/System.Net.WebHeaderCollection.dll", - "bytes": 63272, - "sha256": "59E2FF9EE6AC647929BD689486F141645C2412B5B01CBF8A397D2888ADF0E764" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebProxy.dll", - "source": "vendor/pwsh/System.Net.WebProxy.dll", - "bytes": 42832, - "sha256": "41526195B63CD77C37DF1A99F621E2E5A8F40666E9613D974E9935D99F662905" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebSockets.Client.dll", - "source": "vendor/pwsh/System.Net.WebSockets.Client.dll", - "bytes": 104232, - "sha256": "45996C74C3C6C2E144EC006DEB5C70781F2E3C333782B815BB44B612D41EA27E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Net.WebSockets.dll", - "source": "vendor/pwsh/System.Net.WebSockets.dll", - "bytes": 243536, - "sha256": "7EF764FC11991DB23A90920DC3D49BBDDC6D2F766529D19463B17EAEE720CA71" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Numerics.dll", - "source": "vendor/pwsh/System.Numerics.dll", - "bytes": 15144, - "sha256": "49351764F0118538F1E531D91F416F325F362F6638C55896AF3D0FF631296B05" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Numerics.Vectors.dll", - "source": "vendor/pwsh/System.Numerics.Vectors.dll", - "bytes": 15656, - "sha256": "F7D2DBA3D30B09FA8CA802CDA5D0EE0827CB3AA44E1E19F790F62C390DDF5DE8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ObjectModel.dll", - "source": "vendor/pwsh/System.ObjectModel.dll", - "bytes": 79656, - "sha256": "9A7D09EF51C648C4CE51A0E21CDC64DEDE904420CCEC224D62C09FCEEE6B150A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Printing.dll", - "source": "vendor/pwsh/System.Printing.dll", - "bytes": 968488, - "sha256": "1CB55A01A300A2EBC0C8E14BD498005F4B34A0F4FC5F5CF7A88D50EC8AB8DCE6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.CoreLib.dll", - "source": "vendor/pwsh/System.Private.CoreLib.dll", - "bytes": 16033616, - "sha256": "63D83DD8169F23820F5D3BF0F0399F60E5E26200361787DFCB1C24CABC76E423" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.DataContractSerialization.dll", - "source": "vendor/pwsh/System.Private.DataContractSerialization.dll", - "bytes": 2062120, - "sha256": "A34745F8431BA8D38EAC37A65BCBD644C0F50AB88AFD66AF236A9A49C693B9F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Uri.dll", - "source": "vendor/pwsh/System.Private.Uri.dll", - "bytes": 255784, - "sha256": "D14188A1352F793F862726FA2C956DC270FB0E2578D46C6561F682FF2B4E110E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Windows.Core.dll", - "source": "vendor/pwsh/System.Private.Windows.Core.dll", - "bytes": 1906472, - "sha256": "0341B9E7466D143656E804D694A3C0B0BDB040FD44DE709047873661EC638884" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Windows.GdiPlus.dll", - "source": "vendor/pwsh/System.Private.Windows.GdiPlus.dll", - "bytes": 427816, - "sha256": "4ED46AD143DB84BDFA38C0973F1525296264FCCCA14AAB7D8C177982390C8260" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Xml.dll", - "source": "vendor/pwsh/System.Private.Xml.dll", - "bytes": 7788328, - "sha256": "65F1252BAD1A18D985A55D6434608E4A66DBA8A426310B2C6FDAE12B09E3FAF6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Private.Xml.Linq.dll", - "source": "vendor/pwsh/System.Private.Xml.Linq.dll", - "bytes": 390992, - "sha256": "8A82630530FB3AD2E29A3D755BCE1F50497EC764804DDC7B014428A88E6DF16C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Context.dll", - "source": "vendor/pwsh/System.Reflection.Context.dll", - "bytes": 190256, - "sha256": "B7B226B5E08F84B6D6DDF77EED92A112ECA3D61087EF689F5BCCEB8996A736AE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.DispatchProxy.dll", - "source": "vendor/pwsh/System.Reflection.DispatchProxy.dll", - "bytes": 75560, - "sha256": "827499A045D2E71731BEB0391D392436D911E9D3195663E14F3CCA0F6B13691A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.dll", - "source": "vendor/pwsh/System.Reflection.dll", - "bytes": 16168, - "sha256": "2D0A26F77691CAC4D6522D9212FD70116DC183E1035B3AE4E6087C1EB9255F89" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Emit.dll", - "source": "vendor/pwsh/System.Reflection.Emit.dll", - "bytes": 309032, - "sha256": "F3CAEDCFF578946F80ECE81947A5ABB5D905BF1567D77D05C0725E0FF1FA1782" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Emit.ILGeneration.dll", - "source": "vendor/pwsh/System.Reflection.Emit.ILGeneration.dll", - "bytes": 15696, - "sha256": "9D003F3B5DF43C3062FA1E363E5F6B3ECC5D4B84213FC7397DA53E583494F390" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Emit.Lightweight.dll", - "source": "vendor/pwsh/System.Reflection.Emit.Lightweight.dll", - "bytes": 15656, - "sha256": "0461AC768E7F2E3BCD20A016C1E06E207092DF250458C868FF628C2E7766B748" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Extensions.dll", - "source": "vendor/pwsh/System.Reflection.Extensions.dll", - "bytes": 15144, - "sha256": "28B889CEA13DCE1ECFECF62CF887C1C9587255596333680C8441120E4EDA25C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Metadata.dll", - "source": "vendor/pwsh/System.Reflection.Metadata.dll", - "bytes": 1156904, - "sha256": "B453AC5B26229328268803037B95EBDFD9EA3C7AABD830513BD6A39CF73F7076" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.Primitives.dll", - "source": "vendor/pwsh/System.Reflection.Primitives.dll", - "bytes": 15656, - "sha256": "B20C388943B74385F712CDAF74CDE691F6EEE14CB5320D8136C3D40B57C02D42" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Reflection.TypeExtensions.dll", - "source": "vendor/pwsh/System.Reflection.TypeExtensions.dll", - "bytes": 42832, - "sha256": "7CEDCBAB51CACAEEECA6A766F92A2B93FD74CA88D326BADFAD090067D9D19150" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.Extensions.dll", - "source": "vendor/pwsh/System.Resources.Extensions.dll", - "bytes": 194344, - "sha256": "CFBD1384747751A7BA4CE8A16F191501CDB762567029FF57F6E768AD4D4C58FD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.Reader.dll", - "source": "vendor/pwsh/System.Resources.Reader.dll", - "bytes": 15144, - "sha256": "3E83796FDDDA54188EBE691C4B45641BB2FE7780F8E4D1928D5F4B435BB6C6F8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.ResourceManager.dll", - "source": "vendor/pwsh/System.Resources.ResourceManager.dll", - "bytes": 15656, - "sha256": "DA9AD363EE8A17881D33768D75A3D9689605C626415AF780D0FF5BA743C3A3F4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Resources.Writer.dll", - "source": "vendor/pwsh/System.Resources.Writer.dll", - "bytes": 50984, - "sha256": "E089809FA22829491D90FB8BCBA9210EE7F37A7C9D07D0FA2E333C15016B87A4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Caching.dll", - "source": "vendor/pwsh/System.Runtime.Caching.dll", - "bytes": 202560, - "sha256": "DB5D7363FFAF4616FD9FF0225DEFFE1644418D792AED29C90E63924A852A6838" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.CompilerServices.Unsafe.dll", - "source": "vendor/pwsh/System.Runtime.CompilerServices.Unsafe.dll", - "bytes": 15144, - "sha256": "3D00BF59EE665068C013BBF7258A0BF46A86CDACEC9FC5BD727ABFF9D190D3E5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.CompilerServices.VisualC.dll", - "source": "vendor/pwsh/System.Runtime.CompilerServices.VisualC.dll", - "bytes": 30504, - "sha256": "84820BF55EDAEEBCFC2F8AA5ED64ECDCB8EDA8A6ED1D44AA9DFD28C8AE362FD1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.dll", - "source": "vendor/pwsh/System.Runtime.dll", - "bytes": 44840, - "sha256": "2C733C433C3D26C0EDB1EB89C9ED7A53CB3194092D1A56A79BF14AB71F4A3425" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Extensions.dll", - "source": "vendor/pwsh/System.Runtime.Extensions.dll", - "bytes": 17704, - "sha256": "71CAB63C02F634D86377BB06C481E733347ABC22B56106B39228A1A01305DE10" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Handles.dll", - "source": "vendor/pwsh/System.Runtime.Handles.dll", - "bytes": 15656, - "sha256": "784E5ADCC91FB6C3154C3F4E9C7C4E311892A2182686205DC4C2B3118C8355BA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.InteropServices.dll", - "source": "vendor/pwsh/System.Runtime.InteropServices.dll", - "bytes": 112464, - "sha256": "F3587B04D04F05C5A22E129EBE3DABE34243315E035AB6BBD91A4A3350FBAD18" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.InteropServices.JavaScript.dll", - "source": "vendor/pwsh/System.Runtime.InteropServices.JavaScript.dll", - "bytes": 50984, - "sha256": "A865BF2368D23E2328C68DAC87B136E71E00BA8136A0CF4B7EC10F71CC3525A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", - "source": "vendor/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", - "bytes": 15656, - "sha256": "4E38FBC3D52D0EA22A13D8569D24F40C3C6043DAA28DDF242CF77288FBBE2DEA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Intrinsics.dll", - "source": "vendor/pwsh/System.Runtime.Intrinsics.dll", - "bytes": 17192, - "sha256": "58A88BA0B94E29F26C3846697A6A251FE7677CBCC7C2B8950090D4AF59109720" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Loader.dll", - "source": "vendor/pwsh/System.Runtime.Loader.dll", - "bytes": 15656, - "sha256": "B3A9500ED9E7F4A698FB46513B8C73B5AA752331597A544E2722B2C5DB262B09" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Numerics.dll", - "source": "vendor/pwsh/System.Runtime.Numerics.dll", - "bytes": 354088, - "sha256": "9F067C312E0C0C097D56BB3487933DCDA758D547480AB76E5A2987AEADD8FD7E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.dll", - "bytes": 17192, - "sha256": "CA3289757E8F3BC2BCCBC0B02205CD0F7A21AD9845417FB9496A7C9FA4117DF5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Formatters.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Formatters.dll", - "bytes": 124712, - "sha256": "7832E9DCC1A36276B2DF00427C5C926F1A48DFF2D03977632A83AD138D853013" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Json.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Json.dll", - "bytes": 15656, - "sha256": "3E23CF2C3DABB7F3D58A88838884FFA68E0A73E40EB0AFCB316BA29DDD567A38" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Primitives.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Primitives.dll", - "bytes": 38736, - "sha256": "1C2DED997862CD07FD93BD8450A98517875F7BB52A4C4E651A4D3B8832D7DB9C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Runtime.Serialization.Xml.dll", - "source": "vendor/pwsh/System.Runtime.Serialization.Xml.dll", - "bytes": 16680, - "sha256": "6E9DFAA42EEF9F406696E5D2372C100143BD47E0E95EC21158FE5FA7DD2EE428" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.AccessControl.dll", - "source": "vendor/pwsh/System.Security.AccessControl.dll", - "bytes": 227112, - "sha256": "AA6BF70A513CB43DAAD41F91BDE35740EC28456481639A57508BE02B08F1969B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Claims.dll", - "source": "vendor/pwsh/System.Security.Claims.dll", - "bytes": 100136, - "sha256": "6AE287447243764B203F58EABBC8E5B9EBCA135F6174AD73DD06AE572936DCC4" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Algorithms.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Algorithms.dll", - "bytes": 17192, - "sha256": "D679F5CAF8BCF0007E4003D5CE728EF3DE03EB88F5DC90528AEEE1D266436318" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Cng.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Cng.dll", - "bytes": 16168, - "sha256": "E6D327C11059988B2C12584040C588CAEC73334E9745FFE85C3522BC85756F49" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Csp.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Csp.dll", - "bytes": 16168, - "sha256": "629D0E04F3605F8CABCB04105EB7CA369D3AF023FBE171CBACB60476395509A7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.dll", - "source": "vendor/pwsh/System.Security.Cryptography.dll", - "bytes": 2549584, - "sha256": "ACB3E95912A1B3D2CC96BFD42346328462A930AC77858DFC62D7D712CEED4F5E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Encoding.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Encoding.dll", - "bytes": 15656, - "sha256": "7D824AC50A0D6A75B8F85BA007BD84BEC6714CCFFDEBEAD00A01EDB9CCD3918E" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.OpenSsl.dll", - "source": "vendor/pwsh/System.Security.Cryptography.OpenSsl.dll", - "bytes": 15656, - "sha256": "4F6747F232BF9C3AC7B5352D818E31131DCC299503806A8190E9FBEF95D4A50A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Pkcs.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Pkcs.dll", - "bytes": 767784, - "sha256": "AC3A317E89EF612F3DC6169887C8A1B0A692121E1FF48D402F75F4A82DA4899A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Primitives.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Primitives.dll", - "bytes": 15656, - "sha256": "E17D719732B43BA6BEB751A7EF55390B9E9B9B659826FABF1288CD03AF0241C2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.ProtectedData.dll", - "source": "vendor/pwsh/System.Security.Cryptography.ProtectedData.dll", - "bytes": 59216, - "sha256": "1E5826CE27C48902506553C8BC7C648004A2767ED786979538D725BA2561E2D9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.X509Certificates.dll", - "source": "vendor/pwsh/System.Security.Cryptography.X509Certificates.dll", - "bytes": 16680, - "sha256": "752EB010626928B51584B2B58D7C67110D16E73B9ACE071ADC6009F33275636C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Cryptography.Xml.dll", - "source": "vendor/pwsh/System.Security.Cryptography.Xml.dll", - "bytes": 452392, - "sha256": "FF3E018DA84A0294FDD2A4F8A120CBC05B40E99CE4AD57CE45809AB3280C6DEC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.dll", - "source": "vendor/pwsh/System.Security.dll", - "bytes": 18216, - "sha256": "4C5D662D946BA84F5159FAA8197661AAD054B98899977F5BFC3C4F7A66AC13FC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Permissions.dll", - "source": "vendor/pwsh/System.Security.Permissions.dll", - "bytes": 190248, - "sha256": "37D4C12B1BAC234541871123511525B680089A61758CE6F28B09A18F8F0B39E6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Principal.dll", - "source": "vendor/pwsh/System.Security.Principal.dll", - "bytes": 15144, - "sha256": "40671DC07A55EC29B00DA6D45610CB663A8C03AE3A686695A9E28412AB87ACF5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.Principal.Windows.dll", - "source": "vendor/pwsh/System.Security.Principal.Windows.dll", - "bytes": 177960, - "sha256": "4C6EE4BC691D111FF4500A6D455DEEEEB3F56F14F5CD686F404BA063C8D2741B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Security.SecureString.dll", - "source": "vendor/pwsh/System.Security.SecureString.dll", - "bytes": 15656, - "sha256": "83941FEE8EB17E7215855D7D655243DC5D1D5ED631C92C39F4163D4A491AEBE8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.dll", - "source": "vendor/pwsh/System.ServiceModel.dll", - "bytes": 25160, - "sha256": "CF390C8379F3249937DA11DAD3A5BA88804A7453503A246BE01FC849B899653B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Duplex.dll", - "source": "vendor/pwsh/System.ServiceModel.Duplex.dll", - "bytes": 15432, - "sha256": "5E7D8985922C7D7D673D2D204C2F6D12E3F70F70D0E9FF15507FECD2026A8815" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Http.dll", - "source": "vendor/pwsh/System.ServiceModel.Http.dll", - "bytes": 481096, - "sha256": "4B4F5C934A4CBAD1227507570697BC75F7E332A7A5D85EC62F9A24E5897E7B32" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.NetFramingBase.dll", - "source": "vendor/pwsh/System.ServiceModel.NetFramingBase.dll", - "bytes": 477000, - "sha256": "98175B9ABD29326B1C410AA6ED82370677F1781A5A26B7A9AB8479C76A4A6EF2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.NetTcp.dll", - "source": "vendor/pwsh/System.ServiceModel.NetTcp.dll", - "bytes": 137032, - "sha256": "8169433D0DF8D3186ABD4C94CCFF3987CA267D56F5A384CF539E613C997FC8FA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Primitives.dll", - "source": "vendor/pwsh/System.ServiceModel.Primitives.dll", - "bytes": 5388112, - "sha256": "C53A7289B255E6A32E42FEB63CE24E32C2E0FAF4B5972EC2EC83BB17C4F01EF9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Security.dll", - "source": "vendor/pwsh/System.ServiceModel.Security.dll", - "bytes": 16928, - "sha256": "99EC51765CF53B36DC1064A48DB4C910EDB17C92E176E03B947E5E200B82F7D2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Syndication.dll", - "source": "vendor/pwsh/System.ServiceModel.Syndication.dll", - "bytes": 313152, - "sha256": "CF572334CFBB7791716282B0802DD2A33691B55B13FE3F65A45058E57700EECA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceModel.Web.dll", - "source": "vendor/pwsh/System.ServiceModel.Web.dll", - "bytes": 16680, - "sha256": "58A681BB145F2CDC9E996B48E40FFFAEA30DE469A535DDB985454A4AF8828D50" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceProcess.dll", - "source": "vendor/pwsh/System.ServiceProcess.dll", - "bytes": 15696, - "sha256": "203F52B3F352E0E3DF54B9F588131743EB562AF8612BFA2C2DEBAB21B29110FA" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ServiceProcess.ServiceController.dll", - "source": "vendor/pwsh/System.ServiceProcess.ServiceController.dll", - "bytes": 149336, - "sha256": "2CEC344EF1D23339A5257FA8E9B96D4842B1E15AFD04D1A56261B1990A0F9B7C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Speech.dll", - "source": "vendor/pwsh/System.Speech.dll", - "bytes": 1386304, - "sha256": "1A06AB169EACB6FB9CDF76096284284236F6C55A3F588CAA24BD7AF06A8F4A10" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encoding.CodePages.dll", - "source": "vendor/pwsh/System.Text.Encoding.CodePages.dll", - "bytes": 861992, - "sha256": "9D8D9E0FA33437502FD659FCB726E295CF439E56BDB044970FE5B715D7813E37" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encoding.dll", - "source": "vendor/pwsh/System.Text.Encoding.dll", - "bytes": 15656, - "sha256": "1E717906C9F94DAA686B68E9E86F4A1C08916DB62B05AF5A17661A5B83B9C7A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encoding.Extensions.dll", - "source": "vendor/pwsh/System.Text.Encoding.Extensions.dll", - "bytes": 15656, - "sha256": "F507CC18B0449947A86606DFE9281B89316B39C1BA60A9249E41C0A1CD30E201" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Encodings.Web.dll", - "source": "vendor/pwsh/System.Text.Encodings.Web.dll", - "bytes": 124712, - "sha256": "AB138D71B868D606ACD1D48EF17B9F12E51F75ECD297120ADD0C4552F6758160" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.Json.dll", - "source": "vendor/pwsh/System.Text.Json.dll", - "bytes": 1890088, - "sha256": "ABCECB266DFD7AF409EBA45CD2FD581D40F72D5FE916A1A95F05B32AC738968F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Text.RegularExpressions.dll", - "source": "vendor/pwsh/System.Text.RegularExpressions.dll", - "bytes": 1034024, - "sha256": "CC5C01E9C1591DF68708CA6386C1259B0AC34257336CF53106CE784F9D1175C0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.AccessControl.dll", - "source": "vendor/pwsh/System.Threading.AccessControl.dll", - "bytes": 75600, - "sha256": "78A2D7FD94410ABEFEFC9AF09FADB9FBDC7069F5572949C62E81423DC9F8A33B" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Channels.dll", - "source": "vendor/pwsh/System.Threading.Channels.dll", - "bytes": 157480, - "sha256": "104051A542F04760C2C89998A49396E0F4FCE403E86D7D976984280BF835C966" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.dll", - "source": "vendor/pwsh/System.Threading.dll", - "bytes": 83792, - "sha256": "B1AAE26002952704D7D7913170DFD3C786DE6DEA50463BB671A9AA99423320BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Overlapped.dll", - "source": "vendor/pwsh/System.Threading.Overlapped.dll", - "bytes": 15656, - "sha256": "239A90D22546A4ED01A05BFA8CEBA846E4C73F73BA6C2B3E06E176F859890DB5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.Dataflow.dll", - "source": "vendor/pwsh/System.Threading.Tasks.Dataflow.dll", - "bytes": 472872, - "sha256": "1412A7614F9FAFFE37E102384B0917B436BA3CAC685F8ED64607912C805DB9E9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.dll", - "source": "vendor/pwsh/System.Threading.Tasks.dll", - "bytes": 16680, - "sha256": "3C3CBA2C78819A31B400B52014678A4AF1DC1F482B2424E38B85E28EEF1C40D0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.Extensions.dll", - "source": "vendor/pwsh/System.Threading.Tasks.Extensions.dll", - "bytes": 15656, - "sha256": "DAAC261AD95033C320720BE3AE53263ABD6344FD548BA36F405D2D5AB39207F2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Tasks.Parallel.dll", - "source": "vendor/pwsh/System.Threading.Tasks.Parallel.dll", - "bytes": 132904, - "sha256": "47733F454E9D7DA1BEC529A36AE86DB431E8A89AD1FE84641E80C9207D91F4C7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Thread.dll", - "source": "vendor/pwsh/System.Threading.Thread.dll", - "bytes": 15656, - "sha256": "0B016736E7E4995A7E0A127A7BAEB6F8487A97433E2DD034B8CC8DD5FA1048AC" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.ThreadPool.dll", - "source": "vendor/pwsh/System.Threading.ThreadPool.dll", - "bytes": 15656, - "sha256": "104C0F37199430CCEBF3B81EB28E4C072547447075EACFF8D488954035BD231F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Threading.Timer.dll", - "source": "vendor/pwsh/System.Threading.Timer.dll", - "bytes": 15144, - "sha256": "632C125112639BD454C6E07DC2481E9EEC8D9FC300FA0EA6DF622729DE499A77" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Transactions.dll", - "source": "vendor/pwsh/System.Transactions.dll", - "bytes": 16680, - "sha256": "0F55A3E2F9BAA40E596E93C76D97C0F5B4A53775D8865F57877E77FB90B8E09D" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Transactions.Local.dll", - "source": "vendor/pwsh/System.Transactions.Local.dll", - "bytes": 640808, - "sha256": "B7CE5D6C73B6E2A298946474473C6E12AC2A81584EFAC155F85ACAD4CD1695E0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.ValueTuple.dll", - "source": "vendor/pwsh/System.ValueTuple.dll", - "bytes": 15696, - "sha256": "1C02D7D7BEF69434A1F9EA0BFAF4A83321E644A5478D39681849883BC1114A61" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Web.dll", - "source": "vendor/pwsh/System.Web.dll", - "bytes": 15144, - "sha256": "88650D1871B5BAC043665F06610799862B66303794FB76DE0DB5E58C86E1B1D9" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Web.HttpUtility.dll", - "source": "vendor/pwsh/System.Web.HttpUtility.dll", - "bytes": 63312, - "sha256": "1CFF49AD07732F67D54CFDF00100692499B24F00955DED4DB528012F5F11A9A5" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Web.Services.Description.dll", - "source": "vendor/pwsh/System.Web.Services.Description.dll", - "bytes": 800576, - "sha256": "AB6BFC2802083328D63EA2A520ED6F5DCF2AD2E0D67276A3884D577A2C13EC7A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Controls.Ribbon.dll", - "source": "vendor/pwsh/System.Windows.Controls.Ribbon.dll", - "bytes": 1439528, - "sha256": "E7E47AF351ED12E58337B7877F6E1C53A0E652D0D42DEF2CA1F61E81F0C93EA1" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.dll", - "source": "vendor/pwsh/System.Windows.dll", - "bytes": 15656, - "sha256": "8A97C2FC1A57682ED6031CB27A2D0B1BD352EEE9A4AF53C4411F8C0304DEE2FE" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Extensions.dll", - "source": "vendor/pwsh/System.Windows.Extensions.dll", - "bytes": 120616, - "sha256": "EB33D2AF1F7F76F7C179856025981A1F30766D3F7CE9F61D3A09DD22B59553E2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.Design.dll", - "source": "vendor/pwsh/System.Windows.Forms.Design.dll", - "bytes": 6092584, - "sha256": "DE308D2141B4EA269DAB0E30510116646F625A16A6493B13218E7428DBEF08D7" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.Design.Editors.dll", - "source": "vendor/pwsh/System.Windows.Forms.Design.Editors.dll", - "bytes": 16168, - "sha256": "B5B8172A40E5E72A54C6B8C4BB3C926CA4EE6248D5BEBE511B060F0D6D053003" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.dll", - "source": "vendor/pwsh/System.Windows.Forms.dll", - "bytes": 13715240, - "sha256": "65D1A845A47F955CD9E4C893E9FDCAE255F63894F45BC0B721F5DE22F223C753" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Forms.Primitives.dll", - "source": "vendor/pwsh/System.Windows.Forms.Primitives.dll", - "bytes": 3622736, - "sha256": "215C6626C23DC962B32A42B218ABD653D78EA09E3D1162C65B0C3BB836A96D6C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Input.Manipulations.dll", - "source": "vendor/pwsh/System.Windows.Input.Manipulations.dll", - "bytes": 141096, - "sha256": "B51133A16A9EA33269D1AA8A9F9CB7171FFC339D251066903D17209FAAA7360F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Presentation.dll", - "source": "vendor/pwsh/System.Windows.Presentation.dll", - "bytes": 30504, - "sha256": "9C752F71E3C79BEBF747A4BE28FA791404C30EE7486893B4D73CD3EF3DDD4654" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Windows.Primitives.dll", - "source": "vendor/pwsh/System.Windows.Primitives.dll", - "bytes": 386856, - "sha256": "7FD4DC6CF1C20115ADB7EC2519A4A2BE1805E2C2A6133E32BCF52B598020E152" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xaml.dll", - "source": "vendor/pwsh/System.Xaml.dll", - "bytes": 1390416, - "sha256": "DF6837BCC37DF3E0AC022A38F545BCF6155A57A55ADE8E4500D481A5F48A3544" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.dll", - "source": "vendor/pwsh/System.Xml.dll", - "bytes": 23336, - "sha256": "77C887B9C9D608538C78607D752E3567B090DD8BD8D41CAADE2201D34A1BE8BD" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.Linq.dll", - "source": "vendor/pwsh/System.Xml.Linq.dll", - "bytes": 15656, - "sha256": "A3581BC00766E7A78974009B2FAFB83B0D0158C3A1619AF11E9389981DE1DF1A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.ReaderWriter.dll", - "source": "vendor/pwsh/System.Xml.ReaderWriter.dll", - "bytes": 21800, - "sha256": "4E8265FE5D68F42648394F0D6CA0C06C60664F5E0C7456BD65C942C664891BD0" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.Serialization.dll", - "source": "vendor/pwsh/System.Xml.Serialization.dll", - "bytes": 16168, - "sha256": "66AC0372545EDB9C426E8E884292F81B26E6E185E7A90729695497F1FDCE31BB" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XDocument.dll", - "source": "vendor/pwsh/System.Xml.XDocument.dll", - "bytes": 15656, - "sha256": "7E9217B005D31B73E5CD83BC6DA85217677A773020AC736C67D53E6375FF5908" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XmlDocument.dll", - "source": "vendor/pwsh/System.Xml.XmlDocument.dll", - "bytes": 15656, - "sha256": "E604D3C3D891756E18817637E3323C9E67F4DAFB404C877CE3332E598669940C" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XmlSerializer.dll", - "source": "vendor/pwsh/System.Xml.XmlSerializer.dll", - "bytes": 17704, - "sha256": "AC7EFC90B0E73146CFA23A91D97E7BB05E1C3CF02416A7EE5853EB39149AE985" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XPath.dll", - "source": "vendor/pwsh/System.Xml.XPath.dll", - "bytes": 15696, - "sha256": "3E3D6A10185F44C1B78686E08DE8FD59EDF5BA4D953C2FFA4B277B369792DD71" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/System.Xml.XPath.XDocument.dll", - "source": "vendor/pwsh/System.Xml.XPath.XDocument.dll", - "bytes": 30504, - "sha256": "EF4F101B820CD5BA369C6FE06729BDC7E2E2DD023D720058B546DB32227FB500" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/ThirdPartyNotices.txt", - "source": "vendor/pwsh/ThirdPartyNotices.txt", - "bytes": 210587, - "sha256": "5A2D792AA9DAE45DA7579398CA08C0FBB47D78C563CCE492E311B9D5EB9A2142" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationClient.dll", - "source": "vendor/pwsh/UIAutomationClient.dll", - "bytes": 407336, - "sha256": "6074F520F4630DC6DAF4E1CD2F714E4123C4CFCDAD909C76ECC7CF69026C719F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationClientSideProviders.dll", - "source": "vendor/pwsh/UIAutomationClientSideProviders.dll", - "bytes": 857896, - "sha256": "1C287748EB1C5B5BE118FFFD6E9254A67D959A4EA04EF2EE245236CBD6C015C6" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationProvider.dll", - "source": "vendor/pwsh/UIAutomationProvider.dll", - "bytes": 63272, - "sha256": "3A9A965E8759EE679612C3A59BAFE70DFBADA3CE1260D815B1D2D62F941105B2" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/UIAutomationTypes.dll", - "source": "vendor/pwsh/UIAutomationTypes.dll", - "bytes": 296744, - "sha256": "9A26003F55DAC193DAEED86CA35BC01B72DBBEDBFDFF9846E150CEEF80D3D70A" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/vcruntime140_cor3.dll", - "source": "vendor/pwsh/vcruntime140_cor3.dll", - "bytes": 134600, - "sha256": "23AB6BFC1D127318005279A4608401F5C3FB872B5BD32A706863E8D0C2C5065F" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/WindowsBase.dll", - "source": "vendor/pwsh/WindowsBase.dll", - "bytes": 2176808, - "sha256": "ABBEA4B64EFF6A29767E64F99E2574A827B43C383C97145DC9FB316964A8DFB8" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/WindowsFormsIntegration.dll", - "source": "vendor/pwsh/WindowsFormsIntegration.dll", - "bytes": 214864, - "sha256": "0C2C08B731513110D237865AB84E16A22BA05073F6301EA28E894BA343E99415" - }, - { - "scope": "privileged", - "file": "runtime/pwsh/wpfgfx_cor3.dll", - "source": "vendor/pwsh/wpfgfx_cor3.dll", - "bytes": 1956136, - "sha256": "38FFCC5A72DEDFC518D03CCC9B8F5A20D5C8EC84578B42BC30771D79955C2E11" - } - ] -} +{ + "schema": 2, + "product": "Direct Internet Method", + "version": "1.5.5-rc1", + "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", + "offlineRuntime": true, + "normalRunRequiresAdmin": false, + "installOrPrivilegedUpgradeRequiresAdmin": true, + "directUpdate": { + "transport": "GitHub latest release HTTPS", + "privilegeModel": "DirectInternetMethodSvc performs download/verification/installer handoff", + "userUacRequired": false, + "integrity": "GitHub asset SHA-256 digest must equal SHA256SUMS.txt and downloaded installer SHA-256" + }, + "bundledPowerShellVersion": "7.6.6", + "bundledPowerShellArchiveSha256": "02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860", + "files": [ + { + "scope": "user", + "file": "app/DirectInternetMethod.exe", + "source": "app/DirectInternetMethod.exe", + "bytes": 139900030, + "sha256": "FBC67BE3B361308953CD07899A160F1A79A8B20124E54B90585BE00C8B391513" + }, + { + "scope": "user", + "file": "app/DirectInternetMethod.ico", + "source": "app/DirectInternetMethod.ico", + "bytes": 33478, + "sha256": "00D25A881AB5CA731437F5C631C031FD0FC0A8223F45181ACB6240EAA28AB01F" + }, + { + "scope": "user", + "file": "app/Status.ps1", + "source": "app/Status.ps1", + "bytes": 6394, + "sha256": "EC4B00233C1A0BD462D7AD674E54254F02BF462A6D3D53ACED47888CEE4A3447" + }, + { + "scope": "user", + "file": "app/router_gateway/providers.json", + "source": "../router_gateway/providers.json", + "bytes": 7111, + "sha256": "D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD" + }, + { + "scope": "user", + "file": "docs/README-FA.md", + "source": "docs/README-FA.md", + "bytes": 2191, + "sha256": "81C4242367FB6E15EBB2E03F0B4424EFD173091A46FE724AAEB1E9F22DD1B491" + }, + { + "scope": "user", + "file": "docs/ARCHITECTURE.md", + "source": "docs/ARCHITECTURE.md", + "bytes": 1863, + "sha256": "E4AC239D27A060B5CA0EF55C46B99FC9196725B44E07AB16F67C35E162C86BB8" + }, + { + "scope": "user", + "file": "RELEASE.json", + "source": "RELEASE.json", + "bytes": 5851, + "sha256": "AFB3CC3ED64FD3D1E4D9460C1171899FAE0B9233AB96337498BAABF0CF353C5F" + }, + { + "scope": "privileged", + "file": "app/DirectInternetMethod.Service.exe", + "source": "app/DirectInternetMethod.Service.exe", + "bytes": 73627467, + "sha256": "EE02C5188BAF3997DDB56787A671AA7947D2BE895C208271D4AE5ECFE309FF46" + }, + { + "scope": "privileged", + "file": "app/Start-Direct.ps1", + "source": "app/Start-Direct.ps1", + "bytes": 26860, + "sha256": "B186872EDE733AE876FF1463E030988E9149A2CC88188F4B6C1FA5B564AEFC05" + }, + { + "scope": "privileged", + "file": "app/Stop-Direct.ps1", + "source": "app/Stop-Direct.ps1", + "bytes": 10426, + "sha256": "4E113237768506F940870CA7550EB3DD6A1B41C51879DD01D944A5E77BD96327" + }, + { + "scope": "privileged", + "file": "app/Recovery.ps1", + "source": "app/Recovery.ps1", + "bytes": 6157, + "sha256": "A406F1B23EF95C77C6B2FB4ECDB6337E582AB06D9A7927DEB31E916F06C870E1" + }, + { + "scope": "privileged", + "file": "app/SafeLegacyProviderRollback.ps1", + "source": "app/SafeLegacyProviderRollback.ps1", + "bytes": 13397, + "sha256": "522D78CB2A40A917B4E86DD7F98F4D3B076FD8A551B97C4B0C211FDFC7D9B322" + }, + { + "scope": "privileged", + "file": "bin/ctrld/ctrld.exe", + "source": "bin/ctrld/ctrld.exe", + "bytes": 7602136, + "sha256": "FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD" + }, + { + "scope": "privileged", + "file": "bin/ctrld/ctrld.toml", + "source": "bin/ctrld/ctrld.toml", + "bytes": 540, + "sha256": "B056AD8A51078208AED39744D48C4F4369AE5BA1E76F7D6F84B027C34CE1314B" + }, + { + "scope": "privileged", + "file": "bin/ctrld/LICENSE.txt", + "source": "bin/ctrld/LICENSE.txt", + "bytes": 1122, + "sha256": "5B166F4937F5D1ADED9C6D90675524ABAB6A6F0EBA1E226FA30119F37DBAB754" + }, + { + "scope": "privileged", + "file": "bin/zapret/winws.exe", + "source": "bin/zapret/winws.exe", + "bytes": 223232, + "sha256": "A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8" + }, + { + "scope": "privileged", + "file": "bin/zapret/WinDivert.dll", + "source": "bin/zapret/WinDivert.dll", + "bytes": 47616, + "sha256": "C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2" + }, + { + "scope": "privileged", + "file": "bin/zapret/WinDivert64.sys", + "source": "bin/zapret/WinDivert64.sys", + "bytes": 94144, + "sha256": "8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2" + }, + { + "scope": "privileged", + "file": "bin/zapret/cygwin1.dll", + "source": "bin/zapret/cygwin1.dll", + "bytes": 2954293, + "sha256": "103104A52E5293CE418944725DF19E2BF81AD9269B9A120D71D39028E821499B" + }, + { + "scope": "privileged", + "file": "bin/zapret/hosts.txt", + "source": "bin/zapret/hosts.txt", + "bytes": 287, + "sha256": "48EF43491901AB4336DDDFE6324C5820AC8B90F59511F138A7202DBCC8B033A3" + }, + { + "scope": "privileged", + "file": "bin/zapret/adult-fallback-hosts.txt", + "source": "bin/zapret/adult-fallback-hosts.txt", + "bytes": 448, + "sha256": "25BA72F6D56EF1C405115C7AA5D6ED1985E8F6D56F306A95D5AD800602A75288" + }, + { + "scope": "privileged", + "file": "bin/zapret/strong-override-hosts.txt", + "source": "bin/zapret/strong-override-hosts.txt", + "bytes": 99, + "sha256": "A371B754B201D1E87A9C0474BDEA1821539D7DAF20B5DA6C8F5105A1F85C8506" + }, + { + "scope": "privileged", + "file": "bin/zapret/LICENSE.txt", + "source": "bin/zapret/LICENSE.txt", + "bytes": 1090, + "sha256": "1FACB290FF6425CF6AB61FC882E71FFB0DF50FDA527FEF90D4098935097BD984" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Accessibility.dll", + "source": "vendor/pwsh/Accessibility.dll", + "bytes": 20816, + "sha256": "7E2CA83155D9F4B80909036B964461E1D020D25E107E378161AE7A16F52102A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/build.manifest", + "source": "vendor/pwsh/build.manifest", + "bytes": 3874, + "sha256": "58A354C02F0674CF951CEFF4E780F7532EAEE9C814B1FC75BDD3B943DD109F7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/build.manifest.sig", + "source": "vendor/pwsh/build.manifest.sig", + "bytes": 3277, + "sha256": "5E8F9EBCEE16A53E65BF204C4CFF40CF137D567229143126C9BA789E5B75FCA1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clretwrc.dll", + "source": "vendor/pwsh/clretwrc.dll", + "bytes": 317776, + "sha256": "557B5E6D5CC6C0BAECA172B987B023BEDB8B0C0C0ADF1352186768A926F671DD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clrgc.dll", + "source": "vendor/pwsh/clrgc.dll", + "bytes": 674088, + "sha256": "1AD35FC3FC5D8E26E007CFA21377579C748F1EB78BC7AC820813B505398E6403" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clrgcexp.dll", + "source": "vendor/pwsh/clrgcexp.dll", + "bytes": 711464, + "sha256": "2271063DED2A3A5A520380147300B483CA30403F0ACADCF03BAF8FBA0B53E23F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/clrjit.dll", + "source": "vendor/pwsh/clrjit.dll", + "bytes": 2092328, + "sha256": "8B3E57D21CC9914A0F2DF5DF3B0FA5FC426D91282846EB88DA07736CFB7416AA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/coreclr.dll", + "source": "vendor/pwsh/coreclr.dll", + "bytes": 4614952, + "sha256": "20EA763D5AD8BE8656A3123C4AD5AFDB4C714A009BFC63BB8F4906D6678130DB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/createdump.exe", + "source": "vendor/pwsh/createdump.exe", + "bytes": 71512, + "sha256": "6A1B3A1AF1AFC2B756E2E45C36A3AC0D05D3134B14D8C66BCCFDBCC1F308B4E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/D3DCompiler_47_cor3.dll", + "source": "vendor/pwsh/D3DCompiler_47_cor3.dll", + "bytes": 4751568, + "sha256": "2D3B1B3057234778DCEBA837FF082355E6F6625AA5B68C4E69DCDF21106CEB4D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/DirectWriteForwarder.dll", + "source": "vendor/pwsh/DirectWriteForwarder.dll", + "bytes": 505640, + "sha256": "0384825B584E93D197040E7199E1C3D990190BBEDDBF65F327282D1CDA3FAE7F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/en-US/default.help.txt", + "source": "vendor/pwsh/en-US/default.help.txt", + "bytes": 3420, + "sha256": "5AD33AD117ECD78AD5E84AD745E69D45E6A6A10E68A0C7333C2CE95E102FD602" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/getfilesiginforedist.dll", + "source": "vendor/pwsh/getfilesiginforedist.dll", + "bytes": 67104, + "sha256": "153EEFB2EAFA8B2B909854CC1F941350EFB1170E179A299DE8836B8EC5CE6A7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/getfilesiginforedistwrapper.dll", + "source": "vendor/pwsh/getfilesiginforedistwrapper.dll", + "bytes": 30528, + "sha256": "7643100BB96726F8603975EAC7074174BF06F05AC3351CF8FCE0B8AAEF90ADD0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/hostfxr.dll", + "source": "vendor/pwsh/hostfxr.dll", + "bytes": 379688, + "sha256": "6D2D8B595846BFCBB5EF7B1CB1A3156AFF4930D2002B6AC813910B848C918855" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/hostpolicy.dll", + "source": "vendor/pwsh/hostpolicy.dll", + "bytes": 378664, + "sha256": "400E8547169E8D4405ADFD79510AE429F76F7A580932AC8A602EF5D88E4FEB2E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Humanizer.dll", + "source": "vendor/pwsh/Humanizer.dll", + "bytes": 845656, + "sha256": "E6AD30F638466744B2F64006AB48E8712B35D95269C08B64E9C9E6044150B612" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Install-PowerShellRemoting.ps1", + "source": "vendor/pwsh/Install-PowerShellRemoting.ps1", + "bytes": 23219, + "sha256": "A7BB6A752B186E225AB5080F0E5D26CD8598D827322D5927444508AEE937E285" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/InstallPSCorePolicyDefinitions.ps1", + "source": "vendor/pwsh/InstallPSCorePolicyDefinitions.ps1", + "bytes": 17206, + "sha256": "0B392011A5B57C05B7A8974406B20F8654CE1B32E11972DB6BABEB2765AE8EF8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Json.More.dll", + "source": "vendor/pwsh/Json.More.dll", + "bytes": 145224, + "sha256": "A34CF6FDCA5028DAC2D51F97951A13AEC724F172718568708CB9EBE2767813E6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/JsonPointer.Net.dll", + "source": "vendor/pwsh/JsonPointer.Net.dll", + "bytes": 75592, + "sha256": "D570D76B007F563490074D4BA738904F2886F9BD8F44D4C3F365D6AE51A90311" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/JsonSchema.Net.dll", + "source": "vendor/pwsh/JsonSchema.Net.dll", + "bytes": 812848, + "sha256": "262C0DFCFE5E1B2E99BA4D915E00B18C1094195643B19F04559DFDA1A8218A23" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/LICENSE.txt", + "source": "vendor/pwsh/LICENSE.txt", + "bytes": 1095, + "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Markdig.Signed.dll", + "source": "vendor/pwsh/Markdig.Signed.dll", + "bytes": 1361728, + "sha256": "17E508A3B13A1F52C463D0126004095032E4F0614CC275628F55BA267D281F56" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.ApplicationInsights.dll", + "source": "vendor/pwsh/Microsoft.ApplicationInsights.dll", + "bytes": 923480, + "sha256": "EA8B0774AFFEEC16ADFD24FC55CECCE39F778CA55E89A2FF6B2B457AFCCF44BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", + "source": "vendor/pwsh/Microsoft.Bcl.AsyncInterfaces.dll", + "bytes": 30536, + "sha256": "53029EFFB9F6426939C5B702B4B2B322E4A5ACD35CF4A1ACE8E90F38B5BDEC15" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.CodeAnalysis.CSharp.dll", + "source": "vendor/pwsh/Microsoft.CodeAnalysis.CSharp.dll", + "bytes": 18536312, + "sha256": "78533CFBF2E43A374E58F05EF2C495ED56BF93EAA63BE0EC3F2B35FC621D008E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.CodeAnalysis.dll", + "source": "vendor/pwsh/Microsoft.CodeAnalysis.dll", + "bytes": 8128336, + "sha256": "14856FD1CDB7F425323C82D276A9ADD64454EDF0325A28B588C9825838938200" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.CSharp.dll", + "source": "vendor/pwsh/Microsoft.CSharp.dll", + "bytes": 980776, + "sha256": "C412EB8721B14AFC5BAE7C8D26B4239D1DB11486F0192FA39C3A8B4F6F23A1D2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Extensions.ObjectPool.dll", + "source": "vendor/pwsh/Microsoft.Extensions.ObjectPool.dll", + "bytes": 42824, + "sha256": "68AB071B3E6031888BD4944C2CED33B4973CB4C04877D9202781F6BD41F23EAE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", + "source": "vendor/pwsh/Microsoft.Management.Infrastructure.CimCmdlets.dll", + "bytes": 493376, + "sha256": "3C3FEDB396CFB166288A4ED6726F3F7E9CC4057A1052830573BE4B3313146965" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/microsoft.management.infrastructure.dll", + "source": "vendor/pwsh/microsoft.management.infrastructure.dll", + "bytes": 309040, + "sha256": "A5138F7B4EA083FA297E744D26AF2254EE3B593186CAA750A6E4963C00F3D778" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/microsoft.management.infrastructure.native.dll", + "source": "vendor/pwsh/microsoft.management.infrastructure.native.dll", + "bytes": 362288, + "sha256": "FFA5E12A8A10634F88796D20711CA0585C42BB22652157A2D776965AEBD34EAE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", + "source": "vendor/pwsh/microsoft.management.infrastructure.native.unmanaged.dll", + "bytes": 28192, + "sha256": "9BEE4E35576355156F00E2E47EF57AA2C8CA64390C112F083A772F2219026293" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Diagnostics.dll", + "bytes": 333624, + "sha256": "50F7A79D52316975A92AAC3996248C4B842EE6CE5EB41A6D0B25667B4791E336" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Management.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Management.dll", + "bytes": 1124152, + "sha256": "556085D8CDCF6D442F0F59776DE3D74535BBFA9FACF33D1358D90BB948F6BC92" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Commands.Utility.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Commands.Utility.dll", + "bytes": 1652536, + "sha256": "D156B47A70718A54D1B5691D0FF58663D11270BC94911D870AB2895648C1057D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.ConsoleHost.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.ConsoleHost.dll", + "bytes": 603960, + "sha256": "456BE117016C7B7E474C66E48439C1917D4E9717C2EE247B6DD4E19B4B7A674A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.CoreCLR.Eventing.dll", + "bytes": 231224, + "sha256": "9EF54EB74EDA1C44BB1E7B71218DE2887A0490A526A3BAC967DC319ED3A74D3A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll", + "bytes": 993048, + "sha256": "D0CAC79C0CAFC557A981B1B7F3AB79544E764056B5CAF9EAC16A0095009E421E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", + "source": "vendor/pwsh/Microsoft.PowerShell.GraphicalHost.dll.config", + "bytes": 4904, + "sha256": "04EF1B37822130AE6AC35D2CA9CB5224C87FBEB3AD2B0C48F953C87B083CC26C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.MarkdownRender.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.MarkdownRender.dll", + "bytes": 55104, + "sha256": "08A7A5C37E857A0197C710B8D9B52A4269AFE12F00374C077389E3C7DBD584E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.SDK.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.SDK.dll", + "bytes": 142624, + "sha256": "8637EC9E121C43B3280ECAB0AD12DDC76424AE08EE6A84A47F10548BDF076232" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.PowerShell.Security.dll", + "source": "vendor/pwsh/Microsoft.PowerShell.Security.dll", + "bytes": 345904, + "sha256": "2944F4A3EA39EE33FA783A751037CCC7DA4B4A6AEC57DB7F652FEB544BE5D082" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.VisualBasic.Core.dll", + "source": "vendor/pwsh/Microsoft.VisualBasic.Core.dll", + "bytes": 1193808, + "sha256": "788558DDB1196D021FE17CCEF9DA03CC1C8C95174CC7A6CD56D0049AE65C2227" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.VisualBasic.dll", + "source": "vendor/pwsh/Microsoft.VisualBasic.dll", + "bytes": 18728, + "sha256": "4B8D9F34CC136D4F5FE156D3C0CCFA526231D042BE68DD0F412388EF7E0191CE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.VisualBasic.Forms.dll", + "source": "vendor/pwsh/Microsoft.VisualBasic.Forms.dll", + "bytes": 247592, + "sha256": "44A8EBEBEDC5746BBC22411BF34A5C869E3428908DB2D12B89270D1C762264FE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.Primitives.dll", + "source": "vendor/pwsh/Microsoft.Win32.Primitives.dll", + "bytes": 15656, + "sha256": "0327272BF4555EBD266751DFB3AC42463C8C0F266CDFD352263D4C7AA8FBAF04" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.Registry.AccessControl.dll", + "source": "vendor/pwsh/Microsoft.Win32.Registry.AccessControl.dll", + "bytes": 34600, + "sha256": "EA85FD3A01751393A2C4EB8D816EE50A0443C96C91CA2AEAD1F2AA15C52939B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.Registry.dll", + "source": "vendor/pwsh/Microsoft.Win32.Registry.dll", + "bytes": 116520, + "sha256": "FAD9AF3A76A4B89BEE820381FDE9CE06DFAADC3D90859953503F4BE81C74EE31" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.Win32.SystemEvents.dll", + "source": "vendor/pwsh/Microsoft.Win32.SystemEvents.dll", + "bytes": 116520, + "sha256": "75DA025453C94EE0D959A8077ADC2A69B05E28B987BC5EDF93887DF6C1CB393E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.WSMan.Management.dll", + "source": "vendor/pwsh/Microsoft.WSMan.Management.dll", + "bytes": 542512, + "sha256": "B95BB3EBAA7F562BA3EEA65B78429E267819D65A71F5839ADFD498EA695BB09F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Microsoft.WSMan.Runtime.dll", + "source": "vendor/pwsh/Microsoft.WSMan.Runtime.dll", + "bytes": 157472, + "sha256": "D51427C022C1CEB37DE10991BD712F491B15D9BF8E0D42A3270ABAC050DFA0E3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", + "source": "vendor/pwsh/Modules/CimCmdlets/CimCmdlets.psd1", + "bytes": 15219, + "sha256": "5417424A8746E0024526C21D09DD309F8B04B25BE711F99DF4F1507E8F335D70" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/.signature.p7s", + "bytes": 12873, + "sha256": "1C4EE42E1FA99A2768FA8FEDDAADF70A84F8EA8F273234061A6781D41DA0FAD4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/en-US/ArchiveResources.psd1", + "bytes": 17109, + "sha256": "15C2C0874890E5C715AA513C744CA99803A2629466CBB3C62E41AE3CFC857D66" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", + "bytes": 14859, + "sha256": "95E193E52B66E19C8ACD007AB2958B5020B3FDFF52227162B09C5110F01E2685" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", + "bytes": 77022, + "sha256": "48F095C3A48F95AAB0714C06A6EFDB798C77F957948AAF02CAA16CF50692D2F7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", + "bytes": 18243, + "sha256": "01EE5A86A4E774DBBACD911B7CF9070C72C6EF47699C1B94D70C8DF5FA9F0FA9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", + "bytes": 20849, + "sha256": "BD36F3F1242E75599579D6E3F805AF77663767C4DBF3750413FC6794B2EA5D12" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", + "bytes": 20298, + "sha256": "DD35B80DA89FF364BBA431A26223DE85252B3320C06D6489C5ECAE33A4EA8320" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", + "bytes": 14887, + "sha256": "F511D03F0A312CB4C74D1FFAC6E777190A37FDF057388F0DA3AC0DE8CE9CCEFF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", + "bytes": 14721, + "sha256": "BB209A688D755B76351364FE9B4ABB224F5BF881D9281383C7D0A62ACCFFCE63" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", + "bytes": 16092, + "sha256": "4BFCB08F3F388A74BE6DA9B765585974BE9AA37EE4EEBFFE52FC1BF762C25DB8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/.signature.p7s", + "bytes": 12873, + "sha256": "882CEF83D74AD2226EC51479280913B0B6C919DA35BC7365438D6EC540FB1018" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Core.dll", + "bytes": 429624, + "sha256": "0C85C8CC3856D5BEB91895D1A8E00F785453F2AA538D85EE9ACE030B537F0FC2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Azure.Identity.dll", + "bytes": 357944, + "sha256": "B960AB88C55A7EDF0FF7A659CBC88FDA2DC8219221D46B7FCB92245B8E8EEFB4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Bcl.AsyncInterfaces.dll", + "bytes": 26696, + "sha256": "FD587EF95461251B8D4B3D493D77102120622B1FD32B87BB98E032E1365480A1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileProviders.Abstractions.dll", + "bytes": 22048, + "sha256": "FA1FECF3C081A0EF366100169CD539AD03EE10CD18BCF89DE56E9CB8D3659D4F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.FileSystemGlobbing.dll", + "bytes": 45088, + "sha256": "152BEBA2D6F82210F17696B2BE9498ADC9C38D32B9E2FD03BA87810B838F811D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Extensions.Primitives.dll", + "bytes": 44064, + "sha256": "7C6BC24A7D6A1C3C1FB4FC521174CF43E931A19F8BB88754FB738A6402848E20" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.dll", + "bytes": 1664544, + "sha256": "924340C47CBC74B1B811059860DEFA3B136500BB97EF8B6DB95631F2391741D0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.Identity.Client.Extensions.Msal.dll", + "bytes": 65568, + "sha256": "B084586684B03456481A6AA0AC0B149F87EBBC6FB01735D8005F987031BD0343" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Microsoft.IdentityModel.Abstractions.dll", + "bytes": 18976, + "sha256": "7A77F7894D7F925C2E806737B8F4F036CD0D8B7E7843F824BA1139C10A0B3E25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/Newtonsoft.Json.dll", + "bytes": 732328, + "sha256": "1DC3D1DB10368906573D443667CED170FD7A01F3349D93906A8B60F1D914B6BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Commands.dll", + "bytes": 613824, + "sha256": "762988B531247A8AC282B85E0BC81004FA9D02B9118A24C3B18B3A97F1DADE48" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Common.dll", + "bytes": 122800, + "sha256": "E8932AC0C5D7E306551BC4B585F582BAEF7651F21C88D4976445C0BFDF29F227" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Configuration.dll", + "bytes": 170424, + "sha256": "0BB37C45575CBCCCC279A4250FB7F0D87DE5C0A3DDC16565542A6ED0E5682C6B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Credentials.dll", + "bytes": 67616, + "sha256": "E5F44DDC086A8F40989C70460E22E25095D75AC402FAF6FF436E6A08C9CEC71F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.DependencyResolver.Core.dll", + "bytes": 93728, + "sha256": "077951FEA107ED42180AA5EAAA0BE047070A0EFF32A62999A8280535CB5D4F3D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Frameworks.dll", + "bytes": 131104, + "sha256": "C79E07959989FE7E99F31171EF712F9665474E652B4C16BC729160114697F8D4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.LibraryModel.dll", + "bytes": 56760, + "sha256": "EAF71D1D5DD771DAC1510C8779C2FDC98D539C8C07934EE114679D453FA60C21" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Packaging.dll", + "bytes": 689088, + "sha256": "877E82582D7802ACA0B2FD53D34175B42D01BCF713A77D6D0A9E8A73C47EE2A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.ProjectModel.dll", + "bytes": 219056, + "sha256": "275A531DE109D62F71D745DB656D295511DD53CF0FD141CC0153AD357BFC8163" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Protocol.dll", + "bytes": 786368, + "sha256": "AB3FCF87ABA83AC429FB040D5C36A4B7E9AF334C923F8F27041C93046D2AF9CB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/NuGet.Versioning.dll", + "bytes": 65984, + "sha256": "77B3FAD9D3D369538F765035DD142D3D91785CE1308C40C5CA13BF84C9E78ECD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Buffers.dll", + "bytes": 41240, + "sha256": "11568C79856612F9B5AF07FA8EF0A38E524F912D98C188E2510FD7E5ED520EC3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Diagnostics.DiagnosticSource.dll", + "bytes": 189104, + "sha256": "1887592615FC08BBC91AD62307C8C11DFC14C45BF120D271D66BE4A0D1A149A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.IO.FileSystem.AccessControl.dll", + "bytes": 28552, + "sha256": "E6026501AD4056FF2F1655B0AFDFE8923BC6E8FBAD67E1E9EF56E3002F49FBB9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.Data.dll", + "bytes": 31016, + "sha256": "6CF06482F4FD82AF659C2EB4B7BDEAE51EC171AB1CA3B3CBF0328C6A4B882242" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Memory.dll", + "bytes": 162624, + "sha256": "2728C8F0D153C72D3F50363FA1762536675C303F4CF4E7502EE3F099B3474554" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Numerics.Vectors.dll", + "bytes": 126152, + "sha256": "E5B1E3DEFA97A3AA29CC20BE0C9D525C86BB9C716F3B85F9A82CE1F30FF6310E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Runtime.CompilerServices.Unsafe.dll", + "bytes": 38424, + "sha256": "2154F3A7C647BD58C9DA47650E10CC42F906B7F7F1080211023917CA9684839F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.AccessControl.dll", + "bytes": 33672, + "sha256": "1FDA491EEBDB19EA0A83CF6C16AB5DD004A1BFDFC845EDE017EBE0945BEB927F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Cryptography.ProtectedData.dll", + "bytes": 23696, + "sha256": "A79AD1E9E7205BD790F40D6C1900BAF1A9C8CAA06089878D24ED3599AC3228A9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Security.Principal.Windows.dll", + "bytes": 18312, + "sha256": "B4D8E15ADC235D0E858E39B5133E5D00A4BAA8C94F4F39E3B5E791B0F9C0C806" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Encodings.Web.dll", + "bytes": 99408, + "sha256": "A58026527BED9F3098CA3D016A7B6C93BD6F0134092F484B099970EC3D839AB9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Text.Json.dll", + "bytes": 644904, + "sha256": "0CF5A9763C98F09E94DFCDAA4437E9508EABE2E50B408BA421D5FAF734C5556A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.Threading.Tasks.Extensions.dll", + "bytes": 25984, + "sha256": "4F81FFD0DC7204DB75AFC35EA4291769B07C440592F28894260EEA76626A23C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/dependencies/System.ValueTuple.dll", + "bytes": 25232, + "sha256": "E905D102585B22C6DF04F219AF5CBDBFA7BC165979E9788B62DF6DCC165E10F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/InstallPSResourceGetPolicyDefinitions.ps1", + "bytes": 17567, + "sha256": "7429201A7A4BE49FEB0456DEDBD47E9E753DDA4EE871A8E6B977A0917F96745D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/LICENSE", + "bytes": 1095, + "sha256": "BE5723395260D4F07283D596F4B375810EB695653D48D52A5062C0F7070573A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.dll", + "bytes": 433184, + "sha256": "C9ECCB0E5313CED30E7DA3903C27F1A97C6E8B0793A0A91160C4BEE346F3EE02" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.pdb", + "bytes": 138856, + "sha256": "A8890484E98D618F366A5AADCF34F9AB8D9D2C51AC08E7E4789E7C733D62B653" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psd1", + "bytes": 20644, + "sha256": "13A0C3EA11F35E08DD1EE06A15EE9EE793062752EF5DA63B43E7C4BC64976328" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Microsoft.PowerShell.PSResourceGet.psm1", + "bytes": 17978, + "sha256": "303A5B8DD3749DA6A7530E2F040DDC1ECFCCA280120DCEDA473EBB2285661CE8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/Notice.txt", + "bytes": 158819, + "sha256": "CB3714C1F7FC609FEE9264C5F7E87DF89121D09242A4DCCEFE4815E9D784AA65" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSGet.Format.ps1xml", + "bytes": 23783, + "sha256": "097FE2BBE4C6BC7829C64114E46E51479574B5B9E4B2F84D148F29B2FAE0BE9A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.adml", + "bytes": 1364, + "sha256": "1C8163F8163797C4B91466A94207E356256B8F770A0C591C68CD1FADD015A79A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.PSResourceGet/PSResourceRepository.admx", + "bytes": 1839, + "sha256": "37BCB0428C05343483E7B5AA2F76AD3F804BA87830526EC42E8F8A91736D4AA9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", + "bytes": 15415, + "sha256": "9CBBDDBC38465378D4FDF3529B74C39161B728EF295974B5B9D90FD5A87806C3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml", + "bytes": 20141, + "sha256": "B226CC25C17CDD19CE028EA22B3B596EF9A3A8C8E3C58BF3A9E76E45D9F38514" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/.signature.p7s", + "bytes": 12812, + "sha256": "3DB2F058A8F27F3E7BE0E22E8258944F9A6412A379DFFC66F99F3E592899DEC4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/en-us/Microsoft.PowerShell.ThreadJob.dll-Help.xml", + "bytes": 19596, + "sha256": "6E5AA422BFDE0133D9FC12CAB32C404557CA7CDDDA1FF888BF69395595928003" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/LICENSE", + "bytes": 1095, + "sha256": "296604F12546653ED97726DC5FC2F3945D4CCA1F07ABD2589F531647177B06FC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.dll", + "bytes": 34840, + "sha256": "F89AB468EC1329474288FB0FAAD81600005BE3DA88DC2B8A2631CFBDD64F394A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/Microsoft.PowerShell.ThreadJob.psd1", + "bytes": 18088, + "sha256": "4E9CB7A6D8A94560F0025B807162BAAA9C4A6AF4254D3FA7E71B23E0DCC1FB25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.ThreadJob/ThirdPartyNotices.txt", + "bytes": 1979, + "sha256": "296FF3E4A93BC8C20CA5C20D0E1A70B8C4622FADF0351A37845A63ED52585279" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", + "source": "vendor/pwsh/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", + "bytes": 16830, + "sha256": "BB9BD6F3EAB1CF227E1AFAFEC89934E6EB10C6C8530908C6436849120F631D9C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", + "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", + "bytes": 15033, + "sha256": "8E06AA0AB008337C59AEBB32FF672BCD1646FA927DDF4E4EFE82AC454AAB30B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", + "source": "vendor/pwsh/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml", + "bytes": 24000, + "sha256": "6A47415133889D1D676C6170DD14DC43C2C983DC6ADAFE73DC0F66891E5150FE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.ArchiverProviders.dll", + "bytes": 89504, + "sha256": "5A140EB9BFB2EBB42F02E75DA9A408A86BA6034EAA27AC57A10A534011EFD509" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.CoreProviders.dll", + "bytes": 72080, + "sha256": "50CB7269866EC2E3663221662FD1649A0C1B2736525D65231E29B39B25BB2467" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.dll", + "bytes": 318352, + "sha256": "23689DD357312F0924305C612AA41BE0BD592AD9712FED70A3DA72A9C40EB712" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.MetaProvider.PowerShell.dll", + "bytes": 83864, + "sha256": "8F5BFD7FFFFE4930AD035194447B8FB24257C8C799302F04879CD5D5211A38C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PackageManagement.NuGetProvider.dll", + "bytes": 290192, + "sha256": "C0579B5BCA0ADFE99DC17C253F4F00D669AD008A60854D2508BF6D79A886B4B2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", + "source": "vendor/pwsh/Modules/PackageManagement/coreclr/netstandard2.0/Microsoft.PowerShell.PackageManagement.dll", + "bytes": 189328, + "sha256": "10D3F4E3AA47CFC3AAC0E94C3B20000EA73CB0062E5037A79FCBE932112EB0A1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1", + "bytes": 28469, + "sha256": "B7CD11228923E8747B04AE8A03F246F1490B4D51C8A54B41D26CCB20164FE558" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.schema.mof", + "bytes": 31676, + "sha256": "8D9E6819AF5265B47DC514F82EEEF657449C2F3DC72CE4F4F8531FC2FCCDECF0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.strings.psd1", + "bytes": 31412, + "sha256": "50EE9087694A8325E0BC6FF4E5AE008984D46FF84CB9BCC6ED655CFF1A8547A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.psm1", + "bytes": 53524, + "sha256": "56BD33242E6D59F02514EB3B902F65B83A3EA8EA7E44099F57892AB640823C25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.schema.mof", + "bytes": 31566, + "sha256": "ACD658401A6A84F29E091F6549F4AD6BC6E16B71005F542E4BE558C40B50EAFA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/MSFT_PackageManagementSource/MSFT_PackageManagementSource.strings.psd1", + "bytes": 32494, + "sha256": "AB2DD00EE1AB028EF4C506CAC98E5407FB866DA40720C232A3426ADBB57EA67A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.psm1", + "bytes": 24136, + "sha256": "CA8EA0F3E2208FDEDD51C2F282EA9E9A50BE7D8A38C56EEF0141191F7E484776" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/DSCResources/PackageManagementDscUtilities.strings.psd1", + "bytes": 31104, + "sha256": "24E1182E6F6E3EDDF2528EC6D335E0D584CE37995B0D849B5DD5F5EE75A558BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.format.ps1xml", + "bytes": 32443, + "sha256": "6CC6DA35A87357E213FA7A2CD0FAF9B1DB1BB83BDAAF9F73C952FE9AD2BB4596" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psd1", + "bytes": 17889, + "sha256": "EC633E756164C26D1154FAB722648E2737F3650399856D29C089F0C25B190D33" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.psm1", + "bytes": 16422, + "sha256": "D484E31593FE26C8E670F8A0D72D4400A3E8EB75F587DF4B11554B8C000E128A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageManagement.Resources.psd1", + "bytes": 14984, + "sha256": "734BFFF81999A08B4145399A6284B00E46F070A7038BB4C82AB79165FF15F543" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", + "source": "vendor/pwsh/Modules/PackageManagement/PackageProviderFunctions.psm1", + "bytes": 25115, + "sha256": "481FDED09BF8A376EC4BD53CF34C0D66C8D8B1D31AB2012A768227F92B099156" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1", + "bytes": 15939, + "sha256": "4081ABD182F805AB3B9705279478A2A8AF17A0E43F07887E3C68962BCD09C9E8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.psm1", + "bytes": 38882, + "sha256": "6C724F3B86D5D5233C2906FE41206D6EB2279B6D053164F51AAF83B4891619EB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl", + "bytes": 1766, + "sha256": "CF126CC6FD7AFAEC54F07942F346F379725795625B80EC5FC32F547AB7B49BFC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof", + "bytes": 14446, + "sha256": "2107051FAB99F1E8545CC30F72FB089899B19A5BD4D33A6891FBF0DF794F573A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1", + "bytes": 14171, + "sha256": "7520000BE81F281384AF99B787A4629E55269EF73D8FA162CF29390DC273A017" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1", + "bytes": 23645, + "sha256": "627EDB8150EC8C52B33E363652D27E4FD71C1374D03C5B56D98AF244D3C1CC10" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl", + "bytes": 1578, + "sha256": "B9F9A62A3D2D0497BF81CC69A456B9E0BDBF9D1F34909A0283F088940EFFFC34" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", + "source": "vendor/pwsh/Modules/PowerShellGet/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof", + "bytes": 14325, + "sha256": "6AA312F0A73494723FE4CAA98BD74CCF3724226BEE4BFE4EC0A7229BFAD7141C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/en-US/PSGet.Resource.psd1", + "bytes": 59328, + "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1", + "bytes": 20118, + "sha256": "14A35134461ABCFAA7DFE393C7B3A16605032719F5EA0EE062B4655A181C0C76" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1", + "bytes": 15108, + "sha256": "1302F09514E514B4A2C5A2F8D4DFCFC5087070E211357319FB516E27994C88B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1", + "bytes": 27387, + "sha256": "640240A2632C22E75AE372414BEE7136C2ADC932F92A0F656C436BE616B19CDA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/PowerShellGet.psd1", + "bytes": 23683, + "sha256": "B1315712ADE77AF6C5115D8C9AE2F9A5D8CCBFA5E9A5F00C19AB283A52057F2B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", + "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Format.ps1xml", + "bytes": 23680, + "sha256": "4F12DD41E0FAB7B1A97AE83A84BDDB567CE9876F9E6C3496D833CFBB8E52BD7F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", + "source": "vendor/pwsh/Modules/PowerShellGet/PSGet.Resource.psd1", + "bytes": 59328, + "sha256": "B5C9DA477EF0F8662708AC1A755C0B2611800805ABA861663ABB421E0C5F7CA4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PowerShellGet/PSModule.psm1", + "source": "vendor/pwsh/Modules/PowerShellGet/PSModule.psm1", + "bytes": 1368306, + "sha256": "573D4A3861CB967E52EAF6155A41F930FCD4168368A6946F65D985F30DC357EC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", + "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psd1", + "bytes": 14978, + "sha256": "1DCFF05CD03BEC2F6117BB7267DADDB9770CB10FA6FE4AB9D892E87DA15349F7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", + "source": "vendor/pwsh/Modules/PSDiagnostics/PSDiagnostics.psm1", + "bytes": 26145, + "sha256": "2697A9EC7792940DA5F3D91CCAC24B1A3948CFF4E5901D5B98B7689A1C4A9DBF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/.signature.p7s", + "source": "vendor/pwsh/Modules/PSReadLine/.signature.p7s", + "bytes": 12873, + "sha256": "ECDF2C3BC44D32AF003FD81BEF96E83F19D7EFC9658C07F7D78687F105F721A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/Changes.txt", + "source": "vendor/pwsh/Modules/PSReadLine/Changes.txt", + "bytes": 41347, + "sha256": "E18C4D35A40A544877378B36B7D86B5C79DAED0C6E7A55C3027C4F0F998426C0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/License.txt", + "source": "vendor/pwsh/Modules/PSReadLine/License.txt", + "bytes": 1324, + "sha256": "25C2FDFCDC653F65629233F5EF217A83287733D3C0299C207B0BCA865508C463" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", + "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.Pager.dll", + "bytes": 16784, + "sha256": "451994C3D3E38D939B4FA5F8594D0207840F45E7A210AA75F81AEF85AA17C592" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", + "source": "vendor/pwsh/Modules/PSReadLine/Microsoft.PowerShell.PSReadLine.dll", + "bytes": 339528, + "sha256": "F8E3A5B7E3E8CAD2130CE10647564A2A0EA15D98DB8A0CC8D589F80154C108E2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "source": "vendor/pwsh/Modules/PSReadLine/net6plus/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "bytes": 14920, + "sha256": "6D33EA289B405C64BF5DAA9C59E459596CAC59ECE399D0D215EE9F3D46AFD675" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "source": "vendor/pwsh/Modules/PSReadLine/netstd/Microsoft.PowerShell.PSReadLine.Polyfiller.dll", + "bytes": 16928, + "sha256": "2E63DC86D9240243CCFB07DA5D0B4FCE2BC5CE5F9952658A7CE68C26BCD3E14C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", + "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.format.ps1xml", + "bytes": 26820, + "sha256": "1CA887463598D38AA18236A97A4193B2228D1D8434FA73CC8C7932F42A0565CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psd1", + "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psd1", + "bytes": 15471, + "sha256": "DD8766BD4DB0C1D17111B81D145D32448F61F48451D51C780A79BE9CA98D837E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/PSReadLine.psm1", + "source": "vendor/pwsh/Modules/PSReadLine/PSReadLine.psm1", + "bytes": 15076, + "sha256": "9BC0252F616067CA5EF1D0328EC0B97C7D31C757F9FC80ADEE234EA6132A9C7E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", + "source": "vendor/pwsh/Modules/PSReadLine/SamplePSReadLineProfile.ps1", + "bytes": 41594, + "sha256": "53B518B3D72AFEB6C8A3F4ABF079C93CEBAF7100A12C93668B83A4D4BCD45795" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscordaccore.dll", + "source": "vendor/pwsh/mscordaccore.dll", + "bytes": 1366744, + "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", + "source": "vendor/pwsh/mscordaccore_amd64_amd64_10.0.1226.41902.dll", + "bytes": 1366744, + "sha256": "96ED8DAC77D1D8AFF5757933283B3C9AABB25151DFE61317AAB3EE4F85E13C51" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscordbi.dll", + "source": "vendor/pwsh/mscordbi.dll", + "bytes": 1247992, + "sha256": "1875AB683E30F79B39FF861F440A246561B72247C22366615B97CC14537B4808" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscorlib.dll", + "source": "vendor/pwsh/mscorlib.dll", + "bytes": 59688, + "sha256": "E7BE6C67FA82F5F0C10C6553D82CCC2854DF3B49CF8DA706629F80A34A1AE309" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/mscorrc.dll", + "source": "vendor/pwsh/mscorrc.dll", + "bytes": 133928, + "sha256": "11917AEAEC22F310A26A466E7D57CDCAB40F6ED1233B286E39AC675E881A7DAE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/msquic.dll", + "source": "vendor/pwsh/msquic.dll", + "bytes": 536344, + "sha256": "B7158D427B01C70483BB3D80C5211E387DD98E9471C8A70E4484D449DE9157E8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/netstandard.dll", + "source": "vendor/pwsh/netstandard.dll", + "bytes": 100648, + "sha256": "42388B4A19485ADC2A7D384F5257BB9D806F3BAC6F8871DDE42AFD944BBFC0C5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Newtonsoft.Json.dll", + "source": "vendor/pwsh/Newtonsoft.Json.dll", + "bytes": 1824584, + "sha256": "2B6FDDE54CC2D2E9277D458A42FDF9FCA30FC4299652E97144A31C90FEC91829" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PenImc_cor3.dll", + "source": "vendor/pwsh/PenImc_cor3.dll", + "bytes": 157992, + "sha256": "962CC2EB9DBBC45680649AC084685FB0E57DA4DE09D3DD5762188D10B76F598C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/powershell.config.json", + "source": "vendor/pwsh/powershell.config.json", + "bytes": 190, + "sha256": "98C0E5B6EE17EB8B8F4E4940C2B2528689CEC8470DB4BDE427AD16D90D6A52D4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShell.Core.Instrumentation.dll", + "source": "vendor/pwsh/PowerShell.Core.Instrumentation.dll", + "bytes": 106808, + "sha256": "AEEB8062AAFF1057E90E9485F610B508B1179AAC4DF7FC465090BDEE49E3CFF3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShell.Core.Instrumentation.man", + "source": "vendor/pwsh/PowerShell.Core.Instrumentation.man", + "bytes": 227126, + "sha256": "8B2585E50EDE697905DFDB8E3878DA2AB42039135BA23E377207CC8D96E8125F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.adml", + "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.adml", + "bytes": 9675, + "sha256": "2B8C4B7D5BD585431FBF918F223A9B932D80EB9703A1976A1CE3492D4DD26966" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PowerShellCoreExecutionPolicy.admx", + "source": "vendor/pwsh/PowerShellCoreExecutionPolicy.admx", + "bytes": 6198, + "sha256": "EE8EB99C1E66009D95AF46E4F6C8F5C13563C9A0AD827E631149176A1B8D34D2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationCore.dll", + "source": "vendor/pwsh/PresentationCore.dll", + "bytes": 8324904, + "sha256": "7A610FD470016E5C74E2BFC403D2E08DDFF10F1EEFCBBF3AA7B07577855EE746" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemCore.dll", + "source": "vendor/pwsh/PresentationFramework-SystemCore.dll", + "bytes": 38696, + "sha256": "12DDD4029181D0AA27D32A69777760037A85F06BC57D56478669E098417E9990" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemData.dll", + "source": "vendor/pwsh/PresentationFramework-SystemData.dll", + "bytes": 34600, + "sha256": "0075922DB11FDE4BE1E8668BB9ABF5596DA8A40367E1D7264FE44B30B83F2228" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemDrawing.dll", + "source": "vendor/pwsh/PresentationFramework-SystemDrawing.dll", + "bytes": 34640, + "sha256": "953D322E29718C113DB240EB73396E65BA5B9AA55AD25C8A796B7BEB2E11573E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemXml.dll", + "source": "vendor/pwsh/PresentationFramework-SystemXml.dll", + "bytes": 34600, + "sha256": "EBDDF88BDA059E766772C7F3BB073C4660A48FE7CAB1238B971FE6DDBA4C01CA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework-SystemXmlLinq.dll", + "source": "vendor/pwsh/PresentationFramework-SystemXmlLinq.dll", + "bytes": 30504, + "sha256": "8D5290BF6787A161D2689C03C1936BC9D28CB22292197B2EED3A57ADD05DC81A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Aero.dll", + "source": "vendor/pwsh/PresentationFramework.Aero.dll", + "bytes": 440104, + "sha256": "D64069A4E059D5F74EFAE734541A5BC54B13E00A087B7D7A0CE224B4F9F7B3C3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Aero2.dll", + "source": "vendor/pwsh/PresentationFramework.Aero2.dll", + "bytes": 444200, + "sha256": "092E068D4F0117CF5020A072A53A3010EEB54F125DA3BC8D4EA1913A3370E4E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.AeroLite.dll", + "source": "vendor/pwsh/PresentationFramework.AeroLite.dll", + "bytes": 239440, + "sha256": "B7B8BF2214F9653CA2257D1A5E1DD0D978FAF15BFDE826F0ED3EE57B6A1A9A90" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Classic.dll", + "source": "vendor/pwsh/PresentationFramework.Classic.dll", + "bytes": 272168, + "sha256": "CB733EA566618B52415EB32FC32C86A3FE6BC915D471D63DED3A46AAF3071AEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.dll", + "source": "vendor/pwsh/PresentationFramework.dll", + "bytes": 15816528, + "sha256": "6A3C5B98ED693C577FF25827315D7EEE89F73DEDF0088B80B43B4AFB337018C4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Fluent.dll", + "source": "vendor/pwsh/PresentationFramework.Fluent.dll", + "bytes": 980816, + "sha256": "E33301B7F7A38B9A5065127F79EC3860A6827B30BD1C85A669E3C3AE191C4258" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Luna.dll", + "source": "vendor/pwsh/PresentationFramework.Luna.dll", + "bytes": 669480, + "sha256": "F9030C4D064B7F097B6F73FAA9C7683BEFB7A1A4C18C2D6DD741EB34A2325658" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationFramework.Royale.dll", + "source": "vendor/pwsh/PresentationFramework.Royale.dll", + "bytes": 333648, + "sha256": "118F34B2213AFE8075BE78D66FDB5B5B0C099A89F70AEE9CA09A9DC09BF07102" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationNative_cor3.dll", + "source": "vendor/pwsh/PresentationNative_cor3.dll", + "bytes": 1235240, + "sha256": "42E2E543C17E1B37499BAF00D956433B8AFB655348EC0FAE89C926FB7D149B6E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/PresentationUI.dll", + "source": "vendor/pwsh/PresentationUI.dll", + "bytes": 1259304, + "sha256": "5DE46036ACAB98C8B970BFB4234F66438EE102039F9A59A72053A9818C3ED1F0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/preview/pwsh-preview.cmd", + "source": "vendor/pwsh/preview/pwsh-preview.cmd", + "bytes": 25, + "sha256": "7A24CB96B62009CFFB38C85B6ACBF48A5DB5746459B85FF3E727BD952FFD8832" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwrshplugin.dll", + "source": "vendor/pwsh/pwrshplugin.dll", + "bytes": 159032, + "sha256": "FBF2C8FC0D8A3CD5F2DEE9E0F15315F92862B6DF26BF1B02209588A07CC0D429" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.deps.json", + "source": "vendor/pwsh/pwsh.deps.json", + "bytes": 99499, + "sha256": "DFC3A3FB00F53720610F389E2525FB268928B381E55F899B84B478502D5736A2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.dll", + "source": "vendor/pwsh/pwsh.dll", + "bytes": 157472, + "sha256": "91F9892F15A7E2DCF440AFC3A497A7D839F9D38D1AB4A50BD36080D982A01449" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.exe", + "source": "vendor/pwsh/pwsh.exe", + "bytes": 301368, + "sha256": "BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.profile.dsc.resource.json", + "source": "vendor/pwsh/pwsh.profile.dsc.resource.json", + "bytes": 5024, + "sha256": "378030311FCDD7A8D6C401142275BAEB6C031307EC2E12F51399B84851906380" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.profile.resource.ps1", + "source": "vendor/pwsh/pwsh.profile.resource.ps1", + "bytes": 20443, + "sha256": "BA90CDBF60C48C05343A7C4A451BEC2A5A133DEB10F1F7CD763E76F5D2BA7944" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/pwsh.runtimeconfig.json", + "source": "vendor/pwsh/pwsh.runtimeconfig.json", + "bytes": 790, + "sha256": "6C1779B71C67B5F40B2D0041BACAE0873320DD55F0DC3D5DF76D88C8D486275D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ReachFramework.dll", + "source": "vendor/pwsh/ReachFramework.dll", + "bytes": 1570600, + "sha256": "E0EE50A412ABA6DC2FAAD0655D5AEBCFF33587A113984CAC2777ED7B763BD193" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.CSharp.dll", + "source": "vendor/pwsh/ref/Microsoft.CSharp.dll", + "bytes": 18216, + "sha256": "AE0ECD8F42EA3EFACC2612ADE56F29FEEA9A123FCE9FAA455126EC9F4A4540CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.VisualBasic.Core.dll", + "source": "vendor/pwsh/ref/Microsoft.VisualBasic.Core.dll", + "bytes": 59216, + "sha256": "7A7E44D7AC1F870E0B7D8F3F7598CCA79E35CAB12E77C58ED46E9B6B3C78BCFB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.VisualBasic.dll", + "source": "vendor/pwsh/ref/Microsoft.VisualBasic.dll", + "bytes": 17192, + "sha256": "E192508ADDE6A758138A5A8E28998DCCB64E7F45EAF30D8AB3564F87749028B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.Win32.Primitives.dll", + "source": "vendor/pwsh/ref/Microsoft.Win32.Primitives.dll", + "bytes": 16168, + "sha256": "AB197DEC717C52F5A72C8F2366C33E2FC5149BFA7CF49ACD59BBFB563DEFCD05" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/Microsoft.Win32.Registry.dll", + "source": "vendor/pwsh/ref/Microsoft.Win32.Registry.dll", + "bytes": 21288, + "sha256": "E8EB7F780B9CECA6DAE35E74A11FA85E26A1B8B4A7EDD5237CCCC6B63B4E4B7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/mscorlib.dll", + "source": "vendor/pwsh/ref/mscorlib.dll", + "bytes": 55592, + "sha256": "894D95D82126559637417B4F14C48ED20BB1168B13A0E65BD1337CDC2A981D1B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/netstandard.dll", + "source": "vendor/pwsh/ref/netstandard.dll", + "bytes": 100648, + "sha256": "3DC81DBC9DACD28261632C94410859EDA24C2DB50CD6C14309CBB2A434B50C57" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.AppContext.dll", + "source": "vendor/pwsh/ref/System.AppContext.dll", + "bytes": 15144, + "sha256": "D9DD9FA44E019F27172D6B2662E2B6038F48C270912AC8AAE53D4D909DA7E9BE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Buffers.dll", + "source": "vendor/pwsh/ref/System.Buffers.dll", + "bytes": 15144, + "sha256": "A1A35F5524E2F831D08CA55F702FBDDCB8C6DFECD264F0F563D81A1C8B8280BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.Concurrent.dll", + "source": "vendor/pwsh/ref/System.Collections.Concurrent.dll", + "bytes": 27432, + "sha256": "27B446A401163BAA325F9CD82F787DFD32D0E205011093E0AB3057E5DA609555" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.dll", + "source": "vendor/pwsh/ref/System.Collections.dll", + "bytes": 55080, + "sha256": "130FCDD915C3FF8D8279FEFD9A198B784D37693F4C1E96D1F2CFF3EDC96B638D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.Immutable.dll", + "source": "vendor/pwsh/ref/System.Collections.Immutable.dll", + "bytes": 75048, + "sha256": "D4145735395753187965A6F0A8BF6BB6F301563C4D4080249614B8487B0604AC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.NonGeneric.dll", + "source": "vendor/pwsh/ref/System.Collections.NonGeneric.dll", + "bytes": 22864, + "sha256": "99ADF08652A16E5AF30797B98742851B94604AA44BAB677DDC38C7826BD529A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Collections.Specialized.dll", + "source": "vendor/pwsh/ref/System.Collections.Specialized.dll", + "bytes": 25896, + "sha256": "17EF2B9433A1F1E9FC956786067F8D60672437D3FEB76AF84175F2B3BEE4DED1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.Annotations.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.Annotations.dll", + "bytes": 31568, + "sha256": "43C467051FEAFAA27144CC672836F38108377439245833FC8D99DE97D3667C37" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.DataAnnotations.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.DataAnnotations.dll", + "bytes": 16720, + "sha256": "D40E8C7062E6B9CE11BA83EAB3D26BE0EEC8066B99970EA88B8E87FB75F411BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.dll", + "bytes": 15656, + "sha256": "8811E7CC4288C435F54B0350AFBFFCBB13F77618CF9C0D3436CAD89A83D29B23" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.EventBasedAsync.dll", + "bytes": 19240, + "sha256": "B96D6D2F159EE5E192B2406A0EC993C8AD470A847F2E7ACD2AA92CBC0A4C6A58" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.Primitives.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.Primitives.dll", + "bytes": 25936, + "sha256": "09A575C38BD010F3131704E9294941FF9740A0BA70BE0FD6AB8FA54C44DBFCA7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ComponentModel.TypeConverter.dll", + "source": "vendor/pwsh/ref/System.ComponentModel.TypeConverter.dll", + "bytes": 104744, + "sha256": "98A3E34140052C828AB7BDB81040421616FA4E6356C27F7970DF141EF5681B42" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Configuration.dll", + "source": "vendor/pwsh/ref/System.Configuration.dll", + "bytes": 19240, + "sha256": "3952A72E1F18774F16952B46C20E80ED2BC601A06ECA01F33FA924C738022A9C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Console.dll", + "source": "vendor/pwsh/ref/System.Console.dll", + "bytes": 26408, + "sha256": "783E5EA6D049218EC30316598654E031415F0D1EC6CE6932C2D1A9CBD97A77E0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Core.dll", + "source": "vendor/pwsh/ref/System.Core.dll", + "bytes": 23336, + "sha256": "AC23FAF1B37FA49F1B117BAAD951A65609E799A6E669A6DF06D3552677E0C086" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Data.Common.dll", + "source": "vendor/pwsh/ref/System.Data.Common.dll", + "bytes": 154448, + "sha256": "E563226DCE5E8D2F1ECF24B13A37D8948E42208D7B93BC9812475074382F2303" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Data.DataSetExtensions.dll", + "source": "vendor/pwsh/ref/System.Data.DataSetExtensions.dll", + "bytes": 15656, + "sha256": "AEEAE0755C8C7F6747765CDF82FA43182E320E97F2035E3A0A19F21905E6C113" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Data.dll", + "source": "vendor/pwsh/ref/System.Data.dll", + "bytes": 23848, + "sha256": "8BEE1D4675FEE975B5454E7407827AA3C6D4A2E53B9EFB40BE5409AC50900EDB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Contracts.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Contracts.dll", + "bytes": 19752, + "sha256": "4366CCD79A875FB64E38813A81BDF2F61915B4C838CB74572D87919C2E7DEE41" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Debug.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Debug.dll", + "bytes": 15656, + "sha256": "8B160FFE7518588BF64E13C01C6DCB2EF504D64F38BBF0F198D31E7B698DB92C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.DiagnosticSource.dll", + "bytes": 43304, + "sha256": "6CAA24C3FE3819A6F78AEDB0E8F718A6BD83EAB4D9A786C609A228E330557BB0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.FileVersionInfo.dll", + "bytes": 16720, + "sha256": "AE73589F3288FC3648A718797361F0AB37606CC18667C8AAEBEA2787F62736A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Process.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Process.dll", + "bytes": 31568, + "sha256": "CD121308CB25B34EBC2549D72DA361E56A093B29EF5AB0A85870D13FB91DE052" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.StackTrace.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.StackTrace.dll", + "bytes": 22824, + "sha256": "E287630ABE5753B658E64E947616C8BC8291BCD27BFCADC745A33818A43AD30D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.TextWriterTraceListener.dll", + "bytes": 17704, + "sha256": "2C567AD9A2EFE67D171E0AB06D0C7035383508588CAE62B3C3D08F579A064595" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Tools.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Tools.dll", + "bytes": 15144, + "sha256": "841C5E2B6C171FA9AAFF0FC6295F3FBC93DC5DCAB44118B62F1C942BB674AFF2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.TraceSource.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.TraceSource.dll", + "bytes": 27984, + "sha256": "ECFE5B354EA6B7BF3736D8C4B02AE322C766F2BCCF260182632A492F3309D883" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Diagnostics.Tracing.dll", + "source": "vendor/pwsh/ref/System.Diagnostics.Tracing.dll", + "bytes": 28968, + "sha256": "E0F72905EB1B1CFD6DB130E6B2F18424788769D79FAE486054A242615D713A50" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.dll", + "source": "vendor/pwsh/ref/System.dll", + "bytes": 49448, + "sha256": "FF11E2AB9009EF141E315187254F8327DD1E2591E44B8BF158730869028F1F0E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Drawing.dll", + "source": "vendor/pwsh/ref/System.Drawing.dll", + "bytes": 20304, + "sha256": "585A8DA9952D715F8DE7A7B1879B59D68FF8608B92F6ED1C5F0E1D251180D3C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Drawing.Primitives.dll", + "source": "vendor/pwsh/ref/System.Drawing.Primitives.dll", + "bytes": 35616, + "sha256": "8FFDA23FAFB73BDAC7B6C8DFCAC1C06894C729F65157E218980DB9A4B74C7427" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Dynamic.Runtime.dll", + "source": "vendor/pwsh/ref/System.Dynamic.Runtime.dll", + "bytes": 16168, + "sha256": "C0EAC30C791DB8C1A2B50B61C50D029F4DDD0EADFDD35433745001688A3E7466" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Formats.Asn1.dll", + "source": "vendor/pwsh/ref/System.Formats.Asn1.dll", + "bytes": 26408, + "sha256": "C6F8CB70A20321AC4C93219CCB5264F50D21D7F7CDD0BD61C02360FF87CD38F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Formats.Tar.dll", + "source": "vendor/pwsh/ref/System.Formats.Tar.dll", + "bytes": 20264, + "sha256": "C6D0AA4D2E3ED3AB6C1949669D79BFC763FC9E7B04830AB7174D4CC55569757C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Globalization.Calendars.dll", + "source": "vendor/pwsh/ref/System.Globalization.Calendars.dll", + "bytes": 15656, + "sha256": "E0F3F3F6E4ACC7B6755782FED5CB075ECF70BEEE4FC5D404579AE99BC0883EF2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Globalization.dll", + "source": "vendor/pwsh/ref/System.Globalization.dll", + "bytes": 15696, + "sha256": "66902CFFD057673B4B77AA68FAF80D33FFA4C94510E61DB2A067943CC53B7702" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Globalization.Extensions.dll", + "source": "vendor/pwsh/ref/System.Globalization.Extensions.dll", + "bytes": 15144, + "sha256": "BE0390557CC72078A7217CF94FD44B9AB66AA30838C00A92AA78C62469C44AA0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.Brotli.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.Brotli.dll", + "bytes": 18256, + "sha256": "1401375A39B7BA5E65E0B49274C0168B6C8B7E5455AFEFD269C1418FD019E997" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.dll", + "bytes": 21800, + "sha256": "AC573888BA94E958243674D82E51E88692E5BEA6FD94BB68D87D30C28CDAECF5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.FileSystem.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.FileSystem.dll", + "bytes": 15184, + "sha256": "4AEEC2C0189F73BA8122F05C0D759008DDAE1A8873B167CC8A558D53F2AF80E1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Compression.ZipFile.dll", + "source": "vendor/pwsh/ref/System.IO.Compression.ZipFile.dll", + "bytes": 18728, + "sha256": "C34EF5004F535F10ADCEB66B3F20F743B15656ED4C7DCF5946A8B83A6CDEE329" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.dll", + "source": "vendor/pwsh/ref/System.IO.dll", + "bytes": 15656, + "sha256": "388400CCB189B819D6EC5F1EF70710A39641D93ED5B870FC9324866B75DFB78B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.AccessControl.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.AccessControl.dll", + "bytes": 20264, + "sha256": "38EB787FD20B30F36E207364387FD34A5C68207BAFFDDF318986216F4B6CC6B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.dll", + "bytes": 15656, + "sha256": "C251F33B29382C47B773D907463EDD9FE978A06E39EFD5670EF3B3B1D4F8C2B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.DriveInfo.dll", + "bytes": 17192, + "sha256": "5CE6349A752189B3BCAA553FEF38F9C0CDD1FB2E39E7CABA67C966F52EF66391" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.Primitives.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.Primitives.dll", + "bytes": 15144, + "sha256": "D3B21B8B38E48C6B4D1CB5CFE9ADFE4ABAD7B3353601FEBD3C7E4027F9EF9C6B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.FileSystem.Watcher.dll", + "source": "vendor/pwsh/ref/System.IO.FileSystem.Watcher.dll", + "bytes": 20776, + "sha256": "07FC2B07B7D53173C695E60E93F2DA4325B68C797C4351FFF571923F01F8054C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.IsolatedStorage.dll", + "source": "vendor/pwsh/ref/System.IO.IsolatedStorage.dll", + "bytes": 22312, + "sha256": "6720027218D12697D30640FB97ACDB02438649CCCB038FFDB3AD80DC27F6F736" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.MemoryMappedFiles.dll", + "source": "vendor/pwsh/ref/System.IO.MemoryMappedFiles.dll", + "bytes": 18256, + "sha256": "028850F2CEBBCC3CCB48FCEF97751522EA6455C05FF9234686A16CC088FAC5A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Pipelines.dll", + "source": "vendor/pwsh/ref/System.IO.Pipelines.dll", + "bytes": 20304, + "sha256": "08344A31693477A61B45DFF21739BF576C7482B394FAB2FA26D2D8BA87B2CB91" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Pipes.AccessControl.dll", + "source": "vendor/pwsh/ref/System.IO.Pipes.AccessControl.dll", + "bytes": 18216, + "sha256": "80FDA558926F7D7A085A6A210596000D281E4AF2FB235E4C8DC9CBEF8F1A87D0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.Pipes.dll", + "source": "vendor/pwsh/ref/System.IO.Pipes.dll", + "bytes": 22312, + "sha256": "0B7AAA8FEA2A9483857B2AC918814AD1D8FF3303C98D6EF71D4593B16ADEE9BA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", + "source": "vendor/pwsh/ref/System.IO.UnmanagedMemoryStream.dll", + "bytes": 15696, + "sha256": "AD6A5F859589E1951103A00F45566DA079CF87BD8B03F0C6AD985A6F39F19C5A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.AsyncEnumerable.dll", + "source": "vendor/pwsh/ref/System.Linq.AsyncEnumerable.dll", + "bytes": 36176, + "sha256": "3367B7D6D2BE9D171E158B544979131E8B97328B293D6E77917234A2A7279507" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.dll", + "source": "vendor/pwsh/ref/System.Linq.dll", + "bytes": 33616, + "sha256": "9CB73D57C035503BDC70FDC422DEE5BF4F9DE97B812CD0DCB379D652C6F91205" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.Expressions.dll", + "source": "vendor/pwsh/ref/System.Linq.Expressions.dll", + "bytes": 63312, + "sha256": "772112C3F1C10FB713CCC3AD62CC313C2CAD1CBAF122EF6D25120AA7B65B0447" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.Parallel.dll", + "source": "vendor/pwsh/ref/System.Linq.Parallel.dll", + "bytes": 31016, + "sha256": "B4B597374FFEBCCF8A46793F03D5E6D5E477DFC5047157217D79449F9729E761" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Linq.Queryable.dll", + "source": "vendor/pwsh/ref/System.Linq.Queryable.dll", + "bytes": 32040, + "sha256": "5BAF83B98F103D9304EAC427C4296C9DD501D320B7B5495ED8EDE892AAF1076D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Memory.dll", + "source": "vendor/pwsh/ref/System.Memory.dll", + "bytes": 57168, + "sha256": "D7E674A1FEFF0546C9932B0FCFFA9D055976736342A4B1904F1F27894F3C0ED6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.dll", + "source": "vendor/pwsh/ref/System.Net.dll", + "bytes": 17192, + "sha256": "481B38C912F8779B3510CD00F079E45096EA22B6DEFDAAB66780D5AC3AE6986A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Http.dll", + "source": "vendor/pwsh/ref/System.Net.Http.dll", + "bytes": 60200, + "sha256": "3FF081221D0ADEB99B5CF40788E20DAB1D0D706618044B34E68A47529C761601" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Http.Json.dll", + "source": "vendor/pwsh/ref/System.Net.Http.Json.dll", + "bytes": 23336, + "sha256": "BF97FCFA0D20ADB597878F902B761480FB02FA0F9F81521F16F2455DDC6A1ED2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.HttpListener.dll", + "source": "vendor/pwsh/ref/System.Net.HttpListener.dll", + "bytes": 25384, + "sha256": "03CA98ABEB9EA2C7E372C31AA460D2A9E526617C22FA97C9D8750977167A9634" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Mail.dll", + "source": "vendor/pwsh/ref/System.Net.Mail.dll", + "bytes": 32040, + "sha256": "8193E3652B1ECB1AA46258087BB5F44BECD72693552DA120E1274EF1AD443E53" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.NameResolution.dll", + "source": "vendor/pwsh/ref/System.Net.NameResolution.dll", + "bytes": 17704, + "sha256": "97E0CC175BF13E4861E1508820F08561B38F28E98EBC9C5ADDB28C58FA7C344D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.NetworkInformation.dll", + "source": "vendor/pwsh/ref/System.Net.NetworkInformation.dll", + "bytes": 33576, + "sha256": "B90B729446F4ED3FA470B5C96E9C5ADECBF4E438EAA91D0548832C58673A4C18" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Ping.dll", + "source": "vendor/pwsh/ref/System.Net.Ping.dll", + "bytes": 20264, + "sha256": "F623594E3309D40D36A7DD63DCB5251F72EB94EB2E74246805B4A0D23FC8C6B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Primitives.dll", + "source": "vendor/pwsh/ref/System.Net.Primitives.dll", + "bytes": 37160, + "sha256": "129E7B324FBC12F8C54B89E6D6706B08455532FB64A8913038ADD92BE2924244" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Quic.dll", + "source": "vendor/pwsh/ref/System.Net.Quic.dll", + "bytes": 23848, + "sha256": "F9651DCDE17F5823C7D42BE080059FD9F88AED81D91C64E58F95AFCA44A2E24A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Requests.dll", + "source": "vendor/pwsh/ref/System.Net.Requests.dll", + "bytes": 41256, + "sha256": "D45910C06D2C2FCD6039E8B4076B5878FD195D0FC09D27C4CEB4009AD9491672" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Security.dll", + "source": "vendor/pwsh/ref/System.Net.Security.dll", + "bytes": 53544, + "sha256": "C4AF7099D2077E34C7074E94EFEB57AC5707AF128EE6E5E0E75D398B8E5843BA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.ServerSentEvents.dll", + "source": "vendor/pwsh/ref/System.Net.ServerSentEvents.dll", + "bytes": 17192, + "sha256": "87BA81CD8FD6AB3A9A3F4DCE922849AEA832928F52495788D371DDA66E57DA01" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.ServicePoint.dll", + "source": "vendor/pwsh/ref/System.Net.ServicePoint.dll", + "bytes": 15144, + "sha256": "84FA83CD96E62FDA02991B8A8E99FA1C7B3AC08807B04F7486A896CFE6B4A760" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.Sockets.dll", + "source": "vendor/pwsh/ref/System.Net.Sockets.dll", + "bytes": 47440, + "sha256": "8B7CEBAFD550083A8D242DBCFB7501384D17142AD6C2BBB047DD37A524762179" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebClient.dll", + "source": "vendor/pwsh/ref/System.Net.WebClient.dll", + "bytes": 27944, + "sha256": "E4E6CC79E23892C8E67A4CB594BB304A26A0328507C45DC2D1998BC6836311A0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebHeaderCollection.dll", + "source": "vendor/pwsh/ref/System.Net.WebHeaderCollection.dll", + "bytes": 19240, + "sha256": "C4EE735A52B966F3805CFBD5C172F151FFE013BD70C080F15C2F68BE628CD357" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebProxy.dll", + "source": "vendor/pwsh/ref/System.Net.WebProxy.dll", + "bytes": 18216, + "sha256": "42C8EB8BF1563806069E1DD1C6C651159DAA5589B7F69FF817CDEAB3BD02915F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebSockets.Client.dll", + "source": "vendor/pwsh/ref/System.Net.WebSockets.Client.dll", + "bytes": 19240, + "sha256": "5AC14D45C1B92E9C831B37591941EA01F3097A39D130BC292506DD099AEB7260" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Net.WebSockets.dll", + "source": "vendor/pwsh/ref/System.Net.WebSockets.dll", + "bytes": 23848, + "sha256": "C16043D3FFA90A8FDAF193F0DF91F99D6C23518D8432B4CAA2D70A92D4C3CFA7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Numerics.dll", + "source": "vendor/pwsh/ref/System.Numerics.dll", + "bytes": 15184, + "sha256": "35ADC9360421B3B7A3D50399CB2C651C160158D7E8C302907ACBCE5F61F4A4F7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Numerics.Vectors.dll", + "source": "vendor/pwsh/ref/System.Numerics.Vectors.dll", + "bytes": 58152, + "sha256": "CC06324F279C581D8F76C0C8F6B011CF8D05EFD0C95E998D68F9243DA124EB52" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ObjectModel.dll", + "source": "vendor/pwsh/ref/System.ObjectModel.dll", + "bytes": 23336, + "sha256": "3A591E043CD443761537580084F714392BB3278176C9A19F1BE8700A29EF6F56" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.DispatchProxy.dll", + "source": "vendor/pwsh/ref/System.Reflection.DispatchProxy.dll", + "bytes": 15656, + "sha256": "0D46DFB9C4FFC27D05A1C089D8F60A1CB365855140A9B30972133208270DD08B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.dll", + "source": "vendor/pwsh/ref/System.Reflection.dll", + "bytes": 16168, + "sha256": "95059346D6271DD6D0322CC321E5FB8CCEAB662F550C67AEE7615059E41739BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Emit.dll", + "source": "vendor/pwsh/ref/System.Reflection.Emit.dll", + "bytes": 44368, + "sha256": "29021699E4128964365A86C22AFB3B0A8368DA7A3F7463C9C1A8FC0A1402A39F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", + "source": "vendor/pwsh/ref/System.Reflection.Emit.ILGeneration.dll", + "bytes": 20776, + "sha256": "0201069B706D397C573B9342902613A71879A2701994A48E2821C32D43101AE4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Emit.Lightweight.dll", + "source": "vendor/pwsh/ref/System.Reflection.Emit.Lightweight.dll", + "bytes": 19240, + "sha256": "EEF97E1049D8DD7ECC695080DD22C199EE8CA82B1CFCB3E33866CF7125DBD1A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Extensions.dll", + "source": "vendor/pwsh/ref/System.Reflection.Extensions.dll", + "bytes": 15144, + "sha256": "CEB1B81E1EB488D87BD43D16DC54450B2A78E0B653C44C988AAE2A20ABC162BC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Metadata.dll", + "source": "vendor/pwsh/ref/System.Reflection.Metadata.dll", + "bytes": 122152, + "sha256": "92EC811516B768220CC41FB6A7197F7BA735F97B694716DFD1BFDF274078A90A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.Primitives.dll", + "source": "vendor/pwsh/ref/System.Reflection.Primitives.dll", + "bytes": 21840, + "sha256": "FA52402A7B4096EF109853F6BF03ADEF0E1DE67E3652BB31F413D197F19F9B22" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Reflection.TypeExtensions.dll", + "source": "vendor/pwsh/ref/System.Reflection.TypeExtensions.dll", + "bytes": 19752, + "sha256": "78178074BDB0F7714C634E4A1B425570264B13B2CA5B9AD6880622BFF0BDA58E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Resources.Reader.dll", + "source": "vendor/pwsh/ref/System.Resources.Reader.dll", + "bytes": 15144, + "sha256": "20BDB5D302B5E28F6042C9F281CFA3AE2F2358544CC40783052FE5ABF6AA859B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Resources.ResourceManager.dll", + "source": "vendor/pwsh/ref/System.Resources.ResourceManager.dll", + "bytes": 15696, + "sha256": "F709EB5CF16E26679782784C428825019785FCC7085562092425D4999BECE03D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Resources.Writer.dll", + "source": "vendor/pwsh/ref/System.Resources.Writer.dll", + "bytes": 16208, + "sha256": "7E772363E799C78D8D1D37FE1D412723DA9350496A6D7BACD37491F2BB777CC4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", + "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.Unsafe.dll", + "bytes": 15184, + "sha256": "6E685EC0477B21A688CD38BFCAFC673888CDF186C99DF3C96FAB352E96D6FDC8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", + "source": "vendor/pwsh/ref/System.Runtime.CompilerServices.VisualC.dll", + "bytes": 17192, + "sha256": "1E5EB23824055D6AF5BABB6A7CE47E314F78B0B464466DB32B60F543304A9140" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.dll", + "source": "vendor/pwsh/ref/System.Runtime.dll", + "bytes": 862504, + "sha256": "10CEAA9B398F512B46DEF6FD16FB5374C82AC03A7495859ADBAAA70868FE70AA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Extensions.dll", + "source": "vendor/pwsh/ref/System.Runtime.Extensions.dll", + "bytes": 17704, + "sha256": "3D7B517D4FD89EE58C53E0F642CC9BE42E7A8669B51A85E861DAFE118067CBFD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Handles.dll", + "source": "vendor/pwsh/ref/System.Runtime.Handles.dll", + "bytes": 15656, + "sha256": "985457A3E6A67E96A445EE0FB69B2D58BCDADC4C964253FB63E2D5061A36B050" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.InteropServices.dll", + "source": "vendor/pwsh/ref/System.Runtime.InteropServices.dll", + "bytes": 101160, + "sha256": "8CED47E6A2A717C459C3FA13F76A521B35917E123A9B1EE7ED1B5DE13DB80709" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", + "source": "vendor/pwsh/ref/System.Runtime.InteropServices.JavaScript.dll", + "bytes": 25896, + "sha256": "CB551CC19DF1067DD8897D14BF02ECFA63D8ECF96067A21902406791D68CE226" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", + "source": "vendor/pwsh/ref/System.Runtime.InteropServices.RuntimeInformation.dll", + "bytes": 15656, + "sha256": "C26D9F94329422BD000F28F6C65FDC162BF9397A6D29E418A189D4CA49C5C3EC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Intrinsics.dll", + "source": "vendor/pwsh/ref/System.Runtime.Intrinsics.dll", + "bytes": 466216, + "sha256": "DF8F497F64B6A3AC02941FAE92E057FADDF93F3F135F2F6444F725617C38B619" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Loader.dll", + "source": "vendor/pwsh/ref/System.Runtime.Loader.dll", + "bytes": 19752, + "sha256": "42884BCDC89EBD4EE509D5FCB4A679B1D7EEBD6DDD9B66CB9E118063DDD30482" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Numerics.dll", + "source": "vendor/pwsh/ref/System.Runtime.Numerics.dll", + "bytes": 36136, + "sha256": "4AFC84F3E73B63FF6683941FCFDA44E8A6AA5227B6AFC128DCB979865E23D183" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.dll", + "bytes": 17192, + "sha256": "D565FC261CA98CBD14833821F777860291E8A0F775DE7AEE91778A9F6647F6F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Formatters.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Formatters.dll", + "bytes": 23848, + "sha256": "EF24BB5669B54B31DA46EB13238FB2B7F880DD780DC9B34A224A69DD59D93AF8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Json.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Json.dll", + "bytes": 20776, + "sha256": "DE7EECA7DF9F6BB664383FBEAD141719BD08D88B6AEF43C0605528719F607E62" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Primitives.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Primitives.dll", + "bytes": 19752, + "sha256": "A547B0D205CEBE84508C2DDEFD56D063C48CDFEAE5B0C44BDE6BAE9F3D72F98A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Runtime.Serialization.Xml.dll", + "source": "vendor/pwsh/ref/System.Runtime.Serialization.Xml.dll", + "bytes": 39760, + "sha256": "117BCB0DD828EAF6BFAD06D8380BA32D32729B45879D907D7023FE73E9655BD9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.AccessControl.dll", + "source": "vendor/pwsh/ref/System.Security.AccessControl.dll", + "bytes": 37160, + "sha256": "2BD23D6AA92B920DAFD374E1AB47ECB597EF3B1CF1652A4EC4944A0DEFB1A4AC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Claims.dll", + "source": "vendor/pwsh/ref/System.Security.Claims.dll", + "bytes": 32552, + "sha256": "693A61DC317AB5A651EF598578A4B55912AC39342A0842FEA003761F861B0CAF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Algorithms.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Algorithms.dll", + "bytes": 17232, + "sha256": "EB99AE7DBE058A1B015ABA6769CE1F65155B7FF51FF9A7203FCF6D7CDEA5FEF7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Cng.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Cng.dll", + "bytes": 16168, + "sha256": "7319F97896D92A7D0729CF28DC6C4D1F797C1520B656EE01A780EEC726EC2C16" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Csp.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Csp.dll", + "bytes": 16208, + "sha256": "4002AF141E9CB29875B677841ADF64A9681E5672C2595492DCBC161460D4E121" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.dll", + "bytes": 163112, + "sha256": "A700A7F6C32169E62BE879088AA2218BDD5450C3945ED6C6D0ACB3D25DB0B1BF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Encoding.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Encoding.dll", + "bytes": 15656, + "sha256": "994494AEC7760F40FADE2264AA7C13EEBAE36360F0B1399A7134BB19C9D58FDA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.OpenSsl.dll", + "bytes": 15656, + "sha256": "1A8902BBAD1B577A98BEC62A8472105DC1E9A253E390F69FBE3D167E93206968" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.Primitives.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.Primitives.dll", + "bytes": 15656, + "sha256": "194CCCEA9354BB248E44C954B5373F9069163F8F21524B3EE4B3185FE436BC15" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", + "source": "vendor/pwsh/ref/System.Security.Cryptography.X509Certificates.dll", + "bytes": 16680, + "sha256": "7439C3B90BC2EE49A9275B28ED29052B4D48B45999AC1B7D25593033B1BB8DD6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.dll", + "source": "vendor/pwsh/ref/System.Security.dll", + "bytes": 18256, + "sha256": "D8EFD304765E859369698EFEF9B3CA707DEFA8E142721FE5605D7A2DE7D38375" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Principal.dll", + "source": "vendor/pwsh/ref/System.Security.Principal.dll", + "bytes": 15144, + "sha256": "1D3E351058BE3A33214B23B308FE57F78CD6FD4D8FAD448FC84CBBE7376129F9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.Principal.Windows.dll", + "source": "vendor/pwsh/ref/System.Security.Principal.Windows.dll", + "bytes": 26920, + "sha256": "4B8EAB6E832E99E8C08DB2B9649F95AE8298E1105DEEA45A62E7D3B68C6593B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Security.SecureString.dll", + "source": "vendor/pwsh/ref/System.Security.SecureString.dll", + "bytes": 15696, + "sha256": "73AAC97072B7DEEFA4DE9647BF822162224F1452AD467841B989FF89D4D94595" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ServiceModel.Web.dll", + "source": "vendor/pwsh/ref/System.ServiceModel.Web.dll", + "bytes": 16680, + "sha256": "CE91013EE3A9A9D69A85E258F7749E6ECE488295D7CB66F7C1079F2E8678FC5E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ServiceProcess.dll", + "source": "vendor/pwsh/ref/System.ServiceProcess.dll", + "bytes": 15696, + "sha256": "63FC498C9D04E3C4345DFDB8016AD0639D54562DD7D8C707C072792C854DF944" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encoding.CodePages.dll", + "source": "vendor/pwsh/ref/System.Text.Encoding.CodePages.dll", + "bytes": 15656, + "sha256": "2D66F9AE5E7439756EBA063C0DF86D6D3A0DC571F4BB07ADD18C59D6F10138CE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encoding.dll", + "source": "vendor/pwsh/ref/System.Text.Encoding.dll", + "bytes": 15696, + "sha256": "5E347DB6B4F9B2746D50E1AC85BE2D9CC7DC4038FDE98AC4295BC7030C83CDA9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encoding.Extensions.dll", + "source": "vendor/pwsh/ref/System.Text.Encoding.Extensions.dll", + "bytes": 20776, + "sha256": "D6224360B0E91DFE544EC3BAF0D5DED78A13349F6378493C96344A310C8CAF08" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Encodings.Web.dll", + "source": "vendor/pwsh/ref/System.Text.Encodings.Web.dll", + "bytes": 26408, + "sha256": "F9F6120753FC9FAEAA5B09E10EF309594DBD4C20C8CDF1717EB910455549793A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.Json.dll", + "source": "vendor/pwsh/ref/System.Text.Json.dll", + "bytes": 82216, + "sha256": "4133A8597A10EEB99D56A962FF581DB9FD3AC85506EE3A20A2D23119C73A4346" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Text.RegularExpressions.dll", + "source": "vendor/pwsh/ref/System.Text.RegularExpressions.dll", + "bytes": 36648, + "sha256": "347C497CF2BD60A298EFB59FE47156909CA854FDAB4DABB944AF7D6F8C270CEF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.AccessControl.dll", + "source": "vendor/pwsh/ref/System.Threading.AccessControl.dll", + "bytes": 20776, + "sha256": "8449A1EE09EDF8BDC69C61605BEBEC602C7B28A9CA401CA463512E4D369A3B7B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Channels.dll", + "source": "vendor/pwsh/ref/System.Threading.Channels.dll", + "bytes": 19240, + "sha256": "C72028E4423FB04F3A644A76E1807E3651191AEB606B8F7B15B3650E4714CDCA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.dll", + "source": "vendor/pwsh/ref/System.Threading.dll", + "bytes": 33576, + "sha256": "0C2A3B4FDDB2D732B8EBD0BCA9AC57A8C66C9D4F5FBCB55FCD17E3CBCF678A4B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Overlapped.dll", + "source": "vendor/pwsh/ref/System.Threading.Overlapped.dll", + "bytes": 18768, + "sha256": "7837D3BD05E28F95749029C832FB1D0CE5BF403A17BEF7BF95DF522BA93DAC3A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.Dataflow.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.Dataflow.dll", + "bytes": 32040, + "sha256": "5D605D4C470C38BC37A04D58B4023A9C60179172790A29F4F7CB92ABA6251648" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.dll", + "bytes": 16680, + "sha256": "F80B21F7DAE04377B73945456CABE25E2AB1F4B564095AED546C2AA571644567" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.Extensions.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.Extensions.dll", + "bytes": 15696, + "sha256": "F148E3A1414B7C2763AE21529AD79AD759F7CFD3C885256B64B0D2209DEC2613" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Tasks.Parallel.dll", + "source": "vendor/pwsh/ref/System.Threading.Tasks.Parallel.dll", + "bytes": 20264, + "sha256": "97F9CCB69D9F1D4D86DF2869E1EF667C4C68285B04F336B1188E633C8F814D05" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Thread.dll", + "source": "vendor/pwsh/ref/System.Threading.Thread.dll", + "bytes": 23888, + "sha256": "FFF2281F039C07E564BCB198E041627082702BEDA24DCCC8DF39683701EA25A0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.ThreadPool.dll", + "source": "vendor/pwsh/ref/System.Threading.ThreadPool.dll", + "bytes": 18216, + "sha256": "12FEDF2DE6449BFEC4C1A08D762493BC0188E1B9B0A58028289105ED81B983F5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Threading.Timer.dll", + "source": "vendor/pwsh/ref/System.Threading.Timer.dll", + "bytes": 15144, + "sha256": "9E4620846572F49AEB476AA99405071B4BBD0DF4323E4E232787ED73642B1EE5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Transactions.dll", + "source": "vendor/pwsh/ref/System.Transactions.dll", + "bytes": 16720, + "sha256": "56ABFCA0350C111D077B3D86A2469EA3DA4FCCC1871F7C51A8B4FDB664BB0EDE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Transactions.Local.dll", + "source": "vendor/pwsh/ref/System.Transactions.Local.dll", + "bytes": 25896, + "sha256": "C9FAFFC186D677DE7800FF7D4D1B0D204AE7614E6B8518ADD582C840D7532A3B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.ValueTuple.dll", + "source": "vendor/pwsh/ref/System.ValueTuple.dll", + "bytes": 15696, + "sha256": "07A5347F6A597E5E0D72109EFA1A525697E8C3F74FB0029005500F8EBBF9187A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Web.dll", + "source": "vendor/pwsh/ref/System.Web.dll", + "bytes": 15144, + "sha256": "BC260B3DB247AD9A6E4A82A275DA85C3691A41272CC745CB7EAEFCF1F31CC817" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Web.HttpUtility.dll", + "source": "vendor/pwsh/ref/System.Web.HttpUtility.dll", + "bytes": 17192, + "sha256": "F26B0586048FDF472E0FB33CA9435B163C91325DE4281FCE8684251CACC304CC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Windows.dll", + "source": "vendor/pwsh/ref/System.Windows.dll", + "bytes": 15696, + "sha256": "716EEE98CEEE87D38F0B301224462F8442AA793F4B1B9C3FFAC315753320AB37" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.dll", + "source": "vendor/pwsh/ref/System.Xml.dll", + "bytes": 23376, + "sha256": "277A84BA155FE7808A514F32694A8157EC9F0EEEEF00EF9643E5250004054FF0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.Linq.dll", + "source": "vendor/pwsh/ref/System.Xml.Linq.dll", + "bytes": 15696, + "sha256": "AE04903B25717FA4695E7E63489091F0D461E791E0BD264C6C85250D668F540D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.ReaderWriter.dll", + "source": "vendor/pwsh/ref/System.Xml.ReaderWriter.dll", + "bytes": 116008, + "sha256": "942964D32F72BA10288593F212E3A52C4811B65CA4D5F1A3979C92D2D526AA4C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.Serialization.dll", + "source": "vendor/pwsh/ref/System.Xml.Serialization.dll", + "bytes": 16208, + "sha256": "489694401C60905CB6DCA8EF0BF738CB609CD45B337126A01C825B913086BA43" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XDocument.dll", + "source": "vendor/pwsh/ref/System.Xml.XDocument.dll", + "bytes": 34088, + "sha256": "05F6380411A81245960714A9FD81F6134F7BD279CD70B248A1E1586977798F1A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XmlDocument.dll", + "source": "vendor/pwsh/ref/System.Xml.XmlDocument.dll", + "bytes": 15656, + "sha256": "A50B9B3A03596E36E73E7A7D54F4D018E8DCFAA7FE79E8CF900AF8838EF3EC36" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XmlSerializer.dll", + "source": "vendor/pwsh/ref/System.Xml.XmlSerializer.dll", + "bytes": 50984, + "sha256": "F957611B80E082A453264A13BF27B5B251FAF598F377F948195123A374B2E8F6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XPath.dll", + "source": "vendor/pwsh/ref/System.Xml.XPath.dll", + "bytes": 16680, + "sha256": "0E68260B4C36D00C9A77C30A270100461123C0E7E1A37402BB7F67174051B8F3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/System.Xml.XPath.XDocument.dll", + "source": "vendor/pwsh/ref/System.Xml.XPath.XDocument.dll", + "bytes": 16168, + "sha256": "B8F74E198C3C2CD00CCC90549579F1B69C6AE1655F8B348ACE414FD9075C921F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ref/WindowsBase.dll", + "source": "vendor/pwsh/ref/WindowsBase.dll", + "bytes": 16208, + "sha256": "0EC46A38F29A234B9A4D0AE9364C8322F661A47C831C5B948F2787B7CA12DBBD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/RegisterManifest.ps1", + "source": "vendor/pwsh/RegisterManifest.ps1", + "bytes": 17502, + "sha256": "13D50727D810BABE2A8FCBC77A0A4EA967A6C78DE4960469116DF9D8EEF53EC0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/RegisterMicrosoftUpdate.ps1", + "source": "vendor/pwsh/RegisterMicrosoftUpdate.ps1", + "bytes": 16907, + "sha256": "8DD5BF144D53B976B3F414973FB0F6C886F754E515427A2E8FD58B9C3B3E1B52" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/base.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/base.xsd", + "bytes": 6434, + "sha256": "B860EF30B01C57486A56948800E9B2DA116EC473CBECBB33FA9F71B8CEAB90AE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/baseConditional.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/baseConditional.xsd", + "bytes": 5297, + "sha256": "D26D4EF29124B2F9107B167D73E611F649FB2021D30C48A8616C6CC96230DE81" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/block.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/block.xsd", + "bytes": 917, + "sha256": "9A8B3C184394711480C38D71DB4850659B87729DF9EAC5A2235BDCF37A2020CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/blockCommon.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/blockCommon.xsd", + "bytes": 3872, + "sha256": "928717B190B014F271C09C2FFB7633C1E660B26F0ECBDD926662121914795789" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/blockSoftware.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/blockSoftware.xsd", + "bytes": 1282, + "sha256": "88AA3462598B7C394350F83EF366E5F83885C50A9C6966F02179228277358334" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/command.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/command.xsd", + "bytes": 13061, + "sha256": "A3DA7F8F2260A826CE253C33BDA8576246B4298794765EDC4F80A433357C6B87" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/conditionSet.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/conditionSet.xsd", + "bytes": 4279, + "sha256": "506415CD77EDE25B33C3E28FB89012B282E90B5D01F266323A83FEA9625F8EAB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developer.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developer.xsd", + "bytes": 11403, + "sha256": "AB9A7DDB85084F331ED79CB34374509767D9A64C510B0264682D5E682DC59497" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.rld", + "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.rld", + "bytes": 115907, + "sha256": "CD8A9A6B113DB5005F2A5C8321E52368411691B5F0C5EB90595E2A97B0FDEE89" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerCommand.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerCommand.xsd", + "bytes": 14520, + "sha256": "BBC9B2DD6D4473F09CE95A4E44B7F654A8D34A70197BEABACD1C9FF5233C3DEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerDscResource.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerDscResource.xsd", + "bytes": 2896, + "sha256": "7C22D9A1393062734E4923657C48D9117F6AC824D4E3F85EF88D8DE23F8C7BDF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManaged.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManaged.xsd", + "bytes": 10238, + "sha256": "5585FD25ED4FA8D39B75E9A83082EAB1D305B00839E7BCF05A24530E3D91D6EA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedClass.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedClass.xsd", + "bytes": 3693, + "sha256": "4CB1C23D86D22B4BCC5ACA98CD309C37F54A30358D673115D460FEEACB78DF9A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedConstructor.xsd", + "bytes": 2557, + "sha256": "0CA55A744AD968B537742ED37CD62852DF01CD96533D4E17874AD4F75271C8DC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedDelegate.xsd", + "bytes": 2558, + "sha256": "961DAA1C4B3513B83CF2E37FDB1361BE0D123E8B2CFE7F32510AD534FEA98E3D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEnumeration.xsd", + "bytes": 2306, + "sha256": "3AB7329914D7A9A6D711A62405089E736CE714EE4DE72F3493222887A72FDD39" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedEvent.xsd", + "bytes": 3108, + "sha256": "93EBD3AF5A078830839B10C9DB753CD445DC2D1A78A5449C90AC605E7FFC397C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedField.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedField.xsd", + "bytes": 2803, + "sha256": "B17F73ADD6678C4BC259065ADD380A9070531BC24A66AF3C4CAD3CF3D811F3A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedInterface.xsd", + "bytes": 2882, + "sha256": "B851D8BE146F1F51E5857045C28726585CC96A29C5A5A57DFC86CF7C509D8234" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedMethod.xsd", + "bytes": 2544, + "sha256": "36EB2BC76B2CDBFD908DF62E16CA915F7490CE75CF7BC386974EF89AA77B32FF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedNamespace.xsd", + "bytes": 3359, + "sha256": "68D68D8CEC8181C0C3046B419426EE1BD4D0371ABFDBF9A6789755B0F5764D91" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOperator.xsd", + "bytes": 1896, + "sha256": "053C18557BE353860222E14D7CE5E5844F5A746BA45347A9E08DA1D5A2B29F3B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedOverload.xsd", + "bytes": 2573, + "sha256": "7D2695D2F6AB6EB3C0BAB6125EEE415545C3EABE0923B1B37519F2D4A8B9251D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedProperty.xsd", + "bytes": 2843, + "sha256": "832A25485EAA9060868E9258AA1F93626804ED439EE2B43B2D74F9CB0DC438C4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerManagedStructure.xsd", + "bytes": 2951, + "sha256": "B7D1F7DDBCFEF43BD171FD78D5A255089A97762AD67C116B6A6998815905901F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerReference.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerReference.xsd", + "bytes": 12604, + "sha256": "42C585D48C418C7EB47CBA298A147923597B967E236997BB815301B57E31CB92" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerStructure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerStructure.xsd", + "bytes": 4112, + "sha256": "D1C0E02BB7207095E49EFC216A574B596A82E97B88E686E1C7A9717362623C44" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/developerXaml.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/developerXaml.xsd", + "bytes": 6746, + "sha256": "83ED2EE26CF6AB4C95A880F1424F7D5E09385B71239B84B3CFFB58288EEFF3B2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/endUser.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/endUser.xsd", + "bytes": 2144, + "sha256": "24CAEE5D0536ED81EB68D6FD836CF08849E056497B6064971A145397A04EB853" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/hierarchy.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/hierarchy.xsd", + "bytes": 10070, + "sha256": "D50D8096A11350A8E3B6873CF9CBC173B8A4322324B16F949538B1B98857C5B4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inline.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inline.xsd", + "bytes": 7644, + "sha256": "5A1A2D4586203BB3D6A4E6EC93E4D09D0AB66D33539B88A2550A36100D1E16FD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inlineCommon.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inlineCommon.xsd", + "bytes": 8860, + "sha256": "02071402B963966AD574AB09BB42719BDB1CE6CD7E9AF260A13FAF9EBB580C55" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inlineSoftware.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inlineSoftware.xsd", + "bytes": 8016, + "sha256": "C444CD1121A8B83B0FFE5A8467CC10D2D810C5A7765F4B4406D1EB329CD8901D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/inlineUi.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/inlineUi.xsd", + "bytes": 6400, + "sha256": "E7FF025576E482632878709E208B95916CCDCA2AF108C1C7B9218FB8472C1AE3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ITPro.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ITPro.xsd", + "bytes": 1208, + "sha256": "767328AD7B14EC90731E52D04EF27873EA674D34DD30297CB0FBA2A11242E96E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.rld", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.rld", + "bytes": 115835, + "sha256": "A711A691FDFDBEEEF09CB23543A8AC3D6BBF92AABEEC05939FCA51665229E710" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.tbr", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.tbr", + "bytes": 30575, + "sha256": "0FFD2E7097B51E0C6E1C1A18867658312D39AC68D8179049521F9ECAEB2CBF08" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsd", + "bytes": 1571, + "sha256": "FDA94A92FE41A7280D7F5ECE13CFC0042D4E1049E8FF961717628412317F6AE2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml.xsx", + "source": "vendor/pwsh/Schemas/PSMaml/Maml.xsx", + "bytes": 211, + "sha256": "639E5ED55BB9FD20672C74C20020C6C96B5FF721E45E9A337133AF858998B08C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML.xsl", + "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML.xsl", + "bytes": 363, + "sha256": "BDA0F195C928E6C18E226A83C77A1477DD88B70025D78F12D92605BBD10723BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", + "source": "vendor/pwsh/Schemas/PSMaml/Maml_HTML_Style.xsl", + "bytes": 14422, + "sha256": "99E1AD569C30396EFC7F37ACD08A8F0F41B20FF82E513D738E331C0EAC5DDC1C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloper.xsd", + "bytes": 14517, + "sha256": "3B89CB9A72549C46A56D9EA2EA12AC53CBC7DDD3392012FDD9D69EF261961AD2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ManagedDeveloperStructure.xsd", + "bytes": 3391, + "sha256": "0FA8EACF615F4A726F846D2938E18E84BB641BB28579BD9E2AB83F3D46D505FA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/ProviderHelp.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/ProviderHelp.xsd", + "bytes": 6766, + "sha256": "0E6ADBE5B896F075BF55AE6F2C1966E0E5596F79BC282C2D17CC36CCF41C30CF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/shellExecute.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/shellExecute.xsd", + "bytes": 2250, + "sha256": "52F48B0830346E30C782AA5C379DC547D0F40265FF1504276C904B819446409E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structure.xsd", + "bytes": 5973, + "sha256": "F35E24830E9E1A6AC79DEDFBC3F2EF03EFF8D41EF8C2414E8BA1C1A52E527391" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureGlossary.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureGlossary.xsd", + "bytes": 4186, + "sha256": "4425E365BA3B646B11E5625644D5F98B0151F60ED2BA0A28316570D7D68F6FA5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureList.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureList.xsd", + "bytes": 4173, + "sha256": "176D55D7715DDECCCBE51330E9C012E09972AE3E4A9C9AC8D1C5869B9510540A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureProcedure.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureProcedure.xsd", + "bytes": 1994, + "sha256": "D323AEB14F98A01583E538DD92EF445E1094DB861DC7205D5D2062976DE539B3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureTable.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureTable.xsd", + "bytes": 6137, + "sha256": "63D397B018E5D3783AE3057658D7ADCB44F968BFD81329D2FCEA1ABB91F2C7B8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/structureTaskExecution.xsd", + "bytes": 6794, + "sha256": "C37308C8CDB9AC988C94A850A848FC79707FB5D268F7E17072D6D2FB8E83E852" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/task.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/task.xsd", + "bytes": 22391, + "sha256": "808A13CEEEBDA94C0152B93A52A7825E20B5C01780A3EC6C2AE0C8AC0230ED74" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/Schemas/PSMaml/troubleshooting.xsd", + "source": "vendor/pwsh/Schemas/PSMaml/troubleshooting.xsd", + "bytes": 3239, + "sha256": "FBA82FDA979C33074968ED01AD95970FCF1151F3E7C298955BD501D5CA554633" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/sni.dll", + "source": "vendor/pwsh/sni.dll", + "bytes": 160040, + "sha256": "68705C4EF9AB818F2956A78E05F3FEFCE501A1448793B073B46110BEB49B47D6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.AppContext.dll", + "source": "vendor/pwsh/System.AppContext.dll", + "bytes": 15184, + "sha256": "6CD5D7FAE26815702D087EADBCE804B157CFDE3A57A8D897016822183FA2190E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Buffers.dll", + "source": "vendor/pwsh/System.Buffers.dll", + "bytes": 15184, + "sha256": "91F7DD9DBBEF1A7A2BD7C83EAB8110F8C1915DEB38723B05E7F65A5FFA491220" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.CodeDom.dll", + "source": "vendor/pwsh/System.CodeDom.dll", + "bytes": 476968, + "sha256": "28B49CE532AF31CA14A28C9B823E628457D2F3D62F96FDF0E54F206E5B1B7169" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.Concurrent.dll", + "source": "vendor/pwsh/System.Collections.Concurrent.dll", + "bytes": 231208, + "sha256": "EFD70AF76FE0182D974DDC1520F335E9764ECFDD6A517168944B76C07A31E7A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.dll", + "source": "vendor/pwsh/System.Collections.dll", + "bytes": 296744, + "sha256": "DC868C8736277084D26726D726C6CE07FFF9EAB3ACA2D3476CFACE8FC3B2403F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.Immutable.dll", + "source": "vendor/pwsh/System.Collections.Immutable.dll", + "bytes": 976680, + "sha256": "CDEFF36E2129E3A1CBF08698C4D692A13F2C25CD1C04CED0120CAEF982461354" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.NonGeneric.dll", + "source": "vendor/pwsh/System.Collections.NonGeneric.dll", + "bytes": 104272, + "sha256": "D84C613DA29BAF7D8DFD7E8AB7CD806E1086EB16B350C8377947B5EDB77B6EEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Collections.Specialized.dll", + "source": "vendor/pwsh/System.Collections.Specialized.dll", + "bytes": 104232, + "sha256": "E203E92897552D5109F4F096EE7019443F83DAC9DC7190217359255CEED1E3A3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Annotations.dll", + "source": "vendor/pwsh/System.ComponentModel.Annotations.dll", + "bytes": 198480, + "sha256": "62C92AA7C62EB1CF7B506F0F3A9A1D63A4F6941EAAF9F6D8180A6C68E05BCD9B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Composition.dll", + "source": "vendor/pwsh/System.ComponentModel.Composition.dll", + "bytes": 718656, + "sha256": "7294AB56FC43F92EBB60700463262EFFE7CCD5BF36777CC78648E5D57F683240" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Composition.Registration.dll", + "source": "vendor/pwsh/System.ComponentModel.Composition.Registration.dll", + "bytes": 108336, + "sha256": "0CB63A3465A5C2A21C2B63B37586E9E61CA6A5454363905F3283707286F2354E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.DataAnnotations.dll", + "source": "vendor/pwsh/System.ComponentModel.DataAnnotations.dll", + "bytes": 16680, + "sha256": "6D8DD0135A19828A0B4F8F62FB79AEE094ADD47EBFFA6D2F3BCDC8CC2CC2425D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.dll", + "source": "vendor/pwsh/System.ComponentModel.dll", + "bytes": 30544, + "sha256": "283C355A88E039F5A49F791F89F1AF721FB82576447E23859A4CB269085A69D1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.EventBasedAsync.dll", + "source": "vendor/pwsh/System.ComponentModel.EventBasedAsync.dll", + "bytes": 46888, + "sha256": "6B5D96FB4CF007AA303FE4EFA2AEEB345A9AD2E93DABD668EB234DC793453CD4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.Primitives.dll", + "source": "vendor/pwsh/System.ComponentModel.Primitives.dll", + "bytes": 79696, + "sha256": "A6268FE9416484303A678B72837B461AAA686D4CD4509430C1E4C5203397E63B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ComponentModel.TypeConverter.dll", + "source": "vendor/pwsh/System.ComponentModel.TypeConverter.dll", + "bytes": 767784, + "sha256": "9FC839CC0C4138DEEEE0080C72D29069D4EA22A4385FB78CDB4FEA6A911C30C0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Configuration.ConfigurationManager.dll", + "source": "vendor/pwsh/System.Configuration.ConfigurationManager.dll", + "bytes": 1046312, + "sha256": "68265C15B59229C842C7239D9456926096DDAB61AA39C2C29F90D98B12ADC609" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Configuration.dll", + "source": "vendor/pwsh/System.Configuration.dll", + "bytes": 19280, + "sha256": "87876603DABB331C85986D8AC56F0B317C4769051F369FB37409158B924F3FD0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Console.dll", + "source": "vendor/pwsh/System.Console.dll", + "bytes": 169768, + "sha256": "53B599B15B7EB4DB618946B943B00229D998D9BF5450CF23274B0F66BCDCB4EA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Core.dll", + "source": "vendor/pwsh/System.Core.dll", + "bytes": 23336, + "sha256": "FFFC038ABE34BB1BCDC13B57C146CD121B91D8BB2F8C80D2292F50C2563D8FAA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.Common.dll", + "source": "vendor/pwsh/System.Data.Common.dll", + "bytes": 2770728, + "sha256": "1CBEF4B075D6F36E914666010CE93FC831858622E775ABE9F73966B24C816AEE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.DataSetExtensions.dll", + "source": "vendor/pwsh/System.Data.DataSetExtensions.dll", + "bytes": 15656, + "sha256": "3FECFE3CAA4A21FF3BADC04354C036FDBE20636AD40A899B0F3CCF30471D39DE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.dll", + "source": "vendor/pwsh/System.Data.dll", + "bytes": 25384, + "sha256": "B801A07AD2B340F4808F1B683997A9D1AF9A70622FF104B1E89E37405403AC49" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.Odbc.dll", + "source": "vendor/pwsh/System.Data.Odbc.dll", + "bytes": 612144, + "sha256": "F609F522D9668A3761C21FD74D4C5EF8B1C76CF21E67377E550630110B5C66AB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.OleDb.dll", + "source": "vendor/pwsh/System.Data.OleDb.dll", + "bytes": 853808, + "sha256": "C6524C47A9584C10B7359A88D6ED5462E9BDFE4B1085129296E82F8C9A2F7BB0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Data.SqlClient.dll", + "source": "vendor/pwsh/System.Data.SqlClient.dll", + "bytes": 2434864, + "sha256": "C37A712C5E0B4F2D388611B0CEB9BCECA902FE8751075ABA4F5DCDCC291EDA03" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Design.dll", + "source": "vendor/pwsh/System.Design.dll", + "bytes": 23376, + "sha256": "3ED1B23FBA9A8E1B61F5036CC84058335F34BE254EACDFAE83F20FF484A422D8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Contracts.dll", + "source": "vendor/pwsh/System.Diagnostics.Contracts.dll", + "bytes": 16168, + "sha256": "E529CEFF3E677722C0779EFB90F464325C37F4598D2275684B6F3E44475DCA54" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Debug.dll", + "source": "vendor/pwsh/System.Diagnostics.Debug.dll", + "bytes": 15656, + "sha256": "97A2FAD35E9C07C9BB37B627FF5517A969421A0CA6C5D31DE665B9B72FB7F1CA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.DiagnosticSource.dll", + "source": "vendor/pwsh/System.Diagnostics.DiagnosticSource.dll", + "bytes": 501544, + "sha256": "1F3297FFFDFD0C97EC4C3AB7B628839B1F43FCB44193E44E96D4480631C5F07B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.EventLog.dll", + "source": "vendor/pwsh/System.Diagnostics.EventLog.dll", + "bytes": 366376, + "sha256": "47100510AA1489B538D0D8F58FF6D97AC79DBE3F09C541CB3E49B2CB8168E3F1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.EventLog.Messages.dll", + "source": "vendor/pwsh/System.Diagnostics.EventLog.Messages.dll", + "bytes": 800552, + "sha256": "5BF5DE8C874F06129A9A016F25A2532F323098F669EC766BEE212354F05ADB13" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.FileVersionInfo.dll", + "source": "vendor/pwsh/System.Diagnostics.FileVersionInfo.dll", + "bytes": 46888, + "sha256": "1A0073C382851890010F74B559F27A033EB783EEA1E38205782E6C8DACB5D10E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.PerformanceCounter.dll", + "source": "vendor/pwsh/System.Diagnostics.PerformanceCounter.dll", + "bytes": 300880, + "sha256": "B4E97299CB6BA71B5F4AB79774F1D5AFB72061CE7866188486770C9DC7D4F10B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Process.dll", + "source": "vendor/pwsh/System.Diagnostics.Process.dll", + "bytes": 329512, + "sha256": "7F70EA57A7868DFEEEA304D5DA3843B9B52D98767BC41A81162031BAA9361405" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.StackTrace.dll", + "source": "vendor/pwsh/System.Diagnostics.StackTrace.dll", + "bytes": 46888, + "sha256": "D696C411324CF024A89D830C57CB713C487950DB5FCD7A3F8790559675A02CE4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.TextWriterTraceListener.dll", + "source": "vendor/pwsh/System.Diagnostics.TextWriterTraceListener.dll", + "bytes": 67408, + "sha256": "308FA04540B284354DC0DB843B88EFCC562B43CB3EC5ABCE2ACE437521A31AEB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Tools.dll", + "source": "vendor/pwsh/System.Diagnostics.Tools.dll", + "bytes": 15144, + "sha256": "3EF95CD1BE21128929FF5A59EB656B79E2F1CF620E7EE8518AD54EF15642ABCC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.TraceSource.dll", + "source": "vendor/pwsh/System.Diagnostics.TraceSource.dll", + "bytes": 141136, + "sha256": "C707B27E877FB0C7D38848828AB08637915E2B7A05563AA1A08327DC69EEA911" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Diagnostics.Tracing.dll", + "source": "vendor/pwsh/System.Diagnostics.Tracing.dll", + "bytes": 16168, + "sha256": "72F5F814B5E142C87F514A38AF6C08D103FF14DAA22160867F987151C7F26C39" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.DirectoryServices.AccountManagement.dll", + "source": "vendor/pwsh/System.DirectoryServices.AccountManagement.dll", + "bytes": 710472, + "sha256": "809BF2787752D82E9B75760FB742E32901470013066E193E5A3C5D21654CD0C7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.DirectoryServices.dll", + "source": "vendor/pwsh/System.DirectoryServices.dll", + "bytes": 1021736, + "sha256": "18A53AF12ED4CDFC3F6FCBE0787E53101ACA6B5C28B730ABEC789FBBE040801A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.DirectoryServices.Protocols.dll", + "source": "vendor/pwsh/System.DirectoryServices.Protocols.dll", + "bytes": 366408, + "sha256": "B5AE31C1B36D24149561B5DD47F3D6E4124CCD3CA7DC94DB21405D945F84BD00" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.dll", + "source": "vendor/pwsh/System.dll", + "bytes": 50472, + "sha256": "2F5F3CCB0BB4E730F8EEDE5585E64AA4164CFDA1EF0C07B3E6C20DD4036F11B1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.Common.dll", + "source": "vendor/pwsh/System.Drawing.Common.dll", + "bytes": 923432, + "sha256": "924F083442617CADED6A137E8926791461D8735F2ECDF4821C56B5F836283155" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.Design.dll", + "source": "vendor/pwsh/System.Drawing.Design.dll", + "bytes": 15656, + "sha256": "3ECF8AD363EBA3E9DEDF01C63698BA257C0F743F734FF92B8FD03C5E7A2F5F26" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.dll", + "source": "vendor/pwsh/System.Drawing.dll", + "bytes": 20816, + "sha256": "E3FEC0493ED82D0F8BA9AB365428EA2008C5344184634D737DAD005F5FC4F1CE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Drawing.Primitives.dll", + "source": "vendor/pwsh/System.Drawing.Primitives.dll", + "bytes": 132944, + "sha256": "00CB49E97C49A30C2282904EB851B5C8D3F3E1B240C7DFF1BB850AC8E424BDAA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Dynamic.Runtime.dll", + "source": "vendor/pwsh/System.Dynamic.Runtime.dll", + "bytes": 16168, + "sha256": "D2A82A14EE1DFCE78D7B4F1F3D161935C56CB4C07EBC8A7DAFA8260745FA3771" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Formats.Asn1.dll", + "source": "vendor/pwsh/System.Formats.Asn1.dll", + "bytes": 239440, + "sha256": "027EC42F8A81D9999425FD9B75FDA1D20F610F2FC2F57C2B55D4D37B2A1FE75C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Formats.Nrbf.dll", + "source": "vendor/pwsh/System.Formats.Nrbf.dll", + "bytes": 137040, + "sha256": "9852CE3FD4EAE4B7638F910860E382BA8EDE1CF51628C3619691B7F24CD4DACA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Formats.Tar.dll", + "source": "vendor/pwsh/System.Formats.Tar.dll", + "bytes": 272168, + "sha256": "D4E9806746831BDE8670F8AF3A0DF7AA370678CDDB7950B28F4FAD0BA3353727" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Globalization.Calendars.dll", + "source": "vendor/pwsh/System.Globalization.Calendars.dll", + "bytes": 15656, + "sha256": "48F3B8DB0B6AF61E000C70C29C977B9CEE6D4C6FA0CF794418DFF29D1BA4E709" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Globalization.dll", + "source": "vendor/pwsh/System.Globalization.dll", + "bytes": 15656, + "sha256": "769DDE445009C78BE8D4CDB28DCC6603DF2218DAFE9491F5F1B915B1945A3F6E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Globalization.Extensions.dll", + "source": "vendor/pwsh/System.Globalization.Extensions.dll", + "bytes": 15144, + "sha256": "D3497DFC648BF299FCE08FC992918C542D1ED66EBD19EF726595369B0CD513B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.Brotli.dll", + "source": "vendor/pwsh/System.IO.Compression.Brotli.dll", + "bytes": 83752, + "sha256": "A14E6E6B390BF8F96A6EF0AA4450C2B0A8455202309C589D1BA8995C0D586611" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.dll", + "source": "vendor/pwsh/System.IO.Compression.dll", + "bytes": 419664, + "sha256": "9362497FA6D4ABE5B8D3DD9F2256564E9A5C84BFB8E9C709A741503DC602EBAA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.FileSystem.dll", + "source": "vendor/pwsh/System.IO.Compression.FileSystem.dll", + "bytes": 15184, + "sha256": "C2635E40545F18B1D0B15887C1AE5DE5504BB21A407E9371BEE83CE8974EA84D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.Native.dll", + "source": "vendor/pwsh/System.IO.Compression.Native.dll", + "bytes": 937296, + "sha256": "9FBB2CE965AE5F0AFB8601F9190326C6FC7A345F4B6E3FD4AF6740A4F6CF2548" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Compression.ZipFile.dll", + "source": "vendor/pwsh/System.IO.Compression.ZipFile.dll", + "bytes": 100136, + "sha256": "79409DEBB2680FF86B7513FBDF622CAAEB791328358F4290EC6A78B403FD4F48" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.dll", + "source": "vendor/pwsh/System.IO.dll", + "bytes": 15656, + "sha256": "5329ADEC3B4B1C869E9814912B80E50E0094F795EFF2A7E360FCCB81A1BC55C9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.AccessControl.dll", + "source": "vendor/pwsh/System.IO.FileSystem.AccessControl.dll", + "bytes": 104232, + "sha256": "1C54898A3EC1EB02A9DFF3BA726C6328D1D0F306926A9A370C0C468E065BAB17" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.dll", + "source": "vendor/pwsh/System.IO.FileSystem.dll", + "bytes": 15696, + "sha256": "CB1CF391EF106BEECB0C7D835E2B24D03101AA7B6D4B279B98C235654ED521C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.DriveInfo.dll", + "source": "vendor/pwsh/System.IO.FileSystem.DriveInfo.dll", + "bytes": 55120, + "sha256": "AD0FA0F61602BABDB65280CFB5295D7F3905E7739431CA64D531919703729D2E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.Primitives.dll", + "source": "vendor/pwsh/System.IO.FileSystem.Primitives.dll", + "bytes": 15184, + "sha256": "2673EA5084171A31D826A8D93C91C7F34798385EAC41155932192A3C9066C20B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.FileSystem.Watcher.dll", + "source": "vendor/pwsh/System.IO.FileSystem.Watcher.dll", + "bytes": 87888, + "sha256": "7B5E2B5E413C7A234F971FE2C1E0A04063380018336F0C22F0C52955B6E7578F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.IsolatedStorage.dll", + "source": "vendor/pwsh/System.IO.IsolatedStorage.dll", + "bytes": 87848, + "sha256": "0494E0FC71AECF0AC89EA062E0887BCB7075DFEA986B2D87E88DEEF2ADDEB5B0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.MemoryMappedFiles.dll", + "source": "vendor/pwsh/System.IO.MemoryMappedFiles.dll", + "bytes": 83752, + "sha256": "8D17D7A790D463C15C3D683A92D449A1D02C423DA680D2DC5B809C9CDE1B1C2F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Packaging.dll", + "source": "vendor/pwsh/System.IO.Packaging.dll", + "bytes": 321320, + "sha256": "6B178F0D551444A502A66055EBDF3FBB65FCDE25056C43866055C2B5B759F2AA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Pipelines.dll", + "source": "vendor/pwsh/System.IO.Pipelines.dll", + "bytes": 190248, + "sha256": "40A3244F2A4EBA94430C8813F7FD4778E15E1D957DCE9034D8DFA17725A0FCF0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Pipes.AccessControl.dll", + "source": "vendor/pwsh/System.IO.Pipes.AccessControl.dll", + "bytes": 15656, + "sha256": "1FA1F01D59F34DD66398931BBC51A4D1C981CE422FC149E7EEC914991A8588F6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Pipes.dll", + "source": "vendor/pwsh/System.IO.Pipes.dll", + "bytes": 165672, + "sha256": "A1858A82360321B757B41F8F7C33CB793D626A712CC29409E5E8D77493F62062" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.Ports.dll", + "source": "vendor/pwsh/System.IO.Ports.dll", + "bytes": 165680, + "sha256": "378B0F569EAF4BA9E6B8DE64186A9CEAEE127FE0BFB13B7D3E832050F0A8D233" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.IO.UnmanagedMemoryStream.dll", + "source": "vendor/pwsh/System.IO.UnmanagedMemoryStream.dll", + "bytes": 15656, + "sha256": "A18AC4B8B1DE6DFC0FA4F9CDA77EF9763E1721A52731B7AD8000BF733FA9FE8E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.AsyncEnumerable.dll", + "source": "vendor/pwsh/System.Linq.AsyncEnumerable.dll", + "bytes": 1292112, + "sha256": "7648125B74F53ABFE7E6F627E4D56D555BCF1AB1A0BFE4912C21969D47110C78" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.dll", + "source": "vendor/pwsh/System.Linq.dll", + "bytes": 698152, + "sha256": "F170005154FE77B1A5AF72179A454CA1D24FF3F805E6DC1A629EF1CF82D3E7AD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.Expressions.dll", + "source": "vendor/pwsh/System.Linq.Expressions.dll", + "bytes": 3639080, + "sha256": "E2627EA364EC3868E20B9F84D2C2A3ECF5FAAD4DC548F53A9D0C6A2D6F9FB282" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.Parallel.dll", + "source": "vendor/pwsh/System.Linq.Parallel.dll", + "bytes": 776016, + "sha256": "75459D9290969BC7E0BE2A352F7E4BC910FAD343F51C01359570CDACE014755D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Linq.Queryable.dll", + "source": "vendor/pwsh/System.Linq.Queryable.dll", + "bytes": 182056, + "sha256": "DFCCADFD6219CE61D1C1263191C919FF2D095395E1B796B82ECF15C486A2B70C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Management.Automation.dll", + "source": "vendor/pwsh/System.Management.Automation.dll", + "bytes": 19658592, + "sha256": "ED8D39663E05435AAB59F1C7FB0AD20292E22B0CB12CF47AAD4B62B31E4D4126" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Management.dll", + "source": "vendor/pwsh/System.Management.dll", + "bytes": 833352, + "sha256": "AD07E0C250FE08CE6B23D63B7A564DA19E128D0A5A7ABA9CE49810ED7752A3DF" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Memory.dll", + "source": "vendor/pwsh/System.Memory.dll", + "bytes": 161576, + "sha256": "A714F1A2B4B8D2695BEA4E5D2A85E62621CED26A7E4EBC5FCD61E85EE4838B77" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.dll", + "source": "vendor/pwsh/System.Net.dll", + "bytes": 17192, + "sha256": "D16A0494E92F84355FB5526A4E2F0587AB4F3C296E9971903E17523169E58E7E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Http.dll", + "source": "vendor/pwsh/System.Net.Http.dll", + "bytes": 1746728, + "sha256": "ED96D8A3995CF09E84981DB6EE8A5199E41FC2A2599749F70CF84ADA7D4CCE53" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Http.Json.dll", + "source": "vendor/pwsh/System.Net.Http.Json.dll", + "bytes": 128848, + "sha256": "DC58C398BAEF3AFFB78A9F2440E153D66BD8DC5F78079ECA71AD1BF18C726F19" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Http.WinHttpHandler.dll", + "source": "vendor/pwsh/System.Net.Http.WinHttpHandler.dll", + "bytes": 304960, + "sha256": "EA7676CB7C3FBA9167FD3B4308D422DBEC961282319267F924306744C2BC12F2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.HttpListener.dll", + "source": "vendor/pwsh/System.Net.HttpListener.dll", + "bytes": 534312, + "sha256": "5EF8C9B43FEDE96449D0AE2C99423658C1549121F31A260078E00173A330CAE4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Mail.dll", + "source": "vendor/pwsh/System.Net.Mail.dll", + "bytes": 481064, + "sha256": "B0C2C70BA2035E683E2232699E0547DF7AB4F361DAF4A5418C8F3B45EA147C13" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.NameResolution.dll", + "source": "vendor/pwsh/System.Net.NameResolution.dll", + "bytes": 124712, + "sha256": "EB7AC4D71D4DDD3A05129221F35C4C02926DAC2A9A664EBBC442B1307D6A74E4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.NetworkInformation.dll", + "source": "vendor/pwsh/System.Net.NetworkInformation.dll", + "bytes": 157480, + "sha256": "2A85A3E70D84F237E10A9BEAE7D4B1FDFE05CE0DE135D5CA45B46DE48491CDE3" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Ping.dll", + "source": "vendor/pwsh/System.Net.Ping.dll", + "bytes": 91944, + "sha256": "1269783428EFC8C8A430C20ECEE9F931AA40D790B953E1D6C97FAAA71AC660B7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Primitives.dll", + "source": "vendor/pwsh/System.Net.Primitives.dll", + "bytes": 210768, + "sha256": "6CA45C7299B6B76A5650F01E63A3290AC50432C6301CEA135C89FFC54AF72E25" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Quic.dll", + "source": "vendor/pwsh/System.Net.Quic.dll", + "bytes": 358184, + "sha256": "11F53F8A10F14C789953C82CBEE8BEF8A9F0DDF3F86527125FBC6797E8E3860D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Requests.dll", + "source": "vendor/pwsh/System.Net.Requests.dll", + "bytes": 374568, + "sha256": "663A98F7F7A8082FA47A7197F362041E1479BF0A50D57A00872A2F128BD97121" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Security.dll", + "source": "vendor/pwsh/System.Net.Security.dll", + "bytes": 665424, + "sha256": "2CA204D66FE544FEF6D92E0EA1CDD2DF2F80160E6FBA6D04E3BA80382C69694D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.ServerSentEvents.dll", + "source": "vendor/pwsh/System.Net.ServerSentEvents.dll", + "bytes": 83792, + "sha256": "BF227E04A1D642D83211A4A1136FB31946B8B7148BBA5DDE2310D7ABCBCADE71" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.ServicePoint.dll", + "source": "vendor/pwsh/System.Net.ServicePoint.dll", + "bytes": 15144, + "sha256": "C77F8868279BF5F0D7129FDF5240ABC2527437B8928E8ABF06BC774438416C64" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.Sockets.dll", + "source": "vendor/pwsh/System.Net.Sockets.dll", + "bytes": 530216, + "sha256": "B0386EBC2A62CDF0BA4D36973B3347C4372D9BFBCEFABD8E3CDD1B0194753653" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebClient.dll", + "source": "vendor/pwsh/System.Net.WebClient.dll", + "bytes": 165712, + "sha256": "52A615FD624996B513848D1CA1BDA1D02D49991AF0A000909779DDBF35FB17EC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebHeaderCollection.dll", + "source": "vendor/pwsh/System.Net.WebHeaderCollection.dll", + "bytes": 63272, + "sha256": "59E2FF9EE6AC647929BD689486F141645C2412B5B01CBF8A397D2888ADF0E764" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebProxy.dll", + "source": "vendor/pwsh/System.Net.WebProxy.dll", + "bytes": 42832, + "sha256": "41526195B63CD77C37DF1A99F621E2E5A8F40666E9613D974E9935D99F662905" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebSockets.Client.dll", + "source": "vendor/pwsh/System.Net.WebSockets.Client.dll", + "bytes": 104232, + "sha256": "45996C74C3C6C2E144EC006DEB5C70781F2E3C333782B815BB44B612D41EA27E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Net.WebSockets.dll", + "source": "vendor/pwsh/System.Net.WebSockets.dll", + "bytes": 243536, + "sha256": "7EF764FC11991DB23A90920DC3D49BBDDC6D2F766529D19463B17EAEE720CA71" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Numerics.dll", + "source": "vendor/pwsh/System.Numerics.dll", + "bytes": 15144, + "sha256": "49351764F0118538F1E531D91F416F325F362F6638C55896AF3D0FF631296B05" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Numerics.Vectors.dll", + "source": "vendor/pwsh/System.Numerics.Vectors.dll", + "bytes": 15656, + "sha256": "F7D2DBA3D30B09FA8CA802CDA5D0EE0827CB3AA44E1E19F790F62C390DDF5DE8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ObjectModel.dll", + "source": "vendor/pwsh/System.ObjectModel.dll", + "bytes": 79656, + "sha256": "9A7D09EF51C648C4CE51A0E21CDC64DEDE904420CCEC224D62C09FCEEE6B150A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Printing.dll", + "source": "vendor/pwsh/System.Printing.dll", + "bytes": 968488, + "sha256": "1CB55A01A300A2EBC0C8E14BD498005F4B34A0F4FC5F5CF7A88D50EC8AB8DCE6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.CoreLib.dll", + "source": "vendor/pwsh/System.Private.CoreLib.dll", + "bytes": 16033616, + "sha256": "63D83DD8169F23820F5D3BF0F0399F60E5E26200361787DFCB1C24CABC76E423" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.DataContractSerialization.dll", + "source": "vendor/pwsh/System.Private.DataContractSerialization.dll", + "bytes": 2062120, + "sha256": "A34745F8431BA8D38EAC37A65BCBD644C0F50AB88AFD66AF236A9A49C693B9F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Uri.dll", + "source": "vendor/pwsh/System.Private.Uri.dll", + "bytes": 255784, + "sha256": "D14188A1352F793F862726FA2C956DC270FB0E2578D46C6561F682FF2B4E110E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Windows.Core.dll", + "source": "vendor/pwsh/System.Private.Windows.Core.dll", + "bytes": 1906472, + "sha256": "0341B9E7466D143656E804D694A3C0B0BDB040FD44DE709047873661EC638884" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Windows.GdiPlus.dll", + "source": "vendor/pwsh/System.Private.Windows.GdiPlus.dll", + "bytes": 427816, + "sha256": "4ED46AD143DB84BDFA38C0973F1525296264FCCCA14AAB7D8C177982390C8260" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Xml.dll", + "source": "vendor/pwsh/System.Private.Xml.dll", + "bytes": 7788328, + "sha256": "65F1252BAD1A18D985A55D6434608E4A66DBA8A426310B2C6FDAE12B09E3FAF6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Private.Xml.Linq.dll", + "source": "vendor/pwsh/System.Private.Xml.Linq.dll", + "bytes": 390992, + "sha256": "8A82630530FB3AD2E29A3D755BCE1F50497EC764804DDC7B014428A88E6DF16C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Context.dll", + "source": "vendor/pwsh/System.Reflection.Context.dll", + "bytes": 190256, + "sha256": "B7B226B5E08F84B6D6DDF77EED92A112ECA3D61087EF689F5BCCEB8996A736AE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.DispatchProxy.dll", + "source": "vendor/pwsh/System.Reflection.DispatchProxy.dll", + "bytes": 75560, + "sha256": "827499A045D2E71731BEB0391D392436D911E9D3195663E14F3CCA0F6B13691A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.dll", + "source": "vendor/pwsh/System.Reflection.dll", + "bytes": 16168, + "sha256": "2D0A26F77691CAC4D6522D9212FD70116DC183E1035B3AE4E6087C1EB9255F89" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Emit.dll", + "source": "vendor/pwsh/System.Reflection.Emit.dll", + "bytes": 309032, + "sha256": "F3CAEDCFF578946F80ECE81947A5ABB5D905BF1567D77D05C0725E0FF1FA1782" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Emit.ILGeneration.dll", + "source": "vendor/pwsh/System.Reflection.Emit.ILGeneration.dll", + "bytes": 15696, + "sha256": "9D003F3B5DF43C3062FA1E363E5F6B3ECC5D4B84213FC7397DA53E583494F390" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Emit.Lightweight.dll", + "source": "vendor/pwsh/System.Reflection.Emit.Lightweight.dll", + "bytes": 15656, + "sha256": "0461AC768E7F2E3BCD20A016C1E06E207092DF250458C868FF628C2E7766B748" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Extensions.dll", + "source": "vendor/pwsh/System.Reflection.Extensions.dll", + "bytes": 15144, + "sha256": "28B889CEA13DCE1ECFECF62CF887C1C9587255596333680C8441120E4EDA25C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Metadata.dll", + "source": "vendor/pwsh/System.Reflection.Metadata.dll", + "bytes": 1156904, + "sha256": "B453AC5B26229328268803037B95EBDFD9EA3C7AABD830513BD6A39CF73F7076" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.Primitives.dll", + "source": "vendor/pwsh/System.Reflection.Primitives.dll", + "bytes": 15656, + "sha256": "B20C388943B74385F712CDAF74CDE691F6EEE14CB5320D8136C3D40B57C02D42" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Reflection.TypeExtensions.dll", + "source": "vendor/pwsh/System.Reflection.TypeExtensions.dll", + "bytes": 42832, + "sha256": "7CEDCBAB51CACAEEECA6A766F92A2B93FD74CA88D326BADFAD090067D9D19150" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.Extensions.dll", + "source": "vendor/pwsh/System.Resources.Extensions.dll", + "bytes": 194344, + "sha256": "CFBD1384747751A7BA4CE8A16F191501CDB762567029FF57F6E768AD4D4C58FD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.Reader.dll", + "source": "vendor/pwsh/System.Resources.Reader.dll", + "bytes": 15144, + "sha256": "3E83796FDDDA54188EBE691C4B45641BB2FE7780F8E4D1928D5F4B435BB6C6F8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.ResourceManager.dll", + "source": "vendor/pwsh/System.Resources.ResourceManager.dll", + "bytes": 15656, + "sha256": "DA9AD363EE8A17881D33768D75A3D9689605C626415AF780D0FF5BA743C3A3F4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Resources.Writer.dll", + "source": "vendor/pwsh/System.Resources.Writer.dll", + "bytes": 50984, + "sha256": "E089809FA22829491D90FB8BCBA9210EE7F37A7C9D07D0FA2E333C15016B87A4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Caching.dll", + "source": "vendor/pwsh/System.Runtime.Caching.dll", + "bytes": 202560, + "sha256": "DB5D7363FFAF4616FD9FF0225DEFFE1644418D792AED29C90E63924A852A6838" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.CompilerServices.Unsafe.dll", + "source": "vendor/pwsh/System.Runtime.CompilerServices.Unsafe.dll", + "bytes": 15144, + "sha256": "3D00BF59EE665068C013BBF7258A0BF46A86CDACEC9FC5BD727ABFF9D190D3E5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.CompilerServices.VisualC.dll", + "source": "vendor/pwsh/System.Runtime.CompilerServices.VisualC.dll", + "bytes": 30504, + "sha256": "84820BF55EDAEEBCFC2F8AA5ED64ECDCB8EDA8A6ED1D44AA9DFD28C8AE362FD1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.dll", + "source": "vendor/pwsh/System.Runtime.dll", + "bytes": 44840, + "sha256": "2C733C433C3D26C0EDB1EB89C9ED7A53CB3194092D1A56A79BF14AB71F4A3425" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Extensions.dll", + "source": "vendor/pwsh/System.Runtime.Extensions.dll", + "bytes": 17704, + "sha256": "71CAB63C02F634D86377BB06C481E733347ABC22B56106B39228A1A01305DE10" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Handles.dll", + "source": "vendor/pwsh/System.Runtime.Handles.dll", + "bytes": 15656, + "sha256": "784E5ADCC91FB6C3154C3F4E9C7C4E311892A2182686205DC4C2B3118C8355BA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.InteropServices.dll", + "source": "vendor/pwsh/System.Runtime.InteropServices.dll", + "bytes": 112464, + "sha256": "F3587B04D04F05C5A22E129EBE3DABE34243315E035AB6BBD91A4A3350FBAD18" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.InteropServices.JavaScript.dll", + "source": "vendor/pwsh/System.Runtime.InteropServices.JavaScript.dll", + "bytes": 50984, + "sha256": "A865BF2368D23E2328C68DAC87B136E71E00BA8136A0CF4B7EC10F71CC3525A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", + "source": "vendor/pwsh/System.Runtime.InteropServices.RuntimeInformation.dll", + "bytes": 15656, + "sha256": "4E38FBC3D52D0EA22A13D8569D24F40C3C6043DAA28DDF242CF77288FBBE2DEA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Intrinsics.dll", + "source": "vendor/pwsh/System.Runtime.Intrinsics.dll", + "bytes": 17192, + "sha256": "58A88BA0B94E29F26C3846697A6A251FE7677CBCC7C2B8950090D4AF59109720" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Loader.dll", + "source": "vendor/pwsh/System.Runtime.Loader.dll", + "bytes": 15656, + "sha256": "B3A9500ED9E7F4A698FB46513B8C73B5AA752331597A544E2722B2C5DB262B09" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Numerics.dll", + "source": "vendor/pwsh/System.Runtime.Numerics.dll", + "bytes": 354088, + "sha256": "9F067C312E0C0C097D56BB3487933DCDA758D547480AB76E5A2987AEADD8FD7E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.dll", + "bytes": 17192, + "sha256": "CA3289757E8F3BC2BCCBC0B02205CD0F7A21AD9845417FB9496A7C9FA4117DF5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Formatters.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Formatters.dll", + "bytes": 124712, + "sha256": "7832E9DCC1A36276B2DF00427C5C926F1A48DFF2D03977632A83AD138D853013" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Json.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Json.dll", + "bytes": 15656, + "sha256": "3E23CF2C3DABB7F3D58A88838884FFA68E0A73E40EB0AFCB316BA29DDD567A38" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Primitives.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Primitives.dll", + "bytes": 38736, + "sha256": "1C2DED997862CD07FD93BD8450A98517875F7BB52A4C4E651A4D3B8832D7DB9C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Runtime.Serialization.Xml.dll", + "source": "vendor/pwsh/System.Runtime.Serialization.Xml.dll", + "bytes": 16680, + "sha256": "6E9DFAA42EEF9F406696E5D2372C100143BD47E0E95EC21158FE5FA7DD2EE428" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.AccessControl.dll", + "source": "vendor/pwsh/System.Security.AccessControl.dll", + "bytes": 227112, + "sha256": "AA6BF70A513CB43DAAD41F91BDE35740EC28456481639A57508BE02B08F1969B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Claims.dll", + "source": "vendor/pwsh/System.Security.Claims.dll", + "bytes": 100136, + "sha256": "6AE287447243764B203F58EABBC8E5B9EBCA135F6174AD73DD06AE572936DCC4" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Algorithms.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Algorithms.dll", + "bytes": 17192, + "sha256": "D679F5CAF8BCF0007E4003D5CE728EF3DE03EB88F5DC90528AEEE1D266436318" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Cng.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Cng.dll", + "bytes": 16168, + "sha256": "E6D327C11059988B2C12584040C588CAEC73334E9745FFE85C3522BC85756F49" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Csp.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Csp.dll", + "bytes": 16168, + "sha256": "629D0E04F3605F8CABCB04105EB7CA369D3AF023FBE171CBACB60476395509A7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.dll", + "source": "vendor/pwsh/System.Security.Cryptography.dll", + "bytes": 2549584, + "sha256": "ACB3E95912A1B3D2CC96BFD42346328462A930AC77858DFC62D7D712CEED4F5E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Encoding.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Encoding.dll", + "bytes": 15656, + "sha256": "7D824AC50A0D6A75B8F85BA007BD84BEC6714CCFFDEBEAD00A01EDB9CCD3918E" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.OpenSsl.dll", + "source": "vendor/pwsh/System.Security.Cryptography.OpenSsl.dll", + "bytes": 15656, + "sha256": "4F6747F232BF9C3AC7B5352D818E31131DCC299503806A8190E9FBEF95D4A50A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Pkcs.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Pkcs.dll", + "bytes": 767784, + "sha256": "AC3A317E89EF612F3DC6169887C8A1B0A692121E1FF48D402F75F4A82DA4899A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Primitives.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Primitives.dll", + "bytes": 15656, + "sha256": "E17D719732B43BA6BEB751A7EF55390B9E9B9B659826FABF1288CD03AF0241C2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.ProtectedData.dll", + "source": "vendor/pwsh/System.Security.Cryptography.ProtectedData.dll", + "bytes": 59216, + "sha256": "1E5826CE27C48902506553C8BC7C648004A2767ED786979538D725BA2561E2D9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.X509Certificates.dll", + "source": "vendor/pwsh/System.Security.Cryptography.X509Certificates.dll", + "bytes": 16680, + "sha256": "752EB010626928B51584B2B58D7C67110D16E73B9ACE071ADC6009F33275636C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Cryptography.Xml.dll", + "source": "vendor/pwsh/System.Security.Cryptography.Xml.dll", + "bytes": 452392, + "sha256": "FF3E018DA84A0294FDD2A4F8A120CBC05B40E99CE4AD57CE45809AB3280C6DEC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.dll", + "source": "vendor/pwsh/System.Security.dll", + "bytes": 18216, + "sha256": "4C5D662D946BA84F5159FAA8197661AAD054B98899977F5BFC3C4F7A66AC13FC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Permissions.dll", + "source": "vendor/pwsh/System.Security.Permissions.dll", + "bytes": 190248, + "sha256": "37D4C12B1BAC234541871123511525B680089A61758CE6F28B09A18F8F0B39E6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Principal.dll", + "source": "vendor/pwsh/System.Security.Principal.dll", + "bytes": 15144, + "sha256": "40671DC07A55EC29B00DA6D45610CB663A8C03AE3A686695A9E28412AB87ACF5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.Principal.Windows.dll", + "source": "vendor/pwsh/System.Security.Principal.Windows.dll", + "bytes": 177960, + "sha256": "4C6EE4BC691D111FF4500A6D455DEEEEB3F56F14F5CD686F404BA063C8D2741B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Security.SecureString.dll", + "source": "vendor/pwsh/System.Security.SecureString.dll", + "bytes": 15656, + "sha256": "83941FEE8EB17E7215855D7D655243DC5D1D5ED631C92C39F4163D4A491AEBE8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.dll", + "source": "vendor/pwsh/System.ServiceModel.dll", + "bytes": 25160, + "sha256": "CF390C8379F3249937DA11DAD3A5BA88804A7453503A246BE01FC849B899653B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Duplex.dll", + "source": "vendor/pwsh/System.ServiceModel.Duplex.dll", + "bytes": 15432, + "sha256": "5E7D8985922C7D7D673D2D204C2F6D12E3F70F70D0E9FF15507FECD2026A8815" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Http.dll", + "source": "vendor/pwsh/System.ServiceModel.Http.dll", + "bytes": 481096, + "sha256": "4B4F5C934A4CBAD1227507570697BC75F7E332A7A5D85EC62F9A24E5897E7B32" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.NetFramingBase.dll", + "source": "vendor/pwsh/System.ServiceModel.NetFramingBase.dll", + "bytes": 477000, + "sha256": "98175B9ABD29326B1C410AA6ED82370677F1781A5A26B7A9AB8479C76A4A6EF2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.NetTcp.dll", + "source": "vendor/pwsh/System.ServiceModel.NetTcp.dll", + "bytes": 137032, + "sha256": "8169433D0DF8D3186ABD4C94CCFF3987CA267D56F5A384CF539E613C997FC8FA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Primitives.dll", + "source": "vendor/pwsh/System.ServiceModel.Primitives.dll", + "bytes": 5388112, + "sha256": "C53A7289B255E6A32E42FEB63CE24E32C2E0FAF4B5972EC2EC83BB17C4F01EF9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Security.dll", + "source": "vendor/pwsh/System.ServiceModel.Security.dll", + "bytes": 16928, + "sha256": "99EC51765CF53B36DC1064A48DB4C910EDB17C92E176E03B947E5E200B82F7D2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Syndication.dll", + "source": "vendor/pwsh/System.ServiceModel.Syndication.dll", + "bytes": 313152, + "sha256": "CF572334CFBB7791716282B0802DD2A33691B55B13FE3F65A45058E57700EECA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceModel.Web.dll", + "source": "vendor/pwsh/System.ServiceModel.Web.dll", + "bytes": 16680, + "sha256": "58A681BB145F2CDC9E996B48E40FFFAEA30DE469A535DDB985454A4AF8828D50" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceProcess.dll", + "source": "vendor/pwsh/System.ServiceProcess.dll", + "bytes": 15696, + "sha256": "203F52B3F352E0E3DF54B9F588131743EB562AF8612BFA2C2DEBAB21B29110FA" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ServiceProcess.ServiceController.dll", + "source": "vendor/pwsh/System.ServiceProcess.ServiceController.dll", + "bytes": 149336, + "sha256": "2CEC344EF1D23339A5257FA8E9B96D4842B1E15AFD04D1A56261B1990A0F9B7C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Speech.dll", + "source": "vendor/pwsh/System.Speech.dll", + "bytes": 1386304, + "sha256": "1A06AB169EACB6FB9CDF76096284284236F6C55A3F588CAA24BD7AF06A8F4A10" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encoding.CodePages.dll", + "source": "vendor/pwsh/System.Text.Encoding.CodePages.dll", + "bytes": 861992, + "sha256": "9D8D9E0FA33437502FD659FCB726E295CF439E56BDB044970FE5B715D7813E37" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encoding.dll", + "source": "vendor/pwsh/System.Text.Encoding.dll", + "bytes": 15656, + "sha256": "1E717906C9F94DAA686B68E9E86F4A1C08916DB62B05AF5A17661A5B83B9C7A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encoding.Extensions.dll", + "source": "vendor/pwsh/System.Text.Encoding.Extensions.dll", + "bytes": 15656, + "sha256": "F507CC18B0449947A86606DFE9281B89316B39C1BA60A9249E41C0A1CD30E201" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Encodings.Web.dll", + "source": "vendor/pwsh/System.Text.Encodings.Web.dll", + "bytes": 124712, + "sha256": "AB138D71B868D606ACD1D48EF17B9F12E51F75ECD297120ADD0C4552F6758160" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.Json.dll", + "source": "vendor/pwsh/System.Text.Json.dll", + "bytes": 1890088, + "sha256": "ABCECB266DFD7AF409EBA45CD2FD581D40F72D5FE916A1A95F05B32AC738968F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Text.RegularExpressions.dll", + "source": "vendor/pwsh/System.Text.RegularExpressions.dll", + "bytes": 1034024, + "sha256": "CC5C01E9C1591DF68708CA6386C1259B0AC34257336CF53106CE784F9D1175C0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.AccessControl.dll", + "source": "vendor/pwsh/System.Threading.AccessControl.dll", + "bytes": 75600, + "sha256": "78A2D7FD94410ABEFEFC9AF09FADB9FBDC7069F5572949C62E81423DC9F8A33B" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Channels.dll", + "source": "vendor/pwsh/System.Threading.Channels.dll", + "bytes": 157480, + "sha256": "104051A542F04760C2C89998A49396E0F4FCE403E86D7D976984280BF835C966" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.dll", + "source": "vendor/pwsh/System.Threading.dll", + "bytes": 83792, + "sha256": "B1AAE26002952704D7D7913170DFD3C786DE6DEA50463BB671A9AA99423320BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Overlapped.dll", + "source": "vendor/pwsh/System.Threading.Overlapped.dll", + "bytes": 15656, + "sha256": "239A90D22546A4ED01A05BFA8CEBA846E4C73F73BA6C2B3E06E176F859890DB5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.Dataflow.dll", + "source": "vendor/pwsh/System.Threading.Tasks.Dataflow.dll", + "bytes": 472872, + "sha256": "1412A7614F9FAFFE37E102384B0917B436BA3CAC685F8ED64607912C805DB9E9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.dll", + "source": "vendor/pwsh/System.Threading.Tasks.dll", + "bytes": 16680, + "sha256": "3C3CBA2C78819A31B400B52014678A4AF1DC1F482B2424E38B85E28EEF1C40D0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.Extensions.dll", + "source": "vendor/pwsh/System.Threading.Tasks.Extensions.dll", + "bytes": 15656, + "sha256": "DAAC261AD95033C320720BE3AE53263ABD6344FD548BA36F405D2D5AB39207F2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Tasks.Parallel.dll", + "source": "vendor/pwsh/System.Threading.Tasks.Parallel.dll", + "bytes": 132904, + "sha256": "47733F454E9D7DA1BEC529A36AE86DB431E8A89AD1FE84641E80C9207D91F4C7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Thread.dll", + "source": "vendor/pwsh/System.Threading.Thread.dll", + "bytes": 15656, + "sha256": "0B016736E7E4995A7E0A127A7BAEB6F8487A97433E2DD034B8CC8DD5FA1048AC" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.ThreadPool.dll", + "source": "vendor/pwsh/System.Threading.ThreadPool.dll", + "bytes": 15656, + "sha256": "104C0F37199430CCEBF3B81EB28E4C072547447075EACFF8D488954035BD231F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Threading.Timer.dll", + "source": "vendor/pwsh/System.Threading.Timer.dll", + "bytes": 15144, + "sha256": "632C125112639BD454C6E07DC2481E9EEC8D9FC300FA0EA6DF622729DE499A77" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Transactions.dll", + "source": "vendor/pwsh/System.Transactions.dll", + "bytes": 16680, + "sha256": "0F55A3E2F9BAA40E596E93C76D97C0F5B4A53775D8865F57877E77FB90B8E09D" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Transactions.Local.dll", + "source": "vendor/pwsh/System.Transactions.Local.dll", + "bytes": 640808, + "sha256": "B7CE5D6C73B6E2A298946474473C6E12AC2A81584EFAC155F85ACAD4CD1695E0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.ValueTuple.dll", + "source": "vendor/pwsh/System.ValueTuple.dll", + "bytes": 15696, + "sha256": "1C02D7D7BEF69434A1F9EA0BFAF4A83321E644A5478D39681849883BC1114A61" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Web.dll", + "source": "vendor/pwsh/System.Web.dll", + "bytes": 15144, + "sha256": "88650D1871B5BAC043665F06610799862B66303794FB76DE0DB5E58C86E1B1D9" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Web.HttpUtility.dll", + "source": "vendor/pwsh/System.Web.HttpUtility.dll", + "bytes": 63312, + "sha256": "1CFF49AD07732F67D54CFDF00100692499B24F00955DED4DB528012F5F11A9A5" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Web.Services.Description.dll", + "source": "vendor/pwsh/System.Web.Services.Description.dll", + "bytes": 800576, + "sha256": "AB6BFC2802083328D63EA2A520ED6F5DCF2AD2E0D67276A3884D577A2C13EC7A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Controls.Ribbon.dll", + "source": "vendor/pwsh/System.Windows.Controls.Ribbon.dll", + "bytes": 1439528, + "sha256": "E7E47AF351ED12E58337B7877F6E1C53A0E652D0D42DEF2CA1F61E81F0C93EA1" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.dll", + "source": "vendor/pwsh/System.Windows.dll", + "bytes": 15656, + "sha256": "8A97C2FC1A57682ED6031CB27A2D0B1BD352EEE9A4AF53C4411F8C0304DEE2FE" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Extensions.dll", + "source": "vendor/pwsh/System.Windows.Extensions.dll", + "bytes": 120616, + "sha256": "EB33D2AF1F7F76F7C179856025981A1F30766D3F7CE9F61D3A09DD22B59553E2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.Design.dll", + "source": "vendor/pwsh/System.Windows.Forms.Design.dll", + "bytes": 6092584, + "sha256": "DE308D2141B4EA269DAB0E30510116646F625A16A6493B13218E7428DBEF08D7" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.Design.Editors.dll", + "source": "vendor/pwsh/System.Windows.Forms.Design.Editors.dll", + "bytes": 16168, + "sha256": "B5B8172A40E5E72A54C6B8C4BB3C926CA4EE6248D5BEBE511B060F0D6D053003" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.dll", + "source": "vendor/pwsh/System.Windows.Forms.dll", + "bytes": 13715240, + "sha256": "65D1A845A47F955CD9E4C893E9FDCAE255F63894F45BC0B721F5DE22F223C753" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Forms.Primitives.dll", + "source": "vendor/pwsh/System.Windows.Forms.Primitives.dll", + "bytes": 3622736, + "sha256": "215C6626C23DC962B32A42B218ABD653D78EA09E3D1162C65B0C3BB836A96D6C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Input.Manipulations.dll", + "source": "vendor/pwsh/System.Windows.Input.Manipulations.dll", + "bytes": 141096, + "sha256": "B51133A16A9EA33269D1AA8A9F9CB7171FFC339D251066903D17209FAAA7360F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Presentation.dll", + "source": "vendor/pwsh/System.Windows.Presentation.dll", + "bytes": 30504, + "sha256": "9C752F71E3C79BEBF747A4BE28FA791404C30EE7486893B4D73CD3EF3DDD4654" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Windows.Primitives.dll", + "source": "vendor/pwsh/System.Windows.Primitives.dll", + "bytes": 386856, + "sha256": "7FD4DC6CF1C20115ADB7EC2519A4A2BE1805E2C2A6133E32BCF52B598020E152" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xaml.dll", + "source": "vendor/pwsh/System.Xaml.dll", + "bytes": 1390416, + "sha256": "DF6837BCC37DF3E0AC022A38F545BCF6155A57A55ADE8E4500D481A5F48A3544" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.dll", + "source": "vendor/pwsh/System.Xml.dll", + "bytes": 23336, + "sha256": "77C887B9C9D608538C78607D752E3567B090DD8BD8D41CAADE2201D34A1BE8BD" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.Linq.dll", + "source": "vendor/pwsh/System.Xml.Linq.dll", + "bytes": 15656, + "sha256": "A3581BC00766E7A78974009B2FAFB83B0D0158C3A1619AF11E9389981DE1DF1A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.ReaderWriter.dll", + "source": "vendor/pwsh/System.Xml.ReaderWriter.dll", + "bytes": 21800, + "sha256": "4E8265FE5D68F42648394F0D6CA0C06C60664F5E0C7456BD65C942C664891BD0" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.Serialization.dll", + "source": "vendor/pwsh/System.Xml.Serialization.dll", + "bytes": 16168, + "sha256": "66AC0372545EDB9C426E8E884292F81B26E6E185E7A90729695497F1FDCE31BB" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XDocument.dll", + "source": "vendor/pwsh/System.Xml.XDocument.dll", + "bytes": 15656, + "sha256": "7E9217B005D31B73E5CD83BC6DA85217677A773020AC736C67D53E6375FF5908" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XmlDocument.dll", + "source": "vendor/pwsh/System.Xml.XmlDocument.dll", + "bytes": 15656, + "sha256": "E604D3C3D891756E18817637E3323C9E67F4DAFB404C877CE3332E598669940C" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XmlSerializer.dll", + "source": "vendor/pwsh/System.Xml.XmlSerializer.dll", + "bytes": 17704, + "sha256": "AC7EFC90B0E73146CFA23A91D97E7BB05E1C3CF02416A7EE5853EB39149AE985" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XPath.dll", + "source": "vendor/pwsh/System.Xml.XPath.dll", + "bytes": 15696, + "sha256": "3E3D6A10185F44C1B78686E08DE8FD59EDF5BA4D953C2FFA4B277B369792DD71" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/System.Xml.XPath.XDocument.dll", + "source": "vendor/pwsh/System.Xml.XPath.XDocument.dll", + "bytes": 30504, + "sha256": "EF4F101B820CD5BA369C6FE06729BDC7E2E2DD023D720058B546DB32227FB500" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/ThirdPartyNotices.txt", + "source": "vendor/pwsh/ThirdPartyNotices.txt", + "bytes": 210587, + "sha256": "5A2D792AA9DAE45DA7579398CA08C0FBB47D78C563CCE492E311B9D5EB9A2142" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationClient.dll", + "source": "vendor/pwsh/UIAutomationClient.dll", + "bytes": 407336, + "sha256": "6074F520F4630DC6DAF4E1CD2F714E4123C4CFCDAD909C76ECC7CF69026C719F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationClientSideProviders.dll", + "source": "vendor/pwsh/UIAutomationClientSideProviders.dll", + "bytes": 857896, + "sha256": "1C287748EB1C5B5BE118FFFD6E9254A67D959A4EA04EF2EE245236CBD6C015C6" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationProvider.dll", + "source": "vendor/pwsh/UIAutomationProvider.dll", + "bytes": 63272, + "sha256": "3A9A965E8759EE679612C3A59BAFE70DFBADA3CE1260D815B1D2D62F941105B2" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/UIAutomationTypes.dll", + "source": "vendor/pwsh/UIAutomationTypes.dll", + "bytes": 296744, + "sha256": "9A26003F55DAC193DAEED86CA35BC01B72DBBEDBFDFF9846E150CEEF80D3D70A" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/vcruntime140_cor3.dll", + "source": "vendor/pwsh/vcruntime140_cor3.dll", + "bytes": 134600, + "sha256": "23AB6BFC1D127318005279A4608401F5C3FB872B5BD32A706863E8D0C2C5065F" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/WindowsBase.dll", + "source": "vendor/pwsh/WindowsBase.dll", + "bytes": 2176808, + "sha256": "ABBEA4B64EFF6A29767E64F99E2574A827B43C383C97145DC9FB316964A8DFB8" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/WindowsFormsIntegration.dll", + "source": "vendor/pwsh/WindowsFormsIntegration.dll", + "bytes": 214864, + "sha256": "0C2C08B731513110D237865AB84E16A22BA05073F6301EA28E894BA343E99415" + }, + { + "scope": "privileged", + "file": "runtime/pwsh/wpfgfx_cor3.dll", + "source": "vendor/pwsh/wpfgfx_cor3.dll", + "bytes": 1956136, + "sha256": "38FFCC5A72DEDFC518D03CCC9B8F5A20D5C8EC84578B42BC30771D79955C2E11" + } + ] +} diff --git a/windows/service/DirectInternetMethod.Service.csproj b/windows/service/DirectInternetMethod.Service.csproj index 67d2b139..111ca5d0 100644 --- a/windows/service/DirectInternetMethod.Service.csproj +++ b/windows/service/DirectInternetMethod.Service.csproj @@ -1,15 +1,15 @@ - - - WinExe - net10.0-windows - enable - enable - DirectInternetMethod.Service - DirectInternetMethod.Service - ..\app\DirectInternetMethod.ico - 1.5.5-rc1 - 1.5.5.0 - 1.5.5.0 - Direct Internet Method Service - + + + WinExe + net10.0-windows + enable + enable + DirectInternetMethod.Service + DirectInternetMethod.Service + ..\app\DirectInternetMethod.ico + 1.5.5-rc1 + 1.5.5.0 + 1.5.5.0 + Direct Internet Method Service + \ No newline at end of file From 037fef325bd9efde3ab624058d8ddbf67e04b916 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:57:50 +0330 Subject: [PATCH 012/188] fix(ci): count pinned .NET10 SDK jobs per private Windows RC1 context (R535) The hosted private Windows acceptance job adds a second SHA-pinned setup-dotnet step; retain the stable exact-count guard and accept exactly two in the RC1 branch, including explicit private job identity checks. No product source/payload change. --- tests/dotnet10_target_contract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/dotnet10_target_contract.py b/tests/dotnet10_target_contract.py index c2b9a95a..8ce47054 100644 --- a/tests/dotnet10_target_contract.py +++ b/tests/dotnet10_target_contract.py @@ -52,7 +52,11 @@ def negative_first(): def main(): negative_first() ci=CI.read_text("utf-8") - assert ci.count("dotnet-version: '10.0.x'")==1 + expected_dotnet_jobs = 2 if CURRENT=="1.5.5-rc1" else 1 + assert ci.count("dotnet-version: '10.0.x'")==expected_dotnet_jobs,"SDK_10_JOB_PIN_COUNT" + if CURRENT=="1.5.5-rc1": + assert ci.count(" windows-private-r535-native:")==1 + assert "runs-on: windows-2025" in ci and "tests/r535_windows_native_rollback.ps1" in ci assert "dotnet-version: '8.0.x'" not in ci assert ci.count("python -B tests/dotnet10_target_contract.py")==1 assert ci.count("dotnet build windows/gui/DirectInternetMethod.csproj")==1 From ad9fb1323520e1968ec6f6bbbe6ce61abc2f37a4 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:07:33 +0330 Subject: [PATCH 013/188] test(ci): require native Windows RC1 acceptance on the main Draft PR branch (R535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After successful hosted Windows v1.5.4→1.5.5-rc1→v1.5.4 in-place install, user-data preservation and rollback on exact tested source commit 037fef3, enable the same GitHub-hosted ephemeral native gate for feature/r520-linux-155rc1-ci. This only changes CI guards and scoped test entrypoint authorization; no runtime or published Stable files altered. --- .github/workflows/ci.yml | 2 +- tests/r535_windows_candidate_hosted_build.ps1 | 2 +- tests/r535_windows_native_rollback.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 254368fb..8bfdd079 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -315,7 +315,7 @@ jobs: windows-private-r535-native: name: windows-private-r535-native - if: github.event_name == 'push' && github.ref == 'refs/heads/feature/r535-windows-155rc1' + if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci') needs: windows-source runs-on: windows-2025 timeout-minutes: 35 diff --git a/tests/r535_windows_candidate_hosted_build.ps1 b/tests/r535_windows_candidate_hosted_build.ps1 index 400655b0..eb6f26a0 100644 --- a/tests/r535_windows_candidate_hosted_build.ps1 +++ b/tests/r535_windows_candidate_hosted_build.ps1 @@ -4,7 +4,7 @@ $ErrorActionPreference='Stop' Set-StrictMode -Version Latest $Root=$env:GITHUB_WORKSPACE if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_GITHUB_HOST_ONLY'} -if($env:GITHUB_REF -cne 'refs/heads/feature/r535-windows-155rc1' -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} $r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json if($r.version -cne '1.5.5-rc1' -or $r.status -cne 'PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R535_RC1_DECLARATION'} $qa=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' diff --git a/tests/r535_windows_native_rollback.ps1 b/tests/r535_windows_native_rollback.ps1 index 8c5a22a7..8ed5c745 100644 --- a/tests/r535_windows_native_rollback.ps1 +++ b/tests/r535_windows_native_rollback.ps1 @@ -20,7 +20,7 @@ $PublicSha='FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133' $PublicGui='BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F' $PublicSvc='2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_HOSTED_VM_ONLY'} -if($env:GITHUB_REF -cne 'refs/heads/feature/r535-windows-155rc1' -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} $admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R535_ADMIN_TOKEN_REQUIRED'} if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R535_UNSAFE_REPLAY_OR_MISSING_BUILD'} From 8e02b927fe6eced64d9c0a755bbd7bf1e47b443a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 17:52:18 +0330 Subject: [PATCH 014/188] fix(linux): reflect explicit DoH consent in GTK state and fail closed (R537) Show valid Control D DoH opt-in in split-Tailscale status; invalid/mutually-exclusive consent prevents Start. Add 12 negative-first GUI status tests, update mocks and exact RC1 package SHA/size pins. Keep user consent explicit and published v1.5.4 unchanged. No physical installations or network changes. --- .github/workflows/ci.yml | 11 +- linux/app/direct_internet_method.py | 20 ++- tests/r519_versioned_package_contract.py | 8 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r537_gui_consent_status_contract.py | 129 ++++++++++++++++++ tests/tailscale_gui_scope_guard_contract.py | 7 +- tests/tailscale_ui_state_contract.py | 3 +- 7 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 tests/r537_gui_consent_status_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bfdd079..18ffe416 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="45A29A65B3E34302AD8E0745324EC2A1C418BB43669F9B95B15963B02CDFCC49" - assert p.is_file() and p.stat().st_size==8334126 + expected="18D8FDF42EC878A936C324F1A8A6EFA39ABE69729B3FA604C2AB785BAA9E92B9" + assert p.is_file() and p.stat().st_size==8335607 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -81,6 +81,9 @@ jobs: python3 -B tests/encrypted_selective_dns_controller_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py + python3 -B tests/r537_gui_consent_status_contract.py + python3 -B tests/tailscale_ui_state_contract.py + python3 -B tests/tailscale_gui_scope_guard_contract.py python3 -B tests/encrypted_selective_dns_integration_contract.py python3 -B tests/tailscale_coexistence_contract.py python3 -B tests/selective_dns_optin_contract.py @@ -137,7 +140,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '45a29a65b3e34302ad8e0745324ec2a1c418bb43669f9b95b15963b02cdfcc49' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '18d8fdf42ec878a936c324f1a8a6efa39abe69729b3fa604c2ab785baa9e92b9' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -166,7 +169,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '45a29a65b3e34302ad8e0745324ec2a1c418bb43669f9b95b15963b02cdfcc49' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '18d8fdf42ec878a936c324f1a8a6efa39abe69729b3fa604c2ab785baa9e92b9' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 22911880..c0213df9 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -395,6 +395,24 @@ def load_state(): if not _coexistence_backend_ready(): return {"mode":"BACKEND_UPDATE_REQUIRED", "detail":"Tailscale split mode is safe, but the installed privileged backend is older or unverified. Start is disabled until the verified backend is installed with rollback; Tailscale and DNS are unchanged."} + # Render the SAME explicit encrypted-DNS consent semantics that the + # protected controller requires. Never advertise "DNS off" when + # DoH is opted in, nor enable Start with an invalid/conflicting + # token that would fail after privileged network actions begin. + encrypted=encrypted_dns_optin_state() + if encrypted=="INVALID": + return {"mode":"CONFIG_REQUIRED", + "detail":"Encrypted DNS consent is invalid, unsafe or conflicts with unencrypted DNS consent. Open Strategy to resolve it before Start; DNS and MagicDNS remain unchanged."} + if encrypted=="ON": + try: + method=STRATEGY_FILE.read_text(encoding="utf-8").strip().lower() + except Exception: + method="invalid" + if method!="balanced": + return {"mode":"CONFIG_REQUIRED", + "detail":"Encrypted targeted DNS requires an explicit Balanced strategy and Targeted scope. Reopen Strategy and Save before Start; no network settings changed."} + return {"mode":"OFF", + "detail":"Tailscale split network active. Encrypted targeted DNS (Control D DoH) is explicitly OPTED IN for selected sites. Start uses Balanced DPI with guarded per-link DoH; physical DNS and MagicDNS stay protected. HTTPS reachability is not guaranteed."} opt=selective_dns_optin_state() if opt=="INVALID": return {"mode":"CONFIG_REQUIRED","detail":"Experimental split-DNS preference is unsafe or unrecognized. Reopen Strategy and explicitly set it."} @@ -407,7 +425,7 @@ def load_state(): return {"mode":"CONFIG_REQUIRED", "detail":"Experimental split DNS requires Targeted + Balanced. Plain, unencrypted third-party UDP DNS is used only for selected sites; disable it or select Balanced in Strategy."} return {"mode":"OFF","detail":"Tailscale split network active. Experimental selective DNS is OPTED IN: third-party UDP upstream is UNENCRYPTED; MagicDNS stays separate. Start runs Balanced DPI and crash-guarded local DNS."} - return {"mode":"OFF","detail":"Tailscale split network active. Start uses physical-interface DPI; system DNS/MagicDNS remain unchanged. Experimental selective DNS is OFF."} + return {"mode":"OFF","detail":"Tailscale split network active. Start uses physical-interface DPI; system DNS/MagicDNS remain unchanged. Optional encrypted and unencrypted selective DNS are both OFF."} return {"mode":"OFF","detail":"No active Direct Internet Method state."} uid=os.getuid() preserved=s.get("dnsMode")=="system-preserved" and bool(s.get("tailscaleCoexistence")) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 13b8444b..e8ef2c4c 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":26,"sha256":"45A29A65B3E34302AD8E0745324EC2A1C418BB43669F9B95B15963B02CDFCC49"} +EXPECTED={"version":VERSION,"files":26,"sha256":"18D8FDF42EC878A936C324F1A8A6EFA39ABE69729B3FA604C2AB785BAA9E92B9"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -82,7 +82,11 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual(set(before),set(after)) self.assertEqual(len(before),26) changed={name for name in before if before[name]!=after[name]} - self.assertEqual(changed,{"app/user_uninstall_preserve.py"}) + self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py"}) + self.assertEqual(after["app/direct_internet_method.py"]["sha256"], + "EB2883C6A8C50869CEA30F1AD0D009689216A175D70F13BE1F4F6F8AA7C02034") + self.assertIn("encrypted=encrypted_dns_optin_state()", + self.info.read(self.prefix+"app/direct_internet_method.py").decode()) previous=before["app/user_uninstall_preserve.py"] self.assertEqual((previous["bytes"],previous["sha256"]), (len(old_source.read_bytes()),digest(old_source.read_bytes()))) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index a3876284..9b114c03 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="45a29a65b3e34302ad8e0745324ec2a1c418bb43669f9b95b15963b02cdfcc49" +CANDIDATE_SHA="18d8fdf42ec878a936c324f1a8a6efa39abe69729b3fa604c2ab785baa9e92b9" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r537_gui_consent_status_contract.py b/tests/r537_gui_consent_status_contract.py new file mode 100644 index 00000000..485d883f --- /dev/null +++ b/tests/r537_gui_consent_status_contract.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""R537 negative-first GUI status/consent contract. No Gtk UI or live network. + +Execute exact AST of the pre-release GUI load_state() against disposable +settings and mocked *read-only* backend status. No actual user consent token is +written, and no subprocess, systemd, DNS, routes, VPN or privileged mutation. +""" +import ast +import json +import os +import pathlib +import tempfile +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +APP=ROOT/"linux/app/direct_internet_method.py" +SOURCE=APP.read_text(encoding="utf8") +TREE=ast.parse(SOURCE) +FN=next(x for x in TREE.body if isinstance(x,ast.FunctionDef) and x.name=="load_state") + +class ExplicitDoHStatus(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="r537_gui_state_",dir=str(ROOT)) + self.addCleanup(self.tmp.cleanup) + base=pathlib.Path(self.tmp.name) + self.scope=base/"scope.txt" + self.strategy=base/"strategy.txt" + self.scope.write_text("targeted\n") + self.strategy.write_text("balanced\n") + self.backend=True + self.old="OFF" + self.encrypted="OFF" + self.split=True + self.foreign=False + self.globals={ + "json":json,"os":os,"pathlib":pathlib, + "STATE":base/"missing-state.json", + "SCOPE_FILE":self.scope,"STRATEGY_FILE":self.strategy, + "_external_tunnel_active":lambda:self.foreign, + "_tailscale_split_safe":lambda:self.split, + "_coexistence_backend_ready":lambda:self.backend, + "selective_dns_optin_state":lambda:self.old, + "encrypted_dns_optin_state":lambda:self.encrypted, + } + exec(compile(ast.Module(body=[FN],type_ignores=[]), + str(APP), "exec"),self.globals) + self.load=self.globals["load_state"] + + def test_01_default_both_disabled(self): + state=self.load() + self.assertEqual(state["mode"],"OFF") + self.assertIn("both OFF",state["detail"]) + + def test_02_authenticated_encrypted_optin_shown_distinctly(self): + self.encrypted="ON" + state=self.load() + self.assertEqual(state["mode"],"OFF") + self.assertIn("Control D DoH",state["detail"]) + self.assertIn("OPTED IN",state["detail"]) + self.assertIn("HTTPS reachability is not guaranteed",state["detail"]) + + def test_03_invalid_encrypted_consent_disables_start(self): + self.encrypted="INVALID" + state=self.load() + self.assertEqual(state["mode"],"CONFIG_REQUIRED") + self.assertIn("invalid",state["detail"]) + + def test_04_conflicting_legacy_consent_invalid_disables_start(self): + self.old="ON" + self.encrypted="INVALID" + state=self.load() + self.assertEqual(state["mode"],"CONFIG_REQUIRED") + self.assertIn("conflicts",state["detail"]) + + def test_05_encrypted_requires_balanced(self): + self.encrypted="ON" + self.strategy.write_text("strong\n") + state=self.load() + self.assertEqual(state["mode"],"CONFIG_REQUIRED") + self.assertIn("Balanced",state["detail"]) + + def test_06_encrypted_requires_explicit_strategy_file(self): + self.encrypted="ON" + self.strategy.unlink() + state=self.load() + self.assertEqual(state["mode"],"CONFIG_REQUIRED") + + def test_07_invalid_all_sites_precedes_encrypted(self): + self.encrypted="ON" + self.scope.write_text("all-sites\n") + state=self.load() + self.assertEqual(state["mode"],"CONFIG_REQUIRED") + self.assertIn("All Sites",state["detail"]) + + def test_08_legacy_unencrypted_consent_distinguished(self): + self.old="ON" + state=self.load() + self.assertEqual(state["mode"],"OFF") + self.assertIn("UNENCRYPTED",state["detail"]) + self.assertNotIn("Control D DoH",state["detail"]) + + def test_09_legacy_invalid_consent_disables_start(self): + self.old="INVALID" + state=self.load() + self.assertEqual(state["mode"],"CONFIG_REQUIRED") + + def test_10_backend_not_updated_refuses_start_even_with_consent(self): + self.encrypted="ON" + self.backend=False + self.assertEqual(self.load()["mode"],"BACKEND_UPDATE_REQUIRED") + + def test_11_foreign_vpn_takes_precedence(self): + self.encrypted="ON" + self.foreign=True + self.assertEqual(self.load()["mode"],"BLOCKED") + + def test_12_no_mutation_syscalls_in_ui_status_function(self): + body=ast.get_source_segment(SOURCE,FN) + for token in ("subprocess.run(", "subprocess.Popen(", "os.system(", + "systemctl start", "resolvectl dns enp7s0", + "pkexec", "tailscale down", ".write_text(", ".unlink("): + self.assertNotIn(token,body) + self.assertIn('encrypted_dns_optin_state()',body) + +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run( + unittest.defaultTestLoader.loadTestsFromTestCase(ExplicitDoHStatus)) + print(f"R537_GUI_DOHSCOPE_STATUS cases={result.testsRun} failures={len(result.failures)} errors={len(result.errors)}") + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/tailscale_gui_scope_guard_contract.py b/tests/tailscale_gui_scope_guard_contract.py index c096c193..f7b77422 100644 --- a/tests/tailscale_gui_scope_guard_contract.py +++ b/tests/tailscale_gui_scope_guard_contract.py @@ -37,6 +37,7 @@ def __truediv__(self,child): return FakePath(self.path.rstrip("/")+"/"+child) "_tailscale_split_safe":lambda:True, "_coexistence_backend_ready":lambda:True, "selective_dns_optin_state":lambda:"OFF", + "encrypted_dns_optin_state":lambda:"OFF", "_systemd_unit_matches":lambda *args:False} exec(code,env) def mode():return env["load_state"]() @@ -102,16 +103,16 @@ def mode():return env["load_state"]() check=h_env["_coexistence_backend_ready"] assert check() is False FakePath.entries[good]="safe-backend" -FakePath.entries[meta]='{"version":"1.5.2","uid":1000}' +FakePath.entries[meta]='{"version":"1.5.5-rc1","uid":1000}' assert check() is True, "pinned helper and metadata must be recognized" FakePath.entries[good]="different-backend" assert check() is False, "foreign helper must be refused" FakePath.entries[good]="safe-backend" FakePath.entries[meta]='{"version":"1.5.1","uid":1000}' assert check() is False, "old metadata must be refused" -FakePath.entries[meta]='{"version":"1.5.2","uid":1001}' +FakePath.entries[meta]='{"version":"1.5.5-rc1","uid":1001}' assert check() is False, "different user metadata must be refused" -FakePath.entries[meta]='{"version":"1.5.2","uid":1000}' +FakePath.entries[meta]='{"version":"1.5.5-rc1","uid":1000}' FakePath.symlinks.add(good) assert check() is False, "helper symlink must be refused" FakePath.symlinks.clear() diff --git a/tests/tailscale_ui_state_contract.py b/tests/tailscale_ui_state_contract.py index 736aa1b7..10857b7c 100644 --- a/tests/tailscale_ui_state_contract.py +++ b/tests/tailscale_ui_state_contract.py @@ -31,7 +31,8 @@ def read_text(self,encoding="utf-8"): "_external_tunnel_active":lambda:False, "_tailscale_split_safe":lambda:True, "_coexistence_backend_ready":lambda:True, - "selective_dns_optin_state":lambda:"OFF"} + "selective_dns_optin_state":lambda:"OFF", + "encrypted_dns_optin_state":lambda:"OFF"} exec(code,env) state=env["load_state"]() assert state["mode"]=="OFF" and "Tailscale split" in state["detail"],state From d70a9d99434822eedbba16446bfe646daeb8b68f Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 18:26:06 +0330 Subject: [PATCH 015/188] fix(linux): correctly attest active encrypted targeted DoH and HTTPS probe (R538) Use actual selectedDnsEncrypted field for active Tailscale/DoH status instead of legacy UDP flag. Fail closed on conflicting active DNS modes, test real YouTube homepage for either selected DNS mode, and disclose encrypted-selective-doh in live diagnostics. 12 negative-first regressions now pass; update the byte-pinned Linux RC1 archive and hosted CI/test hashes. No physical DNS, installed product, Tailscale or public v1.5.4 changes. --- .github/workflows/ci.yml | 9 +- linux/app/direct_internet_method.py | 23 ++- tests/r519_versioned_package_contract.py | 6 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r538_active_encrypted_status_contract.py | 164 ++++++++++++++++++ 5 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 tests/r538_active_encrypted_status_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18ffe416..44c95480 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="18D8FDF42EC878A936C324F1A8A6EFA39ABE69729B3FA604C2AB785BAA9E92B9" - assert p.is_file() and p.stat().st_size==8335607 + expected="66B623D41DD7DC6C5BE141143E863D970EE2230C5D61F1D47142A16AD53DA296" + assert p.is_file() and p.stat().st_size==8336935 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -82,6 +82,7 @@ jobs: python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py + python3 -B tests/r538_active_encrypted_status_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py python3 -B tests/encrypted_selective_dns_integration_contract.py @@ -140,7 +141,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '18d8fdf42ec878a936c324f1a8a6efa39abe69729b3fa604c2ab785baa9e92b9' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '66b623d41dd7dc6c5be141143e863d970ee2230c5d61f1d47142a16ad53da296' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -169,7 +170,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '18d8fdf42ec878a936c324f1a8a6efa39abe69729b3fa604c2ab785baa9e92b9' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '66b623d41dd7dc6c5be141143e863d970ee2230c5d61f1d47142a16ad53da296' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index c0213df9..d9d9d0ac 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -435,7 +435,17 @@ def load_state(): nfqws_ok=_systemd_unit_matches(s.get("nfqwsUnit"),s.get("nfqwsPid"),f"directinternetmethod-nfqws-{uid}.service") if ctrld_ok and nfqws_ok: if preserved: - if s.get("selectiveDns"): + # Authoritative ACTIVE state is written by the protected helper, + # not inferred from (possibly since-revoked) user preference files. + if s.get("selectiveDnsEncrypted") and s.get("selectiveDns"): + return {"mode":"CONFLICT", + "detail":"Both mutually exclusive selective DNS modes are marked active. Stop and use Recovery before any new Start; physical DNS and MagicDNS must be preserved."} + if s.get("selectiveDnsEncrypted"): + detail=(f"Tailscale coexistence: {s.get('physicalInterface','?')} " + "Encrypted targeted DoH ACTIVE for selected sites (Control D HTTPS upstream); " + "physical DNS and MagicDNS preserved; Balanced DPI running. " + "HTTPS target reachability requires real probe verification.") + elif s.get("selectiveDns"): detail=f"Tailscale coexistence: {s.get('physicalInterface','?')} Local experimental selective DNS ACTIVE; third-party upstream UDP is UNENCRYPTED, MagicDNS remains separate; Balanced DPI running" else: detail=f"Tailscale coexistence: {s.get('physicalInterface','?')} DNS/MagicDNS preserved Methods: HTTP + TLS/SNI + QUIC (DPI only)" @@ -464,6 +474,8 @@ def verify_live(): return {"ok":False,"error":"STATE_UNREADABLE_AFTER_START"} if not physical: return {"ok":False,"error":"PHYSICAL_INTERFACE_MISSING"} + if s.get("selectiveDns") and s.get("selectiveDnsEncrypted"): + return {"ok":False,"error":"CONFLICTING_SELECTIVE_DNS_STATE"} if _external_tunnel_active(): return {"ok":False,"error":"EXTERNAL_TUNNEL_APPEARED_AFTER_START"} def curl(url): @@ -474,7 +486,10 @@ def curl(url): return {"exit":p.returncode,"meta":p.stdout.strip(),"error":p.stderr.strip()} d=subprocess.run(["getent","ahostsv4","www.youtube.com"],text=True,capture_output=True) h=curl("http://www.youtube.com/") - y=curl("https://www.youtube.com/" if s.get("selectiveDns") else "https://www.youtube.com/generate_204") + # Both selective modes must test a real YouTube page, not a + # Google-style /generate_204 URL that can falsely fail valid HTTPS. + selected_dns=bool(s.get("selectiveDns") or s.get("selectiveDnsEncrypted")) + y=curl("https://www.youtube.com/" if selected_dns else "https://www.youtube.com/generate_204") o=curl("https://api.openai.com/v1/models") g=curl("https://github.com/") m=curl("https://gemini.google.com/") @@ -484,7 +499,9 @@ def curl(url): and o["exit"]==0 and o["meta"].startswith(("401|","403|")) and g["exit"]==0 and g["meta"].startswith(("200|"))) dns_preserved=s.get("dnsMode")=="system-preserved" - methods=([] if dns_preserved else ["encrypted-dns-doh"])+["http-host-split-tcp80","tls-sni-desync-tcp443","quic-desync-udp443"] + methods=(["encrypted-selective-doh"] if dns_preserved and s.get("selectiveDnsEncrypted") + else [] if dns_preserved else ["encrypted-dns-doh"])+[ + "http-host-split-tcp80","tls-sni-desync-tcp443","quic-desync-udp443"] return {"ok":ok,"physicalInterface":physical,"dnsMode":s.get("dnsMode","direct-doh"),"directMethods":methods,"youtubeHttp80":h,"youtube":y,"openai":o,"github":g,"gemini":m} def _version_tuple(text): diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index e8ef2c4c..15a5015f 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":26,"sha256":"18D8FDF42EC878A936C324F1A8A6EFA39ABE69729B3FA604C2AB785BAA9E92B9"} +EXPECTED={"version":VERSION,"files":26,"sha256":"66B623D41DD7DC6C5BE141143E863D970EE2230C5D61F1D47142A16AD53DA296"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -84,9 +84,11 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self changed={name for name in before if before[name]!=after[name]} self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "EB2883C6A8C50869CEA30F1AD0D009689216A175D70F13BE1F4F6F8AA7C02034") + "652E1D332BF4A3F1F6659E991CFDFFDC3CD85B32B4632FEB255A3533188FF4FB") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) + self.assertIn('s.get("selectiveDnsEncrypted")', + self.info.read(self.prefix+"app/direct_internet_method.py").decode()) previous=before["app/user_uninstall_preserve.py"] self.assertEqual((previous["bytes"],previous["sha256"]), (len(old_source.read_bytes()),digest(old_source.read_bytes()))) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 9b114c03..aa5824cb 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="18d8fdf42ec878a936c324f1a8a6efa39abe69729b3fa604c2ab785baa9e92b9" +CANDIDATE_SHA="66b623d41dd7dc6c5be141143e863d970ee2230c5d61f1d47142a16ad53da296" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r538_active_encrypted_status_contract.py b/tests/r538_active_encrypted_status_contract.py new file mode 100644 index 00000000..c789f6c7 --- /dev/null +++ b/tests/r538_active_encrypted_status_contract.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +"""R538 negative-first: real RC1 active-state DoH and HTTPS verifier semantics. + +Execute just the actual production GUI load_state and verify_live functions +from pinned source AST with synthetic state and deliberately mocked command +outputs. No physical network, installed settings, privileges or GTK mutation. +""" +import ast +import json +import os +import pathlib +import subprocess +import tempfile +import types +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +APP=ROOT/"linux/app/direct_internet_method.py" +SOURCE=APP.read_text(encoding="utf-8") +TREE=ast.parse(SOURCE) +METHODS=[n for n in TREE.body if isinstance(n,ast.FunctionDef) + and n.name in ("load_state","verify_live")] +assert len(METHODS)==2, "R538_EXPECTED_TWO_PRODUCTION_FUNCTIONS" + +class ActiveEncryptedDoH(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="r538_active_doh_",dir=ROOT) + self.addCleanup(self.tmp.cleanup) + self.root=pathlib.Path(self.tmp.name) + self.statefile=self.root/"active.json" + self.calls=[] + self.remote_youtube_ok=True + self.backend_unit_active=True + self.external_tunnel=False + self.tailscale_safe=True + self.data={ + "schema":3,"status":"ACTIVE","uid":os.getuid(), + "dnsMode":"system-preserved","tailscaleCoexistence":True, + "selectiveDnsEncrypted":True,"selectiveDns":False, + "physicalInterface":"enp7s0","dnsInterface":"","dnsIp":"", + "ctrldPid":0,"ctrldUnit":None, + "nfqwsUnit":f"directinternetmethod-nfqws-{os.getuid()}.service", + "nfqwsPid":37531,"strategy":"balanced","scope":"targeted"} + self.store() + self.env={ + "json":json,"os":os,"pathlib":pathlib, + "STATE":self.statefile, + "subprocess":types.SimpleNamespace(run=self.fake_run), + "_external_tunnel_active":lambda:self.external_tunnel, + "_tailscale_split_safe":lambda:self.tailscale_safe, + "_systemd_unit_matches":lambda *a:self.backend_unit_active, + } + exec(compile(ast.Module(body=METHODS,type_ignores=[]),str(APP),"exec"),self.env) + self.show=self.env["load_state"] + self.verify=self.env["verify_live"] + + def store(self): + self.statefile.write_text(json.dumps(self.data)+"\n",encoding="utf8") + + def fake_run(self,argv,**kwargs): + program=argv[0] + self.assertIn(program,("getent","curl"),"R538_ATTEMPTED_REAL_COMMAND") + if program=="getent": + return types.SimpleNamespace(returncode=0,stdout="142.251.151.4 STREAM www.youtube.com\n",stderr="") + url=argv[-1] + self.calls.append(url) + mapping={ + "http://www.youtube.com/":"301|142.251.151.4|0.10", + "https://www.youtube.com/":"200|142.251.151.4|0.31", + # Deliberately incorrect endpoint for YouTube: using it would fail + # a healthy connection and false-negative the acceptance. + "https://www.youtube.com/generate_204":"404|142.251.151.4|0.25", + "https://api.openai.com/v1/models":"401|104.18.0.1|0.25", + "https://github.com/":"200|140.82.112.3|0.23", + "https://gemini.google.com/":"200|142.250.191.14|0.20" + } + self.assertIn(url,mapping,"R538_UNRECOGNIZED_PROBE") + if not self.remote_youtube_ok and url=="https://www.youtube.com/": + return types.SimpleNamespace(returncode=35,stdout="000||1.30",stderr="TLS reset") + return types.SimpleNamespace(returncode=0,stdout=mapping[url],stderr="") + + def test_01_active_encrypted_selected_shows_actual_doh(self): + v=self.show() + self.assertEqual(v["mode"],"ACTIVE") + self.assertIn("Encrypted targeted DoH ACTIVE",v["detail"]) + self.assertIn("MagicDNS",v["detail"]) + + def test_02_active_legacy_udp_remains_explicitly_unencrypted(self): + self.data["selectiveDnsEncrypted"]=False + self.data["selectiveDns"]=True + self.store() + v=self.show() + self.assertEqual(v["mode"],"ACTIVE") + self.assertIn("UNENCRYPTED",v["detail"]) + self.assertNotIn("Encrypted targeted DoH ACTIVE",v["detail"]) + + def test_03_active_without_selected_dns_shows_preserved_dns(self): + self.data["selectiveDnsEncrypted"]=False + self.store() + v=self.show() + self.assertEqual(v["mode"],"ACTIVE") + self.assertIn("DNS/MagicDNS preserved",v["detail"]) + self.assertNotIn("DoH ACTIVE",v["detail"]) + + def test_04_conflicting_active_state_fails_closed(self): + self.data["selectiveDns"]=True + self.store() + self.assertEqual(self.show()["mode"],"CONFLICT") + + def test_05_inactive_nfqws_stays_stale(self): + self.backend_unit_active=False + self.assertEqual(self.show()["mode"],"STALE") + + def test_06_unsupported_vpn_conflicts_even_with_valid_doh(self): + self.external_tunnel=True + self.assertEqual(self.show()["mode"],"CONFLICT") + + def test_07_selected_doh_live_probe_targets_real_homepage(self): + result=self.verify() + self.assertTrue(result["ok"]) + self.assertIn("https://www.youtube.com/",self.calls) + self.assertNotIn("https://www.youtube.com/generate_204",self.calls) + + def test_08_selected_doh_live_methods_include_targeted_doh(self): + result=self.verify() + self.assertIn("encrypted-selective-doh",result["directMethods"]) + self.assertNotIn("unencrypted-selective-udp",result["directMethods"]) + + def test_09_real_tls_failure_never_counted_as_success(self): + self.remote_youtube_ok=False + v=self.verify() + self.assertFalse(v["ok"]) + self.assertEqual(v["youtube"]["exit"],35) + + def test_10_legacy_udp_never_claims_encrypted_methods(self): + self.data["selectiveDnsEncrypted"]=False + self.data["selectiveDns"]=True + self.store() + v=self.verify() + self.assertIn("https://www.youtube.com/",self.calls) + self.assertNotIn("encrypted-selective-doh",v["directMethods"]) + + def test_11_mutually_selected_dns_modes_rejected_by_verifier(self): + self.data["selectiveDns"]=True + self.store() + v=self.verify() + self.assertFalse(v["ok"]) + self.assertEqual(v["error"],"CONFLICTING_SELECTIVE_DNS_STATE") + + def test_12_active_production_status_contains_no_network_mutations(self): + for n in METHODS: + source=ast.get_source_segment(SOURCE,n) + for k in ("resolvectl dns enp7s0","tailscale down", + "systemctl start","ip route add","iptables", + "nft add","pkexec","os.system("): + self.assertNotIn(k,source) + self.assertIn('s.get("selectiveDnsEncrypted")',SOURCE) + +if __name__=="__main__": + res=unittest.TextTestRunner(verbosity=2).run( + unittest.defaultTestLoader.loadTestsFromTestCase(ActiveEncryptedDoH)) + print("R538_ACTIVE_ENCRYPTED_DOH_AND_VERIFIER tests=",res.testsRun, + "failures=",len(res.failures),"errors=",len(res.errors)) + raise SystemExit(0 if res.wasSuccessful() else 1) From 280f27a8eda723ece4fe947635ccb3ca5c9f01ae Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:06:13 +0330 Subject: [PATCH 016/188] fix(linux): reject active NFQUEUE egress-interface drift without changing routes (R541) Physical read-only evidence on Emad Linux found NFQWS bound to wlp2s0 while default Internet egress is enp1s0. The nft OUTPUT oifname rules cannot intercept the current path. Show STALE in GUI, fail closed in HTTPS preflight, refuse false ALREADY_ACTIVE before any destructive cleanup, and direct an explicit Stop/Recovery then Start after route stabilization. Nine negative-first tests passed and physical source-extracted reproduction matched. Refresh dependent protected-root hashes and exact private RC1 package/hosted CI pins, preserving public Stable and user network. --- .github/workflows/ci.yml | 9 +- linux/app/direct_internet_method.py | 18 ++- linux/app/direct_method_helper.sh | 15 ++ linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 12 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- .../r538_active_encrypted_status_contract.py | 1 + tests/r541_route_interface_drift_contract.py | 130 ++++++++++++++++++ tests/tailscale_gui_scope_guard_contract.py | 1 + tests/tailscale_ui_state_contract.py | 1 + 13 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 tests/r541_route_interface_drift_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44c95480..167ba831 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="66B623D41DD7DC6C5BE141143E863D970EE2230C5D61F1D47142A16AD53DA296" - assert p.is_file() and p.stat().st_size==8336935 + expected="0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839" + assert p.is_file() and p.stat().st_size==8338876 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -83,6 +83,7 @@ jobs: python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py python3 -B tests/r538_active_encrypted_status_contract.py + python3 -B tests/r541_route_interface_drift_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py python3 -B tests/encrypted_selective_dns_integration_contract.py @@ -141,7 +142,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '66b623d41dd7dc6c5be141143e863d970ee2230c5d61f1d47142a16ad53da296' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0c5ba7f8da540412e8417152d0e6ff75f1ffe4adab06ee0a1fe72223a94a4839' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -170,7 +171,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '66b623d41dd7dc6c5be141143e863d970ee2230c5d61f1d47142a16ad53da296' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0c5ba7f8da540412e8417152d0e6ff75f1ffe4adab06ee0a1fe72223a94a4839' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index d9d9d0ac..ed878edd 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -48,7 +48,7 @@ SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="b6641270d1ed8687a2fb1331016f212533bfd4e490331213ff24684d2999a397" +COEXIST_HELPER_SHA256="99279af19445b4b793d903ee0e114930869039ce0ac1fb6ff4b412ba79665466" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): @@ -434,6 +434,18 @@ def load_state(): _systemd_unit_matches(s.get("ctrldUnit"),s.get("ctrldPid"),f"directinternetmethod-ctrld-{uid}.service")) nfqws_ok=_systemd_unit_matches(s.get("nfqwsUnit"),s.get("nfqwsPid"),f"directinternetmethod-nfqws-{uid}.service") if ctrld_ok and nfqws_ok: + # NFQUEUE nft output rules are bound to the physical interface + # recorded at Start. A changed/default route silently bypasses all + # filtering even while the nfqws PID and systemd unit remain ACTIVE. + # Read current routing only; never switch a user's network here. + recorded_iface=str(s.get("physicalInterface") or "") + current_iface=_top_default_iface() + if not recorded_iface or not current_iface or current_iface!=recorded_iface: + return {"mode":"STALE", + "detail":f"Network path changed: owned NFQUEUE rules use {recorded_iface or 'missing'}, " + f"but the current default route uses {current_iface or 'missing'}. " + "Stop or Recovery, then Start after the route stabilizes; " + "Tailscale and DNS were not altered."} if preserved: # Authoritative ACTIVE state is written by the protected helper, # not inferred from (possibly since-revoked) user preference files. @@ -474,6 +486,10 @@ def verify_live(): return {"ok":False,"error":"STATE_UNREADABLE_AFTER_START"} if not physical: return {"ok":False,"error":"PHYSICAL_INTERFACE_MISSING"} + current_iface=_top_default_iface() + if not current_iface or current_iface!=physical: + return {"ok":False,"error":"PHYSICAL_INTERFACE_ROUTE_DRIFT", + "recordedInterface":physical,"currentInterface":current_iface} if s.get("selectiveDns") and s.get("selectiveDnsEncrypted"): return {"ok":False,"error":"CONFLICTING_SELECTIVE_DNS_STATE"} if _external_tunnel_active(): diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index e783e43d..037e7d63 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -582,6 +582,21 @@ case "$ACTION" in trap rollback_on_exit EXIT if [ -f "$STATE" ]; then + # Do not claim ALREADY_ACTIVE when networking changed under a live + # owned nfqueue session. The nft OUTPUT rules match *only* the PHY + # saved at Start; a changed physical default route bypasses the engine. + # This is read-only, leaves the existing owner's resources in place, + # and requires an explicit Stop/Recovery and new Start to rebind. + PREVIOUS_PHY="$(state_value physicalInterface)" + CURRENT_PHY="$(physical_iface)" + if [ -z "$PREVIOUS_PHY" ] || [ -z "$CURRENT_PHY" ] || + [ "$PREVIOUS_PHY" != "$CURRENT_PHY" ] || + [ "$(top_iface)" != "$CURRENT_PHY" ]; then + START_COMMITTED=1 + trap - EXIT + echo '{"ok":false,"error":"PHYSICAL_INTERFACE_ROUTE_DRIFT","action":"STOP_THEN_START_AFTER_ROUTE_STABILIZES"}' + exit 79 + fi CPID="$(state_value ctrldPid)" NPID="$(state_value nfqwsPid)" PREVIOUS_DNS_MODE="$(state_value dnsMode)" diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index dc3c82b4..1389ff07 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -23,7 +23,7 @@ FILES=( ("helper","app/direct_method_helper.sh","usr/lib/directinternetmethod/direct_method_helper.sh", "E52FCAEB9C60D9E43D4A5B75462455AD8C5EE4D82FEE679A0CC4A3520D8ABEA5", - "B6641270D1ED8687A2FB1331016F212533BFD4E490331213FF24684D2999A397",0o755), + "99279AF19445B4B793D903EE0E114930869039CE0AC1FB6FF4B412BA79665466",0o755), ("uninstall","system/uninstall_system.sh","usr/lib/directinternetmethod/uninstall_system.sh", "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 323523ad..699eed0d 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="4CE00525AF4773FE25CB7E3E5EDFD661B6A30C6B677964E12F24BCA6650F328D" +TX_SHA="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 15a5015f..6441ad8e 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":26,"sha256":"66B623D41DD7DC6C5BE141143E863D970EE2230C5D61F1D47142A16AD53DA296"} +EXPECTED={"version":VERSION,"files":26,"sha256":"0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -82,13 +82,19 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual(set(before),set(after)) self.assertEqual(len(before),26) changed={name for name in before if before[name]!=after[name]} - self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py"}) + self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", + "app/direct_method_helper.sh","system/protected_upgrade_transaction.py", + "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "652E1D332BF4A3F1F6659E991CFDFFDC3CD85B32B4632FEB255A3533188FF4FB") + "83A50CB575F6398A75904A5E422ADF0A416447BFC0C2899C00BF2FC57B31C875") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', self.info.read(self.prefix+"app/direct_internet_method.py").decode()) + self.assertIn('PHYSICAL_INTERFACE_ROUTE_DRIFT', + self.info.read(self.prefix+"app/direct_method_helper.sh").decode()) + self.assertIn('PHYSICAL_INTERFACE_ROUTE_DRIFT', + self.info.read(self.prefix+"app/direct_internet_method.py").decode()) previous=before["app/user_uninstall_preserve.py"] self.assertEqual((previous["bytes"],previous["sha256"]), (len(old_source.read_bytes()),digest(old_source.read_bytes()))) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index aa5824cb..0ee8811d 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="66b623d41dd7dc6c5be141143e863d970ee2230c5d61f1d47142a16ad53da296" +CANDIDATE_SHA="0c5ba7f8da540412e8417152d0e6ff75f1ffe4adab06ee0a1fe72223a94a4839" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 4a2a0962..228696e8 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "4ce00525af4773fe25cb7e3e5edfd661b6a30c6b677964e12f24bca6650f328d" +ROOT_SHA = "eb107164ccede891c8bf41c06ff422f1b53b59d2d2ddf0b3dd23ebfe5de9601b" USER_SHA = "a909bbb12b78875a9438c756ae2dcaff05e0df2ebb5551e97544d2b20c46346f" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 2fcdf01a..1db4d031 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "4ce00525af4773fe25cb7e3e5edfd661b6a30c6b677964e12f24bca6650f328d" +PIN_ROOT = "eb107164ccede891c8bf41c06ff422f1b53b59d2d2ddf0b3dd23ebfe5de9601b" PIN_USER = "a909bbb12b78875a9438c756ae2dcaff05e0df2ebb5551e97544d2b20c46346f" def digest(path:Path): diff --git a/tests/r538_active_encrypted_status_contract.py b/tests/r538_active_encrypted_status_contract.py index c789f6c7..58c157c1 100644 --- a/tests/r538_active_encrypted_status_contract.py +++ b/tests/r538_active_encrypted_status_contract.py @@ -48,6 +48,7 @@ def setUp(self): "subprocess":types.SimpleNamespace(run=self.fake_run), "_external_tunnel_active":lambda:self.external_tunnel, "_tailscale_split_safe":lambda:self.tailscale_safe, + "_top_default_iface":lambda:self.data["physicalInterface"], "_systemd_unit_matches":lambda *a:self.backend_unit_active, } exec(compile(ast.Module(body=METHODS,type_ignores=[]),str(APP),"exec"),self.env) diff --git a/tests/r541_route_interface_drift_contract.py b/tests/r541_route_interface_drift_contract.py new file mode 100644 index 00000000..06beb48a --- /dev/null +++ b/tests/r541_route_interface_drift_contract.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +"""R541 regression for route/interface drift after real network changes. + +Derived from the physical Emad Linux read-only finding: old owned nft output +rules match recorded Wi-Fi wlp2s0, while live Internet egress uses enp1s0. +No installed state, network, DNS, Tailscale, nfqueue, privileged actions or +user settings may be changed by these tests. +""" +import ast +import json +import os +from pathlib import Path +import tempfile +import types +import unittest + +ROOT=Path(__file__).resolve().parents[1] +GUI=ROOT/"linux/app/direct_internet_method.py" +HELPER=ROOT/"linux/app/direct_method_helper.sh" +SOURCE=GUI.read_text(encoding="utf-8") +TREE=ast.parse(SOURCE) +FUNCS=[x for x in TREE.body if isinstance(x,ast.FunctionDef) + and x.name in ("load_state","verify_live")] +assert len(FUNCS)==2 + +class RouteDrift(unittest.TestCase): + def setUp(self): + self.temporary=tempfile.TemporaryDirectory(prefix="r541_route_test_",dir=ROOT) + self.addCleanup(self.temporary.cleanup) + self.state=Path(self.temporary.name)/"state.json" + self.egress="enp1s0" + self.interface="wlp2s0" + self.backend_running=True + self.calls=[] + self.row={ + "status":"ACTIVE","schema":3,"uid":os.getuid(), + "physicalInterface":self.interface, + "dnsMode":"system-preserved","tailscaleCoexistence":True, + "selectiveDns":False,"selectiveDnsEncrypted":False, + "ctrldPid":0,"ctrldUnit":None, + "nfqwsPid":5005,"nfqwsUnit":f"directinternetmethod-nfqws-{os.getuid()}.service" + } + self.write() + self.env={ + "json":json,"os":os,"pathlib":PathNamespace, + "STATE":self.state, + "_external_tunnel_active":lambda:False, + "_tailscale_split_safe":lambda:True, + "_top_default_iface":lambda:self.egress, + "_systemd_unit_matches":lambda *args:self.backend_running, + "subprocess":types.SimpleNamespace(run=self.fake_process), + } + exec(compile(ast.Module(body=FUNCS,type_ignores=[]),str(GUI),"exec"),self.env) + self.show=self.env["load_state"] + self.verify=self.env["verify_live"] + + def write(self): + self.row["physicalInterface"]=self.interface + self.state.write_text(json.dumps(self.row)+"\n",encoding="utf-8") + + def fake_process(self,*args,**kwargs): + self.calls.append(args[0]) + raise AssertionError("R541_ROUTE_DRIFT_MUST_ABORT_BEFORE_NETWORK_PROBES") + + def test_01_stale_wifi_rule_vs_ethernet_default(self): + s=self.show() + self.assertEqual(s["mode"],"STALE") + self.assertIn("wlp2s0",s["detail"]) + self.assertIn("enp1s0",s["detail"]) + self.assertIn("Stop",s["detail"]) + + def test_02_default_route_missing_does_not_trust_running_pid(self): + self.egress="" + self.assertEqual(self.show()["mode"],"STALE") + + def test_03_route_returns_to_installed_interface(self): + self.egress="wlp2s0" + self.assertEqual(self.show()["mode"],"ACTIVE") + + def test_04_https_probe_preflights_route_without_curl(self): + v=self.verify() + self.assertEqual(v["ok"],False) + self.assertEqual(v["error"],"PHYSICAL_INTERFACE_ROUTE_DRIFT") + self.assertEqual(v["recordedInterface"],"wlp2s0") + self.assertEqual(v["currentInterface"],"enp1s0") + self.assertEqual(self.calls,[]) + + def test_05_https_probe_no_default_route_fails_closed(self): + self.egress="" + v=self.verify() + self.assertEqual(v["error"],"PHYSICAL_INTERFACE_ROUTE_DRIFT") + self.assertEqual(self.calls,[]) + + def test_06_original_missing_nfqws_pid_still_stale(self): + self.backend_running=False + self.assertEqual(self.show()["mode"],"STALE") + + def test_07_tailscale_state_does_not_mask_route_drift(self): + self.row["selectiveDnsEncrypted"]=True + self.write() + s=self.show() + self.assertEqual(s["mode"],"STALE") + self.assertNotIn("DoH ACTIVE",s["detail"]) + + def test_08_shell_backend_refuses_false_already_active_without_mutating(self): + shell=HELPER.read_text(encoding="utf-8") + start=shell.split("\n start)\n",1)[1].split("\n if nft_table_presence;",1)[0] + self.assertIn('CURRENT_PHY="$(physical_iface)"',start) + self.assertIn('PREVIOUS_PHY="$(state_value physicalInterface)"',start) + self.assertIn('PHYSICAL_INTERFACE_ROUTE_DRIFT',start) + self.assertIn('trap - EXIT',start) + self.assertLess(start.index('PHYSICAL_INTERFACE_ROUTE_DRIFT'), + start.index('nft_table_owned "$(state_value physicalInterface)"')) + self.assertIn('exit 79',start) + self.assertIn('nft add rule inet "$TABLE" output oifname "$PHY"',shell) + + def test_09_shell_guard_is_before_any_cleanup(self): + shell=HELPER.read_text(encoding="utf-8") + start=shell.split("\n start)\n",1)[1] + self.assertLess(start.index('PHYSICAL_INTERFACE_ROUTE_DRIFT'),start.index('cleanup_state_owned')) + +class PathNamespace: + Path=Path + +if __name__=="__main__": + res=unittest.TextTestRunner(verbosity=2).run( + unittest.defaultTestLoader.loadTestsFromTestCase(RouteDrift)) + print("R541_ROUTE_DRIFT tests=",res.testsRun, + "failures=",len(res.failures),"errors=",len(res.errors)) + raise SystemExit(0 if res.wasSuccessful() else 1) diff --git a/tests/tailscale_gui_scope_guard_contract.py b/tests/tailscale_gui_scope_guard_contract.py index f7b77422..2cd40a44 100644 --- a/tests/tailscale_gui_scope_guard_contract.py +++ b/tests/tailscale_gui_scope_guard_contract.py @@ -35,6 +35,7 @@ def __truediv__(self,child): return FakePath(self.path.rstrip("/")+"/"+child) "os":SimpleNamespace(getuid=lambda:1000), "_external_tunnel_active":lambda:False, "_tailscale_split_safe":lambda:True, + "_top_default_iface":lambda:"enp1s0", "_coexistence_backend_ready":lambda:True, "selective_dns_optin_state":lambda:"OFF", "encrypted_dns_optin_state":lambda:"OFF", diff --git a/tests/tailscale_ui_state_contract.py b/tests/tailscale_ui_state_contract.py index 10857b7c..af0ebf96 100644 --- a/tests/tailscale_ui_state_contract.py +++ b/tests/tailscale_ui_state_contract.py @@ -30,6 +30,7 @@ def read_text(self,encoding="utf-8"): "_systemd_unit_matches":lambda u,p,w:bool(p) and int(p)==400 and u==w, "_external_tunnel_active":lambda:False, "_tailscale_split_safe":lambda:True, + "_top_default_iface":lambda:"enp1s0", "_coexistence_backend_ready":lambda:True, "selective_dns_optin_state":lambda:"OFF", "encrypted_dns_optin_state":lambda:"OFF"} From 85c3ab43da3b2388a2b91c6da2197c1492e4977d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 21:44:41 +0330 Subject: [PATCH 017/188] test(linux): execute R541 route-drift Start preflight in isolated Bash stubs --- tests/r541_route_interface_drift_contract.py | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/r541_route_interface_drift_contract.py b/tests/r541_route_interface_drift_contract.py index 06beb48a..0e743181 100644 --- a/tests/r541_route_interface_drift_contract.py +++ b/tests/r541_route_interface_drift_contract.py @@ -9,6 +9,8 @@ import ast import json import os +import shlex +import subprocess from pathlib import Path import tempfile import types @@ -119,6 +121,60 @@ def test_09_shell_guard_is_before_any_cleanup(self): start=shell.split("\n start)\n",1)[1] self.assertLess(start.index('PHYSICAL_INTERFACE_ROUTE_DRIFT'),start.index('cleanup_state_owned')) + + def _real_shell_route_preflight(self, previous, current, top): + """Run the exact installed Start preflight under unprivileged Bash stubs. + + This stops before CPID/readiness/cleanup, so no root, service, route, + DNS, Tailscale, nft or network action is possible. Only the protected + drift guard from the real helper is executed, not a rewritten model. + """ + shell=HELPER.read_text(encoding="utf-8") + start=shell.split("\n start)\n",1)[1].split("\n CPID=",1)[0] + self.assertIn("PHYSICAL_INTERFACE_ROUTE_DRIFT",start) + script=( + "set -euo pipefail\n" + f"STATE={shlex.quote(str(self.state))}\n" + f"REQ_PREVIOUS={shlex.quote(previous)}\n" + f"REQ_CURRENT={shlex.quote(current)}\n" + f"REQ_TOP={shlex.quote(top)}\n" + "require_root() { :; }\n" + "acquire_action_lock() { :; }\n" + "rollback_on_exit() { printf 'ROLLBACK_CALLED\\n'; }\n" + "state_value() { [[ \"$1\" == physicalInterface ]] && " + "printf '%s\\n' \"$REQ_PREVIOUS\"; }\n" + "physical_iface() { printf '%s\\n' \"$REQ_CURRENT\"; }\n" + "top_iface() { printf '%s\\n' \"$REQ_TOP\"; }\n" + "cleanup_state_owned() { printf 'CLEANUP_CALLED\\n'; }\n" + "ACTION=start\nSTART_COMMITTED=0\n" + +start+ + "\n fi\n" + "trap - EXIT\nprintf 'PRECHECK_MATCHED\\n'\n" + ) + return subprocess.run(["bash","-c",script],text=True,capture_output=True, + timeout=5,check=False) + + def test_10_executable_start_drift_refuses_without_cleanup(self): + for name,old,new,top in ( + ("wifi_to_ethernet","wlp2s0","enp1s0","enp1s0"), + ("default_route_lost","wlp2s0","",""), + ("top_route_conflict","wlp2s0","wlp2s0","tailscale0"), + ("recorded_interface_missing","","enp1s0","enp1s0"), + ): + with self.subTest(name=name): + p=self._real_shell_route_preflight(old,new,top) + self.assertEqual(p.returncode,79,p.stdout+p.stderr) + self.assertIn('"error":"PHYSICAL_INTERFACE_ROUTE_DRIFT"',p.stdout) + self.assertNotIn("CLEANUP_CALLED",p.stdout) + self.assertNotIn("ROLLBACK_CALLED",p.stdout) + self.assertNotIn("PRECHECK_MATCHED",p.stdout) + + def test_11_executable_start_matching_route_uses_safe_path(self): + p=self._real_shell_route_preflight("wlp2s0","wlp2s0","wlp2s0") + self.assertEqual(p.returncode,0,p.stdout+p.stderr) + self.assertIn("PRECHECK_MATCHED",p.stdout) + self.assertNotIn("CLEANUP_CALLED",p.stdout) + class PathNamespace: Path=Path From 2355e4f0004830a84436ff409da5500d2201eb13 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 22:55:47 +0330 Subject: [PATCH 018/188] docs(brain): reconcile R544 live host and CI authority without promoting RC1 --- PROJECT_BRAIN.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index d0545766..bcd5797d 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -1,8 +1,15 @@ # Current-authority notice — historical project log follows Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -Newest cumulative operational Brain at this revision: BRAIN_R496.zip. Hosted Native Windows/Linux and Saeid/MMZ actual 1.5.4 installations are PASS. Emad Windows/Linux active original network installs remain OPEN, so whole-project FINAL is UNPROVEN. -This file intentionally retains original historical records. Earlier Candidate/Final/signing-HOLD claims below are superseded for current publication and MUST NOT be run as deployment authority. + +**Active unreleased candidate / R544 evidence reconciliation (2026-10-15):** v1.5.5-rc1, GitHub Draft PR #60, source head before this metadata-only update `85c3ab43da3b2388a2b91c6da2197c1492e4977d`. Both exact-head GitHub Actions runs `38784011813` (push) and `38784018203` (PR) were SUCCESS, including 11/11 executable/source route-drift tests, deterministic SHA-pinned Linux package, disposable Root/User and watchdog rollback, v1.5.4 stable regression and hosted private Windows v1.5.4→RC1→v1.5.4 install/rollback. The public Windows release job was intentionally SKIPPED; hosted native results are not production-device acceptance. R544 updates only this current authority notice, not product/runtime/bundle sources. + +**Real installed baseline (read-only observations on 15 October 2026):** Emad Windows is registered v1.5.4; DirectInternetMethodSvc and Tailscale running. Emad Linux has User v1.5.4, Root v1.5.2, Tailscale and owned nfqws active; recorded NFQUEUE interface `wlp2s0` differs from present default `enp1s0`. Direct Google returned HTTP 204, YouTube curl exited 35; the source repair is not an installed repair. MMZ Linux User v1.5.4 / Root v1.5.2 remains the private RC1 acceptance candidate, with exact 8,338,876-byte R541 archive SHA256 `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` present and pending upgrade slots absent. Do not infer installation from the private ZIP receipt. + +**Critical open acceptance:** no independent *production-host* Root+User upgrade watchdog armed; real RC1 install/metadata activation and GUI Start→HTTPS/DoH→Stop/Recovery→rollback plus remote Tailscale/physical DNS invariants have not passed on a production host. Prior physical watchdog installation was denied by the host safety boundary: do not bypass. Preserve the active connection, refuse auto route/DNS/Tailscale changes, and keep PR Draft/public v1.5.4 unchanged until native guarded acceptance. A .NET 10 build can include commit SourceRevisionId in InformationalVersion by default, plausibly explaining changed private Windows installer SHAs between test-only commits; this is not yet proven to be the sole source of build-byte variation. Re-pin each exact-hosted-build artifact independently; never reuse SHA from a different run. + +**Current source authority:** this section of `PROJECT_BRAIN.md` is the single source-branch Index/Brain/State locator; linked unique PR/CI/physical receipts are evidence, not competing authorities. Historical `BRAIN_R*.zip` archives and earlier FINAL/Candidate/signing-HOLD statements in the log below are retained as R0→current provenance and do not override the present publication or release gates. Next useful action: obtain explicit, accepted physical root rollback guard authorization and demonstrate independent fallback before any real privileged RC1 upgrade, while continuing independent source/host read-only verification. + --- From 0e524b4d8f5db18964827bfc6a00d9108b6a376e Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:09:13 +0330 Subject: [PATCH 019/188] feat(linux): R545 periodic fail-closed GTK status refresh and exact RC1 V&V --- .github/workflows/ci.yml | 7 +- PROJECT_BRAIN.md | 9 +- linux/app/direct_internet_method.py | 34 +++++- tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r545_periodic_gui_status_contract.py | 114 ++++++++++++++++++ 6 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 tests/r545_periodic_gui_status_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 167ba831..8bad932c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839" + expected="27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70" assert p.is_file() and p.stat().st_size==8338876 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) @@ -84,6 +84,7 @@ jobs: python3 -B tests/r537_gui_consent_status_contract.py python3 -B tests/r538_active_encrypted_status_contract.py python3 -B tests/r541_route_interface_drift_contract.py + python3 -B tests/r545_periodic_gui_status_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py python3 -B tests/encrypted_selective_dns_integration_contract.py @@ -142,7 +143,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0c5ba7f8da540412e8417152d0e6ff75f1ffe4adab06ee0a1fe72223a94a4839' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '27d5289236a9cdb125669e67269ef16a7c6fcd74ae9300e65b0c8aa2e10b5f70' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -171,7 +172,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0c5ba7f8da540412e8417152d0e6ff75f1ffe4adab06ee0a1fe72223a94a4839' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '27d5289236a9cdb125669e67269ef16a7c6fcd74ae9300e65b0c8aa2e10b5f70' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index bcd5797d..0fd2700b 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,14 +2,15 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Active unreleased candidate / R544 evidence reconciliation (2026-10-15):** v1.5.5-rc1, GitHub Draft PR #60, source head before this metadata-only update `85c3ab43da3b2388a2b91c6da2197c1492e4977d`. Both exact-head GitHub Actions runs `38784011813` (push) and `38784018203` (PR) were SUCCESS, including 11/11 executable/source route-drift tests, deterministic SHA-pinned Linux package, disposable Root/User and watchdog rollback, v1.5.4 stable regression and hosted private Windows v1.5.4→RC1→v1.5.4 install/rollback. The public Windows release job was intentionally SKIPPED; hosted native results are not production-device acceptance. R544 updates only this current authority notice, not product/runtime/bundle sources. +**Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. -**Real installed baseline (read-only observations on 15 October 2026):** Emad Windows is registered v1.5.4; DirectInternetMethodSvc and Tailscale running. Emad Linux has User v1.5.4, Root v1.5.2, Tailscale and owned nfqws active; recorded NFQUEUE interface `wlp2s0` differs from present default `enp1s0`. Direct Google returned HTTP 204, YouTube curl exited 35; the source repair is not an installed repair. MMZ Linux User v1.5.4 / Root v1.5.2 remains the private RC1 acceptance candidate, with exact 8,338,876-byte R541 archive SHA256 `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` present and pending upgrade slots absent. Do not infer installation from the private ZIP receipt. +**Verification scope:** Six extracted-method negative-first tests PASS on MMZ; seven additional real GTK4 timer/route tests PASS in isolated loopback Broadway and synthetic HOME, with unchanged physical installation, Tailscale, routes and DNS (receipt `R541_QA/R545_GTK_TIMER_RECEIPT.json`). Hosted CI for this R545 candidate requires exact new-head confirmation; R544 prior doc-only PR CI passed, and R543 hosted Push/PR CI for the previous source version passed including Root/User/watchdog and private Windows installed upgrade/rollback. -**Critical open acceptance:** no independent *production-host* Root+User upgrade watchdog armed; real RC1 install/metadata activation and GUI Start→HTTPS/DoH→Stop/Recovery→rollback plus remote Tailscale/physical DNS invariants have not passed on a production host. Prior physical watchdog installation was denied by the host safety boundary: do not bypass. Preserve the active connection, refuse auto route/DNS/Tailscale changes, and keep PR Draft/public v1.5.4 unchanged until native guarded acceptance. A .NET 10 build can include commit SourceRevisionId in InformationalVersion by default, plausibly explaining changed private Windows installer SHAs between test-only commits; this is not yet proven to be the sole source of build-byte variation. Re-pin each exact-hosted-build artifact independently; never reuse SHA from a different run. +**Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. -**Current source authority:** this section of `PROJECT_BRAIN.md` is the single source-branch Index/Brain/State locator; linked unique PR/CI/physical receipts are evidence, not competing authorities. Historical `BRAIN_R*.zip` archives and earlier FINAL/Candidate/signing-HOLD statements in the log below are retained as R0→current provenance and do not override the present publication or release gates. Next useful action: obtain explicit, accepted physical root rollback guard authorization and demonstrate independent fallback before any real privileged RC1 upgrade, while continuing independent source/host read-only verification. +**Critical dependency chain:** independent authorized production-host Root+User rollback worker NOT armed; no physical RC1 native install/activation and GUI Start→DoH/HTTPS→Stop/Recovery/rollback with full Tailscale and physical-DNS invariants. A previous attempt to create the physical rollback guard was denied by host safety policy: do not bypass. Keep PR Draft and public v1.5.4 as sole Stable; do not treat hosted/isolated PASS as physical FINAL. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. +**Authority map:** this current `PROJECT_BRAIN.md` notice is the source-branch singleton Index/Brain/State locator, with unique GitHub PR/CI and physical QA receipts as evidence links. The immutable R0→R544 material below and historical `BRAIN_R*.zip` snapshots remain provenance, not competing active authorities. Earlier Candidate/Final/signing-HOLD statements are superseded where conflicting. The owner’s DoD remains one-install in-place, preserved settings, verified rollback, active Linux/Windows runtime and a single trustworthy public release line. --- diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index ed878edd..7674ba49 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -686,6 +686,9 @@ def __init__(self,app): keys=Gtk.EventControllerKey();keys.connect("key-pressed",self.on_key);self.add_controller(keys) self.refresh() + # Observe route/process changes without doing periodic web requests. + self._status_timer_id=GLib.timeout_add_seconds(15,self.periodic_state_tick) + self.connect("close-request",self.stop_status_timer) if self.adult_check_enabled: threading.Thread(target=self.adult_sync_worker,args=(False,),daemon=True).start() threading.Thread(target=self.update_check_worker,args=(False,),daemon=True).start() @@ -696,8 +699,30 @@ def toggle_maximize(self,*_args): else: self.maximize() - def refresh(self): - s=load_state();mode=s["mode"] + def stop_status_timer(self,*_args): + source_id=getattr(self,"_status_timer_id",0) + if source_id: + GLib.source_remove(source_id) + self._status_timer_id=0 + return False + + def periodic_state_tick(self): + # A read-only UI observation; no route/DNS/tunnel/service actions and + # no HTTP probes. The explicit Refresh button owns live website checks. + try: + current=load_state() + except Exception: + current={"mode":"STALE", + "detail":"Automatic status verification failed. Use Refresh or Recovery."} + visible=self.status.get_text().removeprefix("Status: ") + if current["mode"]!=visible: + self.refresh(state=current,start_checks=False) + elif current["detail"]!=self.detail.get_text(): + self.detail.set_text(current["detail"]) + return True + + def refresh(self,state=None,start_checks=True): + s=state if state is not None else load_state();mode=s["mode"] self.status.set_text("Status: "+mode);self.detail.set_text(s["detail"]) for css in ("success","warning","error","dim-label"): self.status.remove_css_class(css) @@ -716,10 +741,13 @@ def refresh(self): if recovery_ok:self.buttons["recovery"].add_css_class("destructive-action") self.live_check_generation+=1 generation=self.live_check_generation - if mode=="ACTIVE": + if mode=="ACTIVE" and start_checks: self.gemini_live.set_text("Gemini: Checking…") self.adult_live.set_text("Adult coverage: Checking…" if self.adult_check_enabled else "Adult coverage: Disabled") threading.Thread(target=self.live_checks_worker,args=(generation,),daemon=True).start() + elif mode=="ACTIVE": + self.gemini_live.set_text("Gemini: Press Refresh for a live check") + self.adult_live.set_text("Adult coverage: Press Refresh for a live check" if self.adult_check_enabled else "Adult coverage: Disabled") else: self.gemini_live.set_text("Gemini: Available when Direct Method is active") self.adult_live.set_text("Adult coverage: Disabled" if not self.adult_check_enabled else "Adult coverage: Available when Direct Method is active") diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 6441ad8e..73441bd5 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":26,"sha256":"0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839"} +EXPECTED={"version":VERSION,"files":26,"sha256":"27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -86,7 +86,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","system/protected_upgrade_transaction.py", "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "83A50CB575F6398A75904A5E422ADF0A416447BFC0C2899C00BF2FC57B31C875") + "732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 0ee8811d..0bec4c7d 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="0c5ba7f8da540412e8417152d0e6ff75f1ffe4adab06ee0a1fe72223a94a4839" +CANDIDATE_SHA="27d5289236a9cdb125669e67269ef16a7c6fcd74ae9300e65b0c8aa2e10b5f70" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r545_periodic_gui_status_contract.py b/tests/r545_periodic_gui_status_contract.py new file mode 100644 index 00000000..a3d30fef --- /dev/null +++ b/tests/r545_periodic_gui_status_contract.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +"""Isolated negative-first source-extracted GUI status timer regression. + +Does not import GTK or run a service, subprocess, DNS, route, network request, +user setting or privileged command. Only original AST methods are executed. +""" +import ast +from pathlib import Path +import types +import unittest + +ROOT=Path(__file__).resolve().parents[1] +SOURCE=(ROOT/"linux/app/direct_internet_method.py").read_text(encoding="utf-8") +TREE=ast.parse(SOURCE) +WINDOW=next(x for x in TREE.body if isinstance(x,ast.ClassDef) and x.name=="Window") +names={"refresh","periodic_state_tick","stop_status_timer"} +funcs=[n for n in WINDOW.body if isinstance(n,ast.FunctionDef) and n.name in names] +assert {f.name for f in funcs}==names +INIT=next(n for n in WINDOW.body if isinstance(n,ast.FunctionDef) and n.name=="__init__") +init=ast.get_source_segment(SOURCE,INIT) +assert 'GLib.timeout_add_seconds(15,self.periodic_state_tick)' in init +assert 'self.connect("close-request",self.stop_status_timer)' in init + +class Label: + def __init__(self,text=""):self.text=text + def get_text(self):return self.text + def set_text(self,text):self.text=text + def remove_css_class(self,*_):pass + def add_css_class(self,*_):pass + +class Button: + def __init__(self):self.enabled=None + def set_sensitive(self,v):self.enabled=v + def remove_css_class(self,*_):pass + def add_css_class(self,*_):pass + +class FakeGLib: + removed=[] + @classmethod + def source_remove(cls,i):cls.removed.append(i) + +class ForbiddenThread: + def __init__(self,*_,**__):raise AssertionError("PERIODIC_REFRESH_MUST_NOT_SPAWN_WEB_PROBES") + +_state=[{"mode":"ACTIVE","detail":"ok"}] +def read_state(): + if isinstance(_state[0],Exception):raise _state[0] + return _state[0] +env={"load_state":read_state,"GLib":FakeGLib,"threading":types.SimpleNamespace(Thread=ForbiddenThread)} +exec(compile(ast.Module(body=funcs,type_ignores=[]),"original_gui_methods","exec"),env) + +class Dummy: + def __init__(self,mode="ACTIVE",detail="ok"): + self.status=Label("Status: "+mode) + self.detail=Label(detail) + self.gemini_live=Label("Gemini: previous") + self.adult_live=Label("Adult coverage: previous") + self.live_check_generation=0 + self.adult_check_enabled=True + self.buttons={k:Button() for k in ("start","stop","recovery","refresh","update")} + self.refresh=types.MethodType(env["refresh"],self) + self.periodic_state_tick=types.MethodType(env["periodic_state_tick"],self) + self.stop_status_timer=types.MethodType(env["stop_status_timer"],self) + self._status_timer_id=0 + +class TimerContract(unittest.TestCase): + def test_01_stable_active_no_repeated_live_probe(self): + _state[0]={"mode":"ACTIVE","detail":"ok"} + d=Dummy() + self.assertTrue(d.periodic_state_tick()) + self.assertEqual(d.live_check_generation,0) + self.assertEqual(d.gemini_live.get_text(),"Gemini: previous") + def test_02_physical_route_drift_marks_stale_and_disables_start(self): + _state[0]={"mode":"STALE","detail":"wlp2s0 differs from enp1s0"} + d=Dummy() + self.assertTrue(d.periodic_state_tick()) + self.assertEqual(d.status.get_text(),"Status: STALE") + self.assertFalse(d.buttons["start"].enabled) + self.assertTrue(d.buttons["recovery"].enabled) + self.assertIn("enp1s0",d.detail.get_text()) + self.assertEqual(d.live_check_generation,1) + def test_03_stale_detail_change_does_not_start_probes(self): + _state[0]={"mode":"STALE","detail":"now no route"} + d=Dummy("STALE","old route") + self.assertTrue(d.periodic_state_tick()) + self.assertEqual(d.detail.get_text(),"now no route") + self.assertEqual(d.live_check_generation,0) + def test_04_auto_recovery_active_no_background_web_checks(self): + _state[0]={"mode":"ACTIVE","detail":"route recovered"} + d=Dummy("STALE","was stale") + self.assertTrue(d.periodic_state_tick()) + self.assertEqual(d.status.get_text(),"Status: ACTIVE") + self.assertIn("Press Refresh",d.gemini_live.get_text()) + self.assertFalse(d.buttons["start"].enabled) + def test_05_state_read_error_is_fail_closed(self): + _state[0]=RuntimeError("synthetic read failure") + d=Dummy() + self.assertTrue(d.periodic_state_tick()) + self.assertEqual(d.status.get_text(),"Status: STALE") + self.assertFalse(d.buttons["start"].enabled) + self.assertTrue(d.buttons["recovery"].enabled) + def test_06_close_removes_only_owned_timer_once(self): + FakeGLib.removed=[] + d=Dummy() + d._status_timer_id=4321 + self.assertFalse(d.stop_status_timer()) + self.assertFalse(d.stop_status_timer()) + self.assertEqual(FakeGLib.removed,[4321]) + self.assertEqual(d._status_timer_id,0) + +if __name__=="__main__": + res=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(TimerContract)) + print("R545_PERIODIC_GUI_STATUS cases=",res.testsRun,"failures=",len(res.failures),"errors=",len(res.errors)) + raise SystemExit(0 if res.wasSuccessful() else 1) From 5b7293cb7acba0f0c76d5a6c9cf5629bc73581a9 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:10:26 +0330 Subject: [PATCH 020/188] ci(linux): pin R545 exact deterministic archive byte length 8340334 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bad932c..35c15c82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") expected="27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70" - assert p.is_file() and p.stat().st_size==8338876 + assert p.is_file() and p.stat().st_size==8340334 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" From 5d4415acb677ed96ef27a280484a987c07a53e31 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:18:08 +0330 Subject: [PATCH 021/188] docs(brain): accept exact R545 hosted CI and MMZ private archive receipt --- PROJECT_BRAIN.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 0fd2700b..a6026107 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,15 +2,15 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. +**Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. The new private R545 ZIP was exclusively staged on physical MMZ as `R541_QA/R545_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600, pinned SHA), with original Root 1.5.2/User 1.5.4, no upgrade slots, route/DNS/Tailscale unchanged; unique receipt `R541_QA/R545_PRIVATE_READY.json` SHA256 `AD78F42CEE5E74F61EF57DE83943F1059900F29F7DED43B21C741EAF79695FC9`. Private staging is NOT installation, permission or DoH consent. -**Verification scope:** Six extracted-method negative-first tests PASS on MMZ; seven additional real GTK4 timer/route tests PASS in isolated loopback Broadway and synthetic HOME, with unchanged physical installation, Tailscale, routes and DNS (receipt `R541_QA/R545_GTK_TIMER_RECEIPT.json`). Hosted CI for this R545 candidate requires exact new-head confirmation; R544 prior doc-only PR CI passed, and R543 hosted Push/PR CI for the previous source version passed including Root/User/watchdog and private Windows installed upgrade/rollback. +**Verification scope:** Six extracted-method negative-first tests PASS on MMZ; seven additional real GTK4 timer/route tests PASS in isolated loopback Broadway and synthetic HOME, with unchanged physical installation, Tailscale, routes and DNS (receipt `R541_QA/R545_GTK_TIMER_RECEIPT.json`). Exact source+CI-metadata head `5b7293cb7acba0f0c76d5a6c9cf5629bc73581a9` passed hosted Push CI [38795817931](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38795817931) and PR CI [38795826936](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38795826936), including Linux package SHA/size, new R545 6/6, R541 11/11, 1.5.4 regression, protected Root, Root/User native disposable upgrade and independent watchdog, Windows source and private Windows native v1.5.4→RC1→v1.5.4 rollback. Public unsigned Windows release job was intentionally SKIPPED (not release acceptance). The exact-run Windows private installer SHA256 was `A1D17585067D46953067AA62A3D4DA6BC9BE56EDB1C6E86A22D381885E809DC1` and its native acceptance receipt SHA256 was `89FC5BAC118EDCA3F60A606EA48BFD79E9CB9E9E484DE007FE16D143DEB4EA77`; these cannot authenticate a different run's artifact. **Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. **Critical dependency chain:** independent authorized production-host Root+User rollback worker NOT armed; no physical RC1 native install/activation and GUI Start→DoH/HTTPS→Stop/Recovery/rollback with full Tailscale and physical-DNS invariants. A previous attempt to create the physical rollback guard was denied by host safety policy: do not bypass. Keep PR Draft and public v1.5.4 as sole Stable; do not treat hosted/isolated PASS as physical FINAL. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. -**Authority map:** this current `PROJECT_BRAIN.md` notice is the source-branch singleton Index/Brain/State locator, with unique GitHub PR/CI and physical QA receipts as evidence links. The immutable R0→R544 material below and historical `BRAIN_R*.zip` snapshots remain provenance, not competing active authorities. Earlier Candidate/Final/signing-HOLD statements are superseded where conflicting. The owner’s DoD remains one-install in-place, preserved settings, verified rollback, active Linux/Windows runtime and a single trustworthy public release line. +**Authority map:** this current `PROJECT_BRAIN.md` notice is the source-branch singleton Index/Brain/State locator, with unique GitHub PR/CI and physical QA receipts as evidence links. The historical R0 lineage below and `BRAIN_R*.zip` snapshots remain provenance, not competing active authorities. Earlier Candidate/Final/signing-HOLD statements are superseded where conflicting. The owner’s DoD remains one-install in-place, preserved settings, verified rollback, active Linux/Windows runtime and a single trustworthy public release line. --- From 7a58e40b9249a88b7e831fbc281428a5cfd13c0e Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:30:39 +0330 Subject: [PATCH 022/188] build(windows): R548 disable commit-derived assembly identity; normalize CI builds --- tests/dotnet10_target_contract.py | 7 +++++++ tests/r535_windows_candidate_hosted_build.ps1 | 7 +++++-- windows/gui/DirectInternetMethod.csproj | 2 +- windows/service/DirectInternetMethod.Service.csproj | 1 + 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/dotnet10_target_contract.py b/tests/dotnet10_target_contract.py index 8ce47054..27bf1fcb 100644 --- a/tests/dotnet10_target_contract.py +++ b/tests/dotnet10_target_contract.py @@ -25,9 +25,11 @@ def inspect(raw,project): tree=ET.fromstring(raw) target=tree.find(".//TargetFramework") version=tree.find(".//Version") + revision=tree.find(".//IncludeSourceRevisionInInformationalVersion") output=tree.find(".//OutputType") assert target is not None and target.text==TARGET,(project,"TargetFramework") assert version is not None and version.text==CURRENT,(project,"product_version_guard") + assert revision is not None and revision.text=="false",(project,"reproducible_informational_version") assert output is not None and output.text=="WinExe",(project,"runtime_subsystem") if "/gui/" in project: wpf=tree.find(".//UseWPF") @@ -40,6 +42,11 @@ def negative_first(): inspect(data,project) assert data.count(b"net10.0-windows")==1 assert b"net8.0-windows" not in data + # In-memory negative fixture: a commit-derived InformationalVersion + # would make unchanged source differ between CI GitHub revisions. + try:inspect(data.replace(b"false",b"true"),project) + except AssertionError:pass + else:raise AssertionError(("FAIL_OPEN_SOURCE_REVISION_EMBED",project)) # In-memory source mutations must be rejected before packaging. for wrong in (b"net8.0-windows",b"net9.0-windows",b"net11.0-windows"): try:inspect(data.replace(b"net10.0-windows",wrong),project) diff --git a/tests/r535_windows_candidate_hosted_build.ps1 b/tests/r535_windows_candidate_hosted_build.ps1 index eb6f26a0..8ccd3aa7 100644 --- a/tests/r535_windows_candidate_hosted_build.ps1 +++ b/tests/r535_windows_candidate_hosted_build.ps1 @@ -22,9 +22,9 @@ Set-Location $Root & pwsh -NoProfile -NonInteractive -File tests/prepare_powershell_runtime.ps1 if($LASTEXITCODE -ne 0){throw 'PWSH_BUNDLE_PREP_FAILED'} if((Get-FileHash (Join-Path $Root 'windows\vendor\pwsh\pwsh.exe') -Algorithm SHA256).Hash -cne 'BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285'){throw 'PWSH_BUNDLE_SHA'} -& dotnet publish windows/gui/DirectInternetMethod.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -o $gstage +& dotnet publish windows/gui/DirectInternetMethod.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:ContinuousIntegrationBuild=true -o $gstage if($LASTEXITCODE -ne 0){throw 'GUI_PUBLISH_FAILED'} -& dotnet publish windows/service/DirectInternetMethod.Service.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -o $sstage +& dotnet publish windows/service/DirectInternetMethod.Service.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:ContinuousIntegrationBuild=true -o $sstage if($LASTEXITCODE -ne 0){throw 'SERVICE_PUBLISH_FAILED'} $gb=Join-Path $gstage 'DirectInternetMethod.exe';$sb=Join-Path $sstage 'DirectInternetMethod.Service.exe' if(!(Test-Path $gb) -or !(Test-Path $sb)){throw 'RC1_BINARIES_MISSING'} @@ -56,4 +56,7 @@ $report=[ordered]@{ installation='NOT_TESTED_YET';publicReleaseUnchanged=$true } [IO.File]::WriteAllText((Join-Path $qa 'BUILD_R535.json'),(($report|ConvertTo-Json -Depth 5)+[Environment]::NewLine),[Text.UTF8Encoding]::new($false)) +Write-Output ('R548_GUI_SHA256='+$report.guiSha) +Write-Output ('R548_SERVICE_SHA256='+$report.serviceSha) +Write-Output ('R548_MANIFEST_SHA256='+$report.manifestSha) Write-Output ('PASS_R535_HOSTED_BUILD_SHA256='+$report.installerSha) diff --git a/windows/gui/DirectInternetMethod.csproj b/windows/gui/DirectInternetMethod.csproj index 53db0d97..c4be8576 100644 --- a/windows/gui/DirectInternetMethod.csproj +++ b/windows/gui/DirectInternetMethod.csproj @@ -1 +1 @@ -WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.5-rc11.5.5.01.5.5.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file +WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.5-rc1false1.5.5.01.5.5.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file diff --git a/windows/service/DirectInternetMethod.Service.csproj b/windows/service/DirectInternetMethod.Service.csproj index 111ca5d0..62c8ed30 100644 --- a/windows/service/DirectInternetMethod.Service.csproj +++ b/windows/service/DirectInternetMethod.Service.csproj @@ -8,6 +8,7 @@ DirectInternetMethod.Service ..\app\DirectInternetMethod.ico 1.5.5-rc1 + false 1.5.5.0 1.5.5.0 Direct Internet Method Service From aba254274a1863931580f20f086c4120071ca308 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:34:00 +0330 Subject: [PATCH 023/188] docs(brain): upsert R546/R547 physical evidence and R548 build identity risk --- PROJECT_BRAIN.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index a6026107..0050a392 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,6 +6,10 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **Verification scope:** Six extracted-method negative-first tests PASS on MMZ; seven additional real GTK4 timer/route tests PASS in isolated loopback Broadway and synthetic HOME, with unchanged physical installation, Tailscale, routes and DNS (receipt `R541_QA/R545_GTK_TIMER_RECEIPT.json`). Exact source+CI-metadata head `5b7293cb7acba0f0c76d5a6c9cf5629bc73581a9` passed hosted Push CI [38795817931](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38795817931) and PR CI [38795826936](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38795826936), including Linux package SHA/size, new R545 6/6, R541 11/11, 1.5.4 regression, protected Root, Root/User native disposable upgrade and independent watchdog, Windows source and private Windows native v1.5.4→RC1→v1.5.4 rollback. Public unsigned Windows release job was intentionally SKIPPED (not release acceptance). The exact-run Windows private installer SHA256 was `A1D17585067D46953067AA62A3D4DA6BC9BE56EDB1C6E86A22D381885E809DC1` and its native acceptance receipt SHA256 was `89FC5BAC118EDCA3F60A606EA48BFD79E9CB9E9E484DE007FE16D143DEB4EA77`; these cannot authenticate a different run's artifact. +**R546/R547 current physical rehearsals:** On actual MMZ, exactly SHA-pinned private R545 Linux archive `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` ran the unchanged original User transaction in temporary synthetic HOME: Stage, Apply, metadata attestation, exact 1.5.4 rollback, custom Provider/config preservation, rejected mutable drift and no orphan slots, **8/8 PASS**, physical pre/post Root/User/DNS/route/Tailscale/consent identical. Receipt `R541_QA/R546_USER_SHADOW_RECEIPT.json`, SHA256 `91D749F244F0375FE822E6E97402F35108A7CBAFF9B755A60613848DF1AA30A7`. Separately the exact untouched production RootTransaction source SHA `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` executed its original *read-only* `_baseline()` against the real MMZ machine, confirming 9 protected static file hashes, the 2 existing owned-file hashes/modes and 2 absent new-file preconditions. Receipt `R541_QA/R547_ROOT_PREFLIGHT_RECEIPT.json`, SHA256 `1CE6EC8CB1F5FF7FFD6A5A11E91DECF689EFB56A4DC0FCFEDEF0A7C2CAAE9937`. No real Root transaction, installed application code change, network/privilege mutation or user consent. + +**R548 Windows build identity hardening:** exact code commit `7a58e40b9249a88b7e831fbc281428a5cfd13c0e` disables commit-derived `InformationalVersion` for both .NET 10 GUI and Service projects and normalizes official hosted `dotnet publish` using `ContinuousIntegrationBuild=true`. Both source CI job checks passed; Windows hosted private installer lifecycle is pending at the time of this evidence upsert. Rationale: official .NET SDK documentation says `SourceRevisionId` is appended by default, causing version-identical binaries to vary across Git commits; disabling it removes *one potential* nondeterminism source, not proof of bit-for-bit installer reproducibility. The exact-source Git commit and SHA remain captured in native CI receipts. Independent Inno Setup file timestamps and other build entropy remain to be measured after two native runs of unchanged Windows sources. Linux R545 archive/GUI, current public release v1.5.4 and physical user installations remain unchanged by R548. + **Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. **Critical dependency chain:** independent authorized production-host Root+User rollback worker NOT armed; no physical RC1 native install/activation and GUI Start→DoH/HTTPS→Stop/Recovery/rollback with full Tailscale and physical-DNS invariants. A previous attempt to create the physical rollback guard was denied by host safety policy: do not bypass. Keep PR Draft and public v1.5.4 as sole Stable; do not treat hosted/isolated PASS as physical FINAL. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. From 857d9e8d721435b15fd8a15e669a6b4b66f68313 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:42:20 +0330 Subject: [PATCH 024/188] build(windows): R549 omit embedded Inno file timestamps; negative-first contract --- tests/windows_contract_audit.py | 27 +++++++++- windows/installer/DirectInternetMethod.iss | 58 +++++++++++----------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 9c9593a2..3fdb4325 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -283,8 +283,33 @@ def idx(s,x): return s.find(x) def checked_notice_mapping(filename): src = r"..\bin\zapret" + "\\" + filename dest = r"{commonpf}\DirectInternetMethod\Privileged\bin\zapret" - line = 'Source: "' + src + '"; DestDir: "' + dest + '"; Flags: ignoreversion' + line = 'Source: "' + src + '"; DestDir: "' + dest + '"; Flags: ignoreversion notimestamp' return iss.splitlines().count(line)==1 +def r549_inno_source_timestamp_contract(source): + try: + rows_section=source.split("[Files]\n",1)[1].split("[InstallDelete]",1)[0] + except IndexError: + return False + rows=[row.strip() for row in rows_section.splitlines() + if row.strip().startswith("Source: ")] + if len(rows)!=28 or len(set(rows))!=28: + return False + for row in rows: + if "Flags:" not in row: + return False + flags=row.split("Flags:",1)[1].strip().split() + if flags.count("notimestamp")!=1 or "touch" in flags or "comparetimestamp" in flags: + return False + return True + +# Negative-first source-time fixture: exactly 28 embedded records without +# stored input timestamps. No change to owned uninstall/upgrade file scope. +check("r549_inno_no_source_timestamps", + r549_inno_source_timestamp_contract(iss) + and not r549_inno_source_timestamp_contract(iss.replace(" notimestamp","",1)) + and not r549_inno_source_timestamp_contract( + iss.replace(" notimestamp"," notimestamp touch",1))) + check("installer_four_upstream_license_notices_byte_pinned", all( (W/"bin"/"zapret"/filename).is_file() diff --git a/windows/installer/DirectInternetMethod.iss b/windows/installer/DirectInternetMethod.iss index 57288340..a7a1aa30 100644 --- a/windows/installer/DirectInternetMethod.iss +++ b/windows/installer/DirectInternetMethod.iss @@ -37,35 +37,35 @@ RestartApplications=no Name: "{commonappdata}\DirectInternetMethod\UserConfig"; Permissions: users-modify [Files] -Source: "..\app\DirectInternetMethod.exe"; DestDir: "{app}\app"; Flags: ignoreversion -Source: "..\app\DirectInternetMethod.ico"; DestDir: "{app}\app"; Flags: ignoreversion -Source: "..\app\Status.ps1"; DestDir: "{app}\app"; Flags: ignoreversion -Source: "..\..\router_gateway\providers.json"; DestDir: "{app}\app\router_gateway"; Flags: ignoreversion onlyifdoesntexist -Source: "..\docs\*"; DestDir: "{app}\docs"; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "..\manifest.json"; DestDir: "{app}"; Flags: ignoreversion -Source: "..\RELEASE.json"; DestDir: "{app}"; Flags: ignoreversion - -Source: "..\app\DirectInternetMethod.Service.exe"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion -Source: "..\vendor\pwsh\*"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\runtime\pwsh"; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "..\app\Start-Direct.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion -Source: "..\app\Stop-Direct.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion -Source: "..\app\Recovery.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion -Source: "..\app\SafeLegacyProviderRollback.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion -Source: "..\bin\ctrld\ctrld.exe"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\ctrld"; Flags: ignoreversion -Source: "..\bin\ctrld\ctrld.toml"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\ctrld"; Flags: ignoreversion -Source: "..\bin\ctrld\LICENSE.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\ctrld"; Flags: ignoreversion -Source: "..\bin\zapret\winws.exe"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\WinDivert.dll"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\WinDivert64.sys"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\cygwin1.dll"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\LICENSE.Cygwin.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\LGPL3.Cygwin.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\GPL3.Cygwin.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\hosts.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\adult-fallback-hosts.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\strong-override-hosts.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\LICENSE.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion -Source: "..\bin\zapret\LICENSE.WinDivert.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion +Source: "..\app\DirectInternetMethod.exe"; DestDir: "{app}\app"; Flags: ignoreversion notimestamp +Source: "..\app\DirectInternetMethod.ico"; DestDir: "{app}\app"; Flags: ignoreversion notimestamp +Source: "..\app\Status.ps1"; DestDir: "{app}\app"; Flags: ignoreversion notimestamp +Source: "..\..\router_gateway\providers.json"; DestDir: "{app}\app\router_gateway"; Flags: ignoreversion onlyifdoesntexist notimestamp +Source: "..\docs\*"; DestDir: "{app}\docs"; Flags: ignoreversion recursesubdirs createallsubdirs notimestamp +Source: "..\manifest.json"; DestDir: "{app}"; Flags: ignoreversion notimestamp +Source: "..\RELEASE.json"; DestDir: "{app}"; Flags: ignoreversion notimestamp + +Source: "..\app\DirectInternetMethod.Service.exe"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion notimestamp +Source: "..\vendor\pwsh\*"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\runtime\pwsh"; Flags: ignoreversion recursesubdirs createallsubdirs notimestamp +Source: "..\app\Start-Direct.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion notimestamp +Source: "..\app\Stop-Direct.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion notimestamp +Source: "..\app\Recovery.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion notimestamp +Source: "..\app\SafeLegacyProviderRollback.ps1"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\app"; Flags: ignoreversion notimestamp +Source: "..\bin\ctrld\ctrld.exe"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\ctrld"; Flags: ignoreversion notimestamp +Source: "..\bin\ctrld\ctrld.toml"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\ctrld"; Flags: ignoreversion notimestamp +Source: "..\bin\ctrld\LICENSE.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\ctrld"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\winws.exe"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\WinDivert.dll"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\WinDivert64.sys"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\cygwin1.dll"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\LICENSE.Cygwin.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\LGPL3.Cygwin.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\GPL3.Cygwin.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\hosts.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\adult-fallback-hosts.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\strong-override-hosts.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\LICENSE.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp +Source: "..\bin\zapret\LICENSE.WinDivert.txt"; DestDir: "{commonpf}\DirectInternetMethod\Privileged\bin\zapret"; Flags: ignoreversion notimestamp [InstallDelete] Type: files; Name: "{app}\app\ControlPanel.cmd" From 952bde0b739fa349f5acc9a921b3f14e6f15f92d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:44:42 +0330 Subject: [PATCH 025/188] docs(brain): R548 two-run byte identities and R549 Inno deterministic candidate --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 0050a392..0b8ad92e 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -8,7 +8,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R546/R547 current physical rehearsals:** On actual MMZ, exactly SHA-pinned private R545 Linux archive `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` ran the unchanged original User transaction in temporary synthetic HOME: Stage, Apply, metadata attestation, exact 1.5.4 rollback, custom Provider/config preservation, rejected mutable drift and no orphan slots, **8/8 PASS**, physical pre/post Root/User/DNS/route/Tailscale/consent identical. Receipt `R541_QA/R546_USER_SHADOW_RECEIPT.json`, SHA256 `91D749F244F0375FE822E6E97402F35108A7CBAFF9B755A60613848DF1AA30A7`. Separately the exact untouched production RootTransaction source SHA `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` executed its original *read-only* `_baseline()` against the real MMZ machine, confirming 9 protected static file hashes, the 2 existing owned-file hashes/modes and 2 absent new-file preconditions. Receipt `R541_QA/R547_ROOT_PREFLIGHT_RECEIPT.json`, SHA256 `1CE6EC8CB1F5FF7FFD6A5A11E91DECF689EFB56A4DC0FCFEDEF0A7C2CAAE9937`. No real Root transaction, installed application code change, network/privilege mutation or user consent. -**R548 Windows build identity hardening:** exact code commit `7a58e40b9249a88b7e831fbc281428a5cfd13c0e` disables commit-derived `InformationalVersion` for both .NET 10 GUI and Service projects and normalizes official hosted `dotnet publish` using `ContinuousIntegrationBuild=true`. Both source CI job checks passed; Windows hosted private installer lifecycle is pending at the time of this evidence upsert. Rationale: official .NET SDK documentation says `SourceRevisionId` is appended by default, causing version-identical binaries to vary across Git commits; disabling it removes *one potential* nondeterminism source, not proof of bit-for-bit installer reproducibility. The exact-source Git commit and SHA remain captured in native CI receipts. Independent Inno Setup file timestamps and other build entropy remain to be measured after two native runs of unchanged Windows sources. Linux R545 archive/GUI, current public release v1.5.4 and physical user installations remain unchanged by R548. +**R548 Windows build identity hardening (accepted source and native lifecycle):** Commit `7a58e40b9249a88b7e831fbc281428a5cfd13c0e` disables commit-derived .NET 10 `InformationalVersion` and normalizes official Windows CI build paths with `ContinuousIntegrationBuild=true`. Hosted Push/PR CI succeeded; the private native Windows v1.5.4→1.5.5-rc1→v1.5.4 installation/user-data/rollback PASSed on both [38798487532](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38798487532) and [38798942807](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38798942807). The intervening Git commit `aba254274a1863931580f20f086c4120071ca308` changed only this Brain, NOT code/build inputs. Exact SHA256 identities across the two independent hosted native builds were identical for GUI `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`, Service `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8` and manifest `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`. However full unsigned private Inno Setup SHA still differed: `F3D56D2AC12ED44563D599EE6459FBBECF5562D791E846C81FD9E555821BC9CD` versus `CF2F15830D09F1F42740C82CA6E4595C3D248D40557632EC84AA536FA1E9BADD`. Therefore binary/payload reproducibility is **PASS**, whole-installer reproducibility remained **OPEN** at R548. Official .NET documentation confirms SourceRevisionId normally enters InformationalVersion, and independent SHA-provenance remains in exact CI build/acceptance receipts. Never reuse a SHA from a different hosted build. + +**R549 Inno Setup timestamp-source hardening (candidate, unpromoted):** Commit `857d9e8d721435b15fd8a15e669a6b4b66f68313` adds the officially supported `notimestamp` flag to all 28 embedded `[Files]` records and a negative-first Windows source audit rejecting missing flags or conflicting `touch`/`comparetimestamp`. Vendor documentation: https://jrsoftware.org/ishelp/topic_filessection.htm . Windows source PR CI is accepted at this update; complete private Windows installer native lifecycle and cross-commit Setup SHA reproducibility remain to be verified by exact later runs. Release metadata, installer paths, one-install/rollback behaviors, Tailscale, physical machine, public v1.5.4 and unchanged 26-file Linux R545 ZIP are not mutated by this source-only candidate. No release promotion without native acceptance and authorized independent physical Root/User watchdog. **Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. From b3b03b55e75b7c8ac0f4b2040ef33c2bce4909a1 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 00:04:23 +0330 Subject: [PATCH 026/188] docs(brain): accept R549 SHA-identical Windows builds and physical R550/R551 blockers --- PROJECT_BRAIN.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 0b8ad92e..9a87d7e3 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -10,7 +10,11 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R548 Windows build identity hardening (accepted source and native lifecycle):** Commit `7a58e40b9249a88b7e831fbc281428a5cfd13c0e` disables commit-derived .NET 10 `InformationalVersion` and normalizes official Windows CI build paths with `ContinuousIntegrationBuild=true`. Hosted Push/PR CI succeeded; the private native Windows v1.5.4→1.5.5-rc1→v1.5.4 installation/user-data/rollback PASSed on both [38798487532](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38798487532) and [38798942807](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38798942807). The intervening Git commit `aba254274a1863931580f20f086c4120071ca308` changed only this Brain, NOT code/build inputs. Exact SHA256 identities across the two independent hosted native builds were identical for GUI `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`, Service `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8` and manifest `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`. However full unsigned private Inno Setup SHA still differed: `F3D56D2AC12ED44563D599EE6459FBBECF5562D791E846C81FD9E555821BC9CD` versus `CF2F15830D09F1F42740C82CA6E4595C3D248D40557632EC84AA536FA1E9BADD`. Therefore binary/payload reproducibility is **PASS**, whole-installer reproducibility remained **OPEN** at R548. Official .NET documentation confirms SourceRevisionId normally enters InformationalVersion, and independent SHA-provenance remains in exact CI build/acceptance receipts. Never reuse a SHA from a different hosted build. -**R549 Inno Setup timestamp-source hardening (candidate, unpromoted):** Commit `857d9e8d721435b15fd8a15e669a6b4b66f68313` adds the officially supported `notimestamp` flag to all 28 embedded `[Files]` records and a negative-first Windows source audit rejecting missing flags or conflicting `touch`/`comparetimestamp`. Vendor documentation: https://jrsoftware.org/ishelp/topic_filessection.htm . Windows source PR CI is accepted at this update; complete private Windows installer native lifecycle and cross-commit Setup SHA reproducibility remain to be verified by exact later runs. Release metadata, installer paths, one-install/rollback behaviors, Tailscale, physical machine, public v1.5.4 and unchanged 26-file Linux R545 ZIP are not mutated by this source-only candidate. No release promotion without native acceptance and authorized independent physical Root/User watchdog. +**R549 Windows deterministic one-installer, HOSTED PASS / PRIVATE RC1, 2026-10-15:** Commit `857d9e8d721435b15fd8a15e669a6b4b66f68313` adds Inno Setup's vendor-supported `notimestamp` flag to all 28 `[Files]` entries, plus a negative-first audit rejecting missing/incompatible flags. Official vendor description: https://jrsoftware.org/ishelp/topic_filessection.htm . Independent [hosted push 38800061372](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38800061372) and [hosted push 38800362560](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38800362560) both SUCCESS for **native v1.5.4→RC1→v1.5.4 in-place upgrade preserving user files, one install registration and rollback**. The only source difference between the two builds was a Brain documentation update. The native GUI, Service, manifest **and full Windows Setup.exe** matched bit-for-bit across these independent runner builds: GUI SHA256 `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`; Service `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`; manifest `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`; private Setup `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`. The two separately stamped acceptance receipts legitimately differ. **R549 whole-installer reproducibility PASS on two tested GitHub-hosted Windows-2025 runners**, not a universal promise or evidence of owner-host RC1 installation. Public Windows release job deliberately skipped; Stable v1.5.4 unchanged. + +**R550/R551 current physical network and authorization, 2026-10-15:** A new read-only MMZ preflight verified actual Root `1.5.2`, User `1.5.4`, Tailscale active, no pending upgrade slots; production `directinternetmethod-upgrade-rollback.service/.timer` units are **not installed**, while `directinternetmethod-recovery.service` only runs owned network Recovery, NOT independent two-sided application upgrade rollback. `pkexec` and `systemd-run` are available but non-interactive `sudo -n` authorization is denied. Receipt `R541_QA/R550_GUARD_READY_READONLY.json`, SHA256 `B9ABA33304131159D1771E02729FD533BD6312B7E8B2A0C910EC8BC101422FA4`. Direct physical-interface HTTPS with DIM OFF on MMZ: Google `204`, GitHub `200` (usable), YouTube `curl 7 / HTTP000` (unusable); receipt `R541_QA/R551_TLS_DIRECT_READONLY.json`, SHA256 `0E354C943B385CDF05879DE3194EBC589453FBD610A72E158B4A555E314507FD`. Full pre/post route, DNS, install identity and Tailscale unchanged. These **do not** establish RC1 live DoH/DPI/HTTPS acceptance. + +**Upstream ctrld research / deferred optional:** The direct-only candidate remains byte-pinned to ctrld v1.5.7. Upstream official `v1.5.8` published 7 October 2026 has DNS64/DNSSEC hardening, improved recovery/split-DNS handling and a new retained diagnostic journal; details https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8 . Do not silently swap the pinned runtime: verify targeted applicability, additional local log/privacy behavior, hashes and host-native acceptance independently. The newer upstream exists but is not an automatic blocker for the present IPv4-only scoped pilot. No extra unrelated release gate is added. **Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. From 83db4236225905e0e950dd6009b4f17baf46e533 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 00:42:24 +0330 Subject: [PATCH 027/188] test(linux): R552 fail-closed guarded-upgrade candidate and isolated watchdog contracts --- .github/workflows/ci.yml | 1 + tests/r552_native_upgrade_guard_candidate.py | 320 +++++++++++++++++++ tests/r552_native_upgrade_guard_contract.py | 116 +++++++ 3 files changed, 437 insertions(+) create mode 100644 tests/r552_native_upgrade_guard_candidate.py create mode 100644 tests/r552_native_upgrade_guard_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35c15c82..549ffba3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,7 @@ jobs: python3 -B tests/r538_active_encrypted_status_contract.py python3 -B tests/r541_route_interface_drift_contract.py python3 -B tests/r545_periodic_gui_status_contract.py + python3 -B tests/r552_native_upgrade_guard_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py python3 -B tests/encrypted_selective_dns_integration_contract.py diff --git a/tests/r552_native_upgrade_guard_candidate.py b/tests/r552_native_upgrade_guard_candidate.py new file mode 100644 index 00000000..b5efbf91 --- /dev/null +++ b/tests/r552_native_upgrade_guard_candidate.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 +"""R552 native Linux Root/User upgrade watchdog candidate, NOT authorized to arm. + +Prepare a pinned root-owned detached systemd rollback guard BEFORE protected +upgrade. Never disable OS authentication, change networking, or act on foreign +owners. The preflight action is unprivileged/read-only. arm/rollback are root +only and are NOT to be used on human hosts before independent hosted validation +and explicit host authorization. No update/install is performed by this file. +""" +from __future__ import annotations +import argparse +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import pwd +import re +import shutil +import stat +import subprocess +import sys +import time + +VERSION="1.5.5-rc1" +BASELINE="1.5.2" +OLD_USER="1.5.4" +PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" +PIN_USER="A909BBB12B78875A9438C756AE2DCAFF05E0DF2EBB5551E97544D2B20C46346F" +PIN_PKG="27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70" +GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") +BASELINE_FILE=GUARD/"baseline.json" +RESULT=GUARD/"rollback-result.json" +WORKER=GUARD/"worker.py" +ROOT_TX=GUARD/"root_tx.py" +USER_TX=GUARD/"user_tx.py" +UNIT="directinternetmethod-upgrade-rollback" +SERVICE=Path("/etc/systemd/system")/(UNIT+".service") +TIMER=Path("/etc/systemd/system")/(UNIT+".timer") +USER_REL=".local/share/DirectInternetMethod" +PENDING_REL=".local/state/DirectInternetMethod/update-pending" +RUNNER_OWNER="runner" + +def need(ok:bool,label:str): + if not ok:raise RuntimeError("R552_GUARD_FAIL_CLOSED_"+label) + +def sha_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 regular(path:Path,uid=None,mode=None): + need(path.is_file() and not path.is_symlink(),"REGULAR_"+path.name) + s=path.stat() + if uid is not None:need(s.st_uid==uid,"OWNER_"+path.name) + if mode is not None:need(stat.S_IMODE(s.st_mode)==mode,"MODE_"+path.name) + return s + +def canonical(obj):return (json.dumps(obj,indent=2,sort_keys=True)+"\n").encode() + +def command(args,timeout=15): + return subprocess.run(args,text=True,capture_output=True,timeout=timeout,check=False) + +def fsync_dir(path:Path): + fd=os.open(path,os.O_RDONLY|os.O_DIRECTORY) + try:os.fsync(fd) + finally:os.close(fd) + +def write_new(path:Path,data:bytes,mode:int): + need(path.parent.is_dir() and not path.parent.is_symlink(),"WRITER_PARENT") + need(not path.exists() and not path.is_symlink(),"WRITER_EXISTS_"+path.name) + fd=os.open(path,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,mode) + try: + with os.fdopen(fd,"wb") as f: + os.fchmod(f.fileno(),mode) + f.write(data);f.flush();os.fsync(f.fileno()) + fsync_dir(path.parent) + finally: + pass + regular(path,0,mode) + +def checked_owner(): + p=Path("/var/lib/directinternetmethod/install.json") + regular(p,0,0o644) + obj=json.loads(p.read_text(encoding="utf8")) + need(obj.get("schema")==1 and obj.get("version")==BASELINE,"PROTECTED_BASELINE") + uid=obj.get("uid");user=obj.get("user");home=obj.get("home") + need(type(uid) is int and 0 Date: Fri, 16 Oct 2026 00:59:41 +0330 Subject: [PATCH 029/188] feat(linux): R553 arm owned independent rollback guard before protected upgrade --- .github/workflows/ci.yml | 12 +-- PROJECT_BRAIN.md | 2 + build_linux_package.py | 1 + linux/app/user_update_transaction.py | 2 +- .../system/upgrade_guard.py | 5 +- linux/system/upgrade_system.sh | 75 +++++++++++++++++++ tests/r519_versioned_package_contract.py | 15 ++-- ...r528_ephemeral_native_root_cli_contract.py | 6 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 4 +- 11 files changed, 104 insertions(+), 22 deletions(-) rename tests/r552_native_upgrade_guard_candidate.py => linux/system/upgrade_guard.py (98%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 549ffba3..3b753bfb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,16 +39,16 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70" - assert p.is_file() and p.stat().st_size==8340334 + expected="E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805" + assert p.is_file() and p.stat().st_size==8360302 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" with zipfile.ZipFile(p) as z: root="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" - assert len(z.namelist())==28 and len(z.namelist())==len(set(z.namelist())) + assert len(z.namelist())==29 and len(z.namelist())==len(set(z.namelist())) j=json.loads(z.read(root+"PACKAGE_MANIFEST.json")) - assert j["version"]=="1.5.5-rc1" and len(j["files"])==26 + assert j["version"]=="1.5.5-rc1" and len(j["files"])==27 for x in j["files"]: data=z.read(root+x["file"]) assert len(data)==x["bytes"] @@ -144,7 +144,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '27d5289236a9cdb125669e67269ef16a7c6fcd74ae9300e65b0c8aa2e10b5f70' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -173,7 +173,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '27d5289236a9cdb125669e67269ef16a7c6fcd74ae9300e65b0c8aa2e10b5f70' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 9a87d7e3..5f45c0d7 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,6 +2,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . +**Current candidate R553 (2026-10-16), unreleased, not physically installed:** the Linux protected upgrade wrapper now arms an independent root-owned systemd rollback timer before protected Stage/Apply, verifies exact owned guard/User stage/Root source SHA, and refuses an unguarded upgrade. New Root/User watchdog is part of the single Linux RC1 package: **27 payload members + manifest/checksums**, SHA256 `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805` (8,360,302 bytes). On physical MMZ the exact archive was reconstructed byte-identically in memory from old R545 plus SHA-pinned replacement sources with no installed/network change. Guard source SHA256 `8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43`, User transaction SHA256 `EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66`, wrapper SHA256 `E92955EBAEDF03F17D2E89A79B61BDCD393D8C6BED6B5795561C6FC1BD87BDDB`. Pending exact-head CI/native testing and physical authorized Root+GUI acceptance; **no public release promotion**, v1.5.4 Stable unchanged. The R545 ZIP `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` is superseded for new canaries; historical QA retained only as provenance. + **Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. The new private R545 ZIP was exclusively staged on physical MMZ as `R541_QA/R545_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600, pinned SHA), with original Root 1.5.2/User 1.5.4, no upgrade slots, route/DNS/Tailscale unchanged; unique receipt `R541_QA/R545_PRIVATE_READY.json` SHA256 `AD78F42CEE5E74F61EF57DE83943F1059900F29F7DED43B21C741EAF79695FC9`. Private staging is NOT installation, permission or DoH consent. **Verification scope:** Six extracted-method negative-first tests PASS on MMZ; seven additional real GTK4 timer/route tests PASS in isolated loopback Broadway and synthetic HOME, with unchanged physical installation, Tailscale, routes and DNS (receipt `R541_QA/R545_GTK_TIMER_RECEIPT.json`). Exact source+CI-metadata head `5b7293cb7acba0f0c76d5a6c9cf5629bc73581a9` passed hosted Push CI [38795817931](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38795817931) and PR CI [38795826936](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38795826936), including Linux package SHA/size, new R545 6/6, R541 11/11, 1.5.4 regression, protected Root, Root/User native disposable upgrade and independent watchdog, Windows source and private Windows native v1.5.4→RC1→v1.5.4 rollback. Public unsigned Windows release job was intentionally SKIPPED (not release acceptance). The exact-run Windows private installer SHA256 was `A1D17585067D46953067AA62A3D4DA6BC9BE56EDB1C6E86A22D381885E809DC1` and its native acceptance receipt SHA256 was `89FC5BAC118EDCA3F60A606EA48BFD79E9CB9E9E484DE007FE16D143DEB4EA77`; these cannot authenticate a different run's artifact. diff --git a/build_linux_package.py b/build_linux_package.py index c1d8eba4..e775b0aa 100644 --- a/build_linux_package.py +++ b/build_linux_package.py @@ -14,6 +14,7 @@ ("linux/system/encrypted_selective_dns.py","system/encrypted_selective_dns.py"), ("linux/system/encrypted_selective_policy.py","system/encrypted_selective_policy.py"), ("linux/system/protected_upgrade_transaction.py","system/protected_upgrade_transaction.py"), + ("linux/system/upgrade_guard.py","system/upgrade_guard.py"), ("linux/system/upgrade_system.sh","system/upgrade_system.sh"), ("linux/system/uninstall_system.sh","system/uninstall_system.sh"), ("linux/app/direct_internet_method.py","app/direct_internet_method.py"), diff --git a/linux/app/user_update_transaction.py b/linux/app/user_update_transaction.py index cf1e181e..3d88ca50 100644 --- a/linux/app/user_update_transaction.py +++ b/linux/app/user_update_transaction.py @@ -154,7 +154,7 @@ def source_files(root: pathlib.Path) -> dict: package_doc.get("version") == "1.5.5-rc1" and package_doc.get("platform") == "linux-x86_64" and isinstance(package_doc.get("files"), list) and - len(package_doc["files"]) == 26, "EXTRACTED_PACKAGE_IDENTITY_WRONG") + len(package_doc["files"]) == 27, "EXTRACTED_PACKAGE_IDENTITY_WRONG") seen = set() digest_lines = [] for item in package_doc["files"]: diff --git a/tests/r552_native_upgrade_guard_candidate.py b/linux/system/upgrade_guard.py similarity index 98% rename from tests/r552_native_upgrade_guard_candidate.py rename to linux/system/upgrade_guard.py index d17406c6..86a8bedc 100644 --- a/tests/r552_native_upgrade_guard_candidate.py +++ b/linux/system/upgrade_guard.py @@ -26,8 +26,7 @@ BASELINE="1.5.2" OLD_USER="1.5.4" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" -PIN_USER="A909BBB12B78875A9438C756AE2DCAFF05E0DF2EBB5551E97544D2B20C46346F" -PIN_PKG="27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70" +PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" RESULT=GUARD/"rollback-result.json" @@ -119,7 +118,7 @@ def validate_source(source:Path): data=json.loads(manifest.read_text("utf8")) files=data.get("files") need(data.get("version")==VERSION and data.get("schema")==1 and - isinstance(files,list) and len(files)==26,"PACKAGE_MANIFEST") + isinstance(files,list) and len(files)==27,"PACKAGE_MANIFEST") pin={x.get("file"):x.get("sha256") for x in files if isinstance(x,dict)} for label,relative,want in ( ("ROOT_TX","system/protected_upgrade_transaction.py",PIN_ROOT), diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 699eed0d..d8dfdec1 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -37,6 +37,81 @@ m=json.loads(p.read_text('utf8')) assert m['schema']==1 and m['version']=='1.5.2' and m['uid']==expected assert m['home'].startswith('/home/') PY +# Arm the separately root-owned boot-persistent R553 guard BEFORE any +# protected Root Stage/Apply. Source identity is a frozen independent SHA; +# a signed-in OS administrator still authenticates through the installer. +GUARD_SHA="8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43" +[ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { + echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; +} +[ "$(sha256sum "$SRC/system/upgrade_guard.py" | cut -d' ' -f1)" = \ + "$(printf '%s' "$GUARD_SHA" | tr '[:upper:]' '[:lower:]')" ] || { + echo "R553_GUARD_SOURCE_SHA_CHANGED" >&2; exit 87; +} +python3 "$SRC/system/upgrade_guard.py" arm --source "$SRC" --uid "$REQ_UID" +# R553: Do not touch root-owned binaries/metadata without a separately +# activated, root-owned two-sided timed rollback guard. A user-scoped backup +# from install.sh is reversible; it is NOT a protected autonomous watchdog. +python3 - "$REQ_UID" "$TX_SHA" <<'PY' +import hashlib,json,os,pathlib,stat,subprocess,sys +uid=int(sys.argv[1]); root_sha=sys.argv[2].upper() +unit="directinternetmethod-upgrade-rollback" +guard=pathlib.Path("/var/lib/directinternetmethod/upgrade-guard") +root=pathlib.Path("/var/lib/directinternetmethod/install.json") +try: + assert guard.is_dir() and not guard.is_symlink() + gs=guard.stat() + assert gs.st_uid==0 and stat.S_IMODE(gs.st_mode)==0o711 + paths={"baseline.json":0o600,"worker.py":0o500,"root_tx.py":0o500,"user_tx.py":0o644} + for name,mode in paths.items(): + p=guard/name + assert p.is_file() and not p.is_symlink() + st=p.stat() + assert st.st_uid==0 and stat.S_IMODE(st.st_mode)==mode + def sha(path): + h=hashlib.sha256() + with path.open("rb") as inp: + for b in iter(lambda:inp.read(1048576),b""):h.update(b) + return h.hexdigest().upper() + metadata=json.loads(root.read_text("utf8")) + home=pathlib.Path(metadata["home"]) + data=json.loads((guard/"baseline.json").read_text("utf8")) + assert data["schema"]==1 and data["revision"]=="R552" + assert data["uid"]==uid and metadata["uid"]==uid and data["home"]==str(home) + assert data["versionFromRoot"]=="1.5.2" and data["versionFromUser"]=="1.5.4" + assert data["versionTo"]=="1.5.5-rc1" + assert data["oldRootMetaSha"]==sha(root) + assert data["oldUserMetaSha"]==sha(home/".local/share/DirectInternetMethod/INSTALL.json") + assert data["rootTransactionSha"]==root_sha + assert sha(guard/"root_tx.py")==root_sha + assert sha(guard/"user_tx.py")=="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" + assert data["userTransactionSha"]==sha(guard/"user_tx.py") + assert data["workerSha"]==sha(guard/"worker.py") + pending=home/".local/state/DirectInternetMethod/update-pending" + assert pending.is_dir() and not pending.is_symlink() and pending.stat().st_uid==uid + pm=pending/"manifest.json" + assert pm.is_file() and not pm.is_symlink() and pm.stat().st_uid==uid + stage=json.loads(pm.read_text("utf8")) + assert stage["schema"]==1 and stage["version"]=="1.5.5-rc1" + assert stage["previousVersion"]=="1.5.4" + old=stage["old"]["INSTALL.json"] + assert old["exists"] is True and str(old["sha"]).upper()==data["oldUserMetaSha"] + for ext in (".service",".timer"): + p=pathlib.Path("/etc/systemd/system")/(unit+ext) + assert p.is_file() and not p.is_symlink() + st=p.stat() + assert st.st_uid==0 and stat.S_IMODE(st.st_mode)==0o644 + res=subprocess.run(["/usr/bin/systemctl","show",unit+".timer", + "--property=ActiveState,LoadState,UnitFileState,Triggers","--no-pager"], + text=True,capture_output=True,timeout=6,check=False) + assert res.returncode==0 and all(s in res.stdout for s in + ("ActiveState=active","LoadState=loaded","UnitFileState=enabled", + unit+".service")) +except Exception as ex: + print("R553_ROOT_GUARD_NOT_ARMED_FAIL_CLOSED "+type(ex).__name__,file=sys.stderr) + sys.exit(87) +print("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED") +PY # Transactional stage + apply obtain native action lock; one rollback slot. python3 "$SRC/system/protected_upgrade_transaction.py" stage --source "$SRC" python3 "$SRC/system/protected_upgrade_transaction.py" apply diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 73441bd5..54f61380 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":26,"sha256":"27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70"} +EXPECTED={"version":VERSION,"files":27,"sha256":"E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -54,7 +54,8 @@ def test_versioned_components_present(self): included={r["file"] for r in self.rows} self.assertTrue({"system/encrypted_selective_dns.py","system/encrypted_selective_policy.py", "system/protected_upgrade_transaction.py","system/upgrade_system.sh", - "app/direct_method_helper.sh","app/user_update_transaction.py"}.issubset(included)) + "app/direct_method_helper.sh","app/user_update_transaction.py", + "system/upgrade_guard.py"}.issubset(included)) def test_r519b_uninstaller_accepts_only_new_ownership_version(self): rel="app/user_uninstall_preserve.py" got=self.info.read(self.prefix+rel) @@ -79,7 +80,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual((prev["schema"],prev["version"],prev["platform"]),(1,VERSION,"linux-x86_64")) before={row["file"]:row for row in prev["files"]} after={row["file"]:row for row in self.rows} - self.assertEqual(set(before),set(after)) + self.assertEqual(set(after),set(before)|{"system/upgrade_guard.py"}) self.assertEqual(len(before),26) changed={name for name in before if before[name]!=after[name]} self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", @@ -106,7 +107,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual(digest(old.read_bytes()), "47776640A1BB8B5D4490244565DDD47A6A817A999347566917CC73E21F41EC24") with zipfile.ZipFile(old) as z: - self.assertEqual(set(z.namelist()),set(self.n)) + self.assertEqual(set(self.n),set(z.namelist())|{self.prefix+"system/upgrade_guard.py"}) self.assertEqual(z.read(self.prefix+"PACKAGE_MANIFEST.json"),old_manifest.read_bytes()) self.assertEqual(z.read(self.prefix+"app/user_uninstall_preserve.py"),old_source.read_bytes()) self.assertEqual(z.testzip(),None) @@ -131,6 +132,10 @@ def test_root_wrapper_pinned_identity(self): src=(R/"linux/system/upgrade_system.sh").read_text() self.assertIn(digest(ROOT_TXN.read_bytes()),src) self.assertIn('DirectInternetMethod_1.5.5-rc1_Linux_x86_64',src) + self.assertIn("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED",src) + self.assertIn("GUARD_SHA=",src) + self.assertLess(src.index("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED"), + src.index('python3 "$SRC/system/protected_upgrade_transaction.py" stage')) self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" stage',src) self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" apply',src) self.assertNotIn(' commit',src) @@ -146,7 +151,7 @@ def test_gui_and_user_metadata_pins(self): self.assertIn('COEXIST_HELPER_SHA256="'+digest(HELPER.read_bytes()).lower()+'"',app) self.assertIn('installed.get("version") == "1.5.5-rc1"',app) updater=USER_TXN.read_text() - self.assertIn('len(package_doc["files"]) == 26',updater) + self.assertIn('len(package_doc["files"]) == 27',updater) self.assertIn('data.get("version") == "1.5.5-rc1"',updater) self.assertIn('"encrypted-dns-optin.txt"',updater) def test_rc_semver_prevents_implicit_downgrade(self): diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 0bec4c7d..638fd709 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="27d5289236a9cdb125669e67269ef16a7c6fcd74ae9300e65b0c8aa2e10b5f70" +CANDIDATE_SHA="e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" @@ -121,9 +121,9 @@ def stage_exact_candidate(candidate_zip:Path,temp:Path)->Path: need(not extract.exists() and not extract.is_symlink(),"TEMP_SOURCE_COLLISION") extract.mkdir(mode=0o700) with zipfile.ZipFile(candidate_zip) as z: - archive_verify(z,CANDIDATE_PREFIX,28) + archive_verify(z,CANDIDATE_PREFIX,29) manifest=json.loads(z.read(CANDIDATE_PREFIX+"PACKAGE_MANIFEST.json")) - need(manifest.get("version")=="1.5.5-rc1" and len(manifest.get("files",[]))==26, + need(manifest.get("version")=="1.5.5-rc1" and len(manifest.get("files",[]))==27, "RC1_PACKAGE_VERSION_MANIFEST") for f in manifest["files"]: relative=f["file"] diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 228696e8..25158a2c 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -37,7 +37,7 @@ TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" ROOT_SHA = "eb107164ccede891c8bf41c06ff422f1b53b59d2d2ddf0b3dd23ebfe5de9601b" -USER_SHA = "a909bbb12b78875a9438c756ae2dcaff05e0df2ebb5551e97544d2b20c46346f" +USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def sha(p:Path)->str: h=hashlib.sha256() diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 1db4d031..7ae200d4 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -26,7 +26,7 @@ ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" PIN_ROOT = "eb107164ccede891c8bf41c06ff422f1b53b59d2d2ddf0b3dd23ebfe5de9601b" -PIN_USER = "a909bbb12b78875a9438c756ae2dcaff05e0df2ebb5551e97544d2b20c46346f" +PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def digest(path:Path): h=hashlib.sha256() diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 4a8075e0..48918330 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest -SRC=pathlib.Path(__file__).with_name("r552_native_upgrade_guard_candidate.py") -SOURCE_SHA="A69875ABBB8A297B3C7A83A8B5C3A3455746AB7B63254DA7255F7B37210BC4E2" +SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" +SOURCE_SHA="8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) From dfff9fe9eea3062c6737738cd08a9e5785c51026 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:05:00 +0330 Subject: [PATCH 030/188] test(linux): track R553 User transaction as accepted package delta --- tests/r519_versioned_package_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 54f61380..139f66c9 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -84,7 +84,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual(len(before),26) changed={name for name in before if before[name]!=after[name]} self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", - "app/direct_method_helper.sh","system/protected_upgrade_transaction.py", + "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], "732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8") From f09d87542b8c1324c4958b9c1f1efa3aec797f49 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:08:44 +0330 Subject: [PATCH 031/188] test(linux): native R554 two-sided watchdog acceptance on disposable Ubuntu systemd --- .github/workflows/ci.yml | 29 +++ tests/r554_native_guard_ephemeral_contract.py | 200 ++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 tests/r554_native_guard_ephemeral_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b753bfb..380da2e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,6 +194,35 @@ jobs: set -euo pipefail sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r532_ephemeral_root_watchdog_contract.py verify + linux-rc1-native-guard-ephemeral: + name: linux-rc1-native-guard-ephemeral + needs: linux-independent-root-watchdog-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R554 byte-pinned exact 27-file Linux candidate + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + - name: R554 download exact public v1.5.4 baseline + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R554 native root-owned systemd guard then exact two-sided rollback + shell: bash + run: | + set -euo pipefail + # Disposable hosted GitHub Ubuntu VM only, no human host mutations. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py new file mode 100644 index 00000000..42082f53 --- /dev/null +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +"""R554 exact product upgrade-guard native disposable systemd lifecycle proof. + +Never execute outside an *empty* GitHub-hosted ephemeral Ubuntu VM. Uses +official SHA-pinned v1.5.4 baseline and exact 27-file 1.5.5 RC archive. +Stages real unprivileged user data, arms actual root-owned systemd timer BEFORE +any protected modification; invokes the protected root stage/apply, applies User +code, verifies exact installer inline metadata, triggers the separately owned +systemd SERVICE (not a fake Python callback) and checks two-sided byte-exact +restoration. Native monotonic timer expiration is independently proven by R532. +No network/DNS/Tailscale/GUI Start/Stop action; not production acceptance. +""" +from __future__ import annotations +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import pwd +import stat +import subprocess +import sys +import time + +R=Path(__file__).resolve().parents[1] +R528=R/"tests/r528_ephemeral_native_root_cli_contract.py" +R529=R/"tests/r529_ephemeral_root_user_transaction.py" +R526=R/"tests/r526_installer_metadata_contract.py" +ROOT=R/"linux/system/protected_upgrade_transaction.py" +GUARD=R/"linux/system/upgrade_guard.py" +PIN_PACKAGE="E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805" +PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" +PIN_GUARD="8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43" +PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" +NAME="directinternetmethod-upgrade-rollback" +SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") + +def need(ok,why): + if not ok:raise RuntimeError("R554_NATIVE_GUARD_FAIL_CLOSED_"+why) + +def sha(path): + h=hashlib.sha256() + with Path(path).open("rb") as f: + for piece in iter(lambda:f.read(1048576),b""):h.update(piece) + return h.hexdigest().upper() + +def module(path,modname): + spec=importlib.util.spec_from_file_location(modname,path) + need(spec is not None and spec.loader is not None,"IMPORT_"+modname) + obj=importlib.util.module_from_spec(spec) + spec.loader.exec_module(obj) + return obj + +def command(args,seconds=25): + proc=subprocess.run(args,capture_output=True,text=True,check=False,timeout=seconds) + need(proc.returncode==0,"COMMAND_"+str(proc.returncode)+"_"+proc.stderr[-200:].replace(" ","_")) + return proc.stdout.strip() + +def preflight_vm(): + need(os.geteuid()==0 and sys.platform.startswith("linux"),"NATIVE_ROOT") + need(os.environ.get("GITHUB_ACTIONS")=="true" and os.environ.get("CI")=="true" + and os.environ.get("RUNNER_ENVIRONMENT")=="github-hosted" + and os.environ.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod" + and Path(os.environ.get("GITHUB_WORKSPACE","")).resolve()==R.resolve(), + "GITHUB_EPHEMERAL_ONLY") + need(Path("/proc/1/comm").read_text().strip()=="systemd","SYSTEMD_PID1") + runner=pwd.getpwnam("runner") + need(runner.pw_dir=="/home/runner" and runner.pw_uid>0,"RUNNER_IDENTITY") + for path in ("/usr/lib/directinternetmethod", + "/var/lib/directinternetmethod", + "/etc/directinternetmethod.uid", + "/etc/systemd/system/"+NAME+".service", + "/etc/systemd/system/"+NAME+".timer", + "/home/runner/.local/share/DirectInternetMethod", + "/home/runner/.local/state/DirectInternetMethod", + "/run/directinternetmethod-encrypted"): + p=Path(path) + need(not p.exists() and not p.is_symlink(),"FOREIGN_PREEXISTING_"+p.name) + need(sha(ROOT)==PIN_ROOT and sha(GUARD)==PIN_GUARD,"SOURCE_SHA_DRIFT") + need(sha(R/"linux/app/user_update_transaction.py")==PIN_USER,"USER_SOURCE_SHA_DRIFT") + return runner + +def run(): + runner=preflight_vm() + r528=module(R528,"r554_r528") + r529=module(R529,"r554_r529") + r526=module(R526,"r554_r526") + rootmod=r528.module() + guard=module(GUARD,"r554_real_guard") + stable,package,temp,user=r528.prerequisites() + need(user.pw_uid==runner.pw_uid,"USER_CHANGED") + need(sha(package)==PIN_PACKAGE,"PINNED_CANDIDATE") + r529.require_clean_home(user) + extracted=r528.stage_exact_candidate(package,temp) + r529.make_source_runner_owned(extracted,user) + # Installed user and root bytes come only from the public v1.5.4 ZIP. + app,original_user,private=r529.prepare_user_from_official(stable,user) + protected=r528.seed_real_protected_root(stable,user,rootmod) + baseline_network={ + p:sha(p) for p in ("/etc/resolv.conf","/etc/hosts") + } + before_link=(os.readlink("/etc/resolv.conf") + if Path("/etc/resolv.conf").is_symlink() else None) + before_user=sha(app/"INSTALL.json") + before_root=sha("/var/lib/directinternetmethod/install.json") + tx_user=extracted/"app/user_update_transaction.py" + need(sha(tx_user)==PIN_USER,"EXTRACTED_USER_SHA") + need(sha(extracted/"system/upgrade_guard.py")==PIN_GUARD,"EXTRACTED_GUARD_SHA") + need(sha(extracted/"system/protected_upgrade_transaction.py")==PIN_ROOT,"EXTRACTED_ROOT_SHA") + root_tx=rootmod.RootTransaction() + need(root_tx.native and root_tx.status()["status"]=="NO_PENDING_UPGRADE","ROOT_PRESTATE") + # Unprivileged stage creates only the bounded user rollback snapshot. + r529.as_runner(user,tx_user,"--stage","--source",extracted) + pending=Path("/home/runner/.local/state/DirectInternetMethod/update-pending") + need((pending/"manifest.json").is_file(),"USER_STAGE") + r529.unchanged_records(original_user) + r529.unchanged_records(private) + # The exact installed native watchdog must be armed while Root is OLD. + armed=guard.arm(extracted,user.pw_uid) + need(armed["status"]=="ARMED_INDEPENDENT_ROOT_USER_WATCHDOG","WATCHDOG_ARM") + present=guard.verify_timer() + need(present["uid"]==user.pw_uid and present["workerSha"]==sha(guard.WORKER), + "SYSTEMD_TIMER_GUARD") + need(root_tx.status()["status"]=="NO_PENDING_UPGRADE","ROOT_MUTATED_BEFORE_ARM") + # The true production Root transaction now changes protected code. + need(root_tx.stage(extracted)["status"]=="STAGED","ROOT_STAGE") + need(root_tx.apply()["status"]=="APPLIED","ROOT_APPLY") + need(root_tx.status()["status"]=="APPLIED","ROOT_PENDING") + r529.as_runner(user,tx_user,"--apply","--source",extracted) + manifest=json.loads((pending/"manifest.json").read_text()) + for rel,row in manifest["managed"].items(): + need(r529.hash_file(app/rel)==row["sha"],"USER_APPLY_SHA_"+rel) + r529.unchanged_records(private) + # Run the exact, unchanged production installer INSTALL.json fragment. + install=(extracted/"install.sh").read_text(encoding="utf8") + need(install.count(r526.BEGIN)==1,"INSTALL_METADATA_ANCHOR") + part=install.split(r526.BEGIN,1)[1] + need(part.count(r526.END)==1,"INSTALL_END") + inline=part.split(r526.END,1)[0] + need("transaction_helper.atomic_bytes(" in inline + and len(inline.splitlines())==64,"INSTALL_INLINE_IDENTITY") + r529.as_runner_inline(user,inline,app/"INSTALL.json",tx_user) + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1", + "UPGRADED_USER_VERSION") + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + "UPGRADED_ROOT_VERSION") + need(root_tx.status()["status"]=="APPLIED","ROOT_STILL_PENDING") + # Actual independent root-owned systemd Service, rather than calling + # guard.rollback_worker in-process. R532 separately proves timed firing. + start=time.monotonic() + command(["/usr/bin/systemctl","start",NAME+".service"],100) + need(SEEN.is_file() and not SEEN.is_symlink(),"SYSTEMD_RECEIPT_MISSING") + receipt=json.loads(SEEN.read_text()) + need(receipt.get("status")=="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" + and receipt.get("uid")==0 and receipt.get("userRollback")=="PASS" + and receipt.get("rootRollback")=="PASS" + and receipt.get("recoveryAttempted") is True, + "ROOT_OWNED_SERVICE_NOT_ACCEPTED") + need(root_tx.status()["status"]=="ROLLED_BACK","ROOT_PHASE_NOT_ROLLED_BACK") + need(not pending.exists(),"USER_PENDING_REMAINS") + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.4", + "USER_VERSION_NOT_RESTORED") + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.2", + "ROOT_VERSION_NOT_RESTORED") + need(sha(app/"INSTALL.json")==before_user + and sha("/var/lib/directinternetmethod/install.json")==before_root, + "ORIGINAL_METADATA_SHA_DRIFT") + for target,want in original_user.items(): + need(sha(target).lower()==want,"USER_FILE_RESTORATION_"+Path(target).name) + r529.unchanged_records(private) + for target,want in protected.items(): + need(sha(target).lower()==want,"PROTECTED_FILE_RESTORATION_"+Path(target).name) + for p,want in baseline_network.items(): + need(sha(p)==want,"NETWORK_FILE_MUTATED") + need(before_link==(os.readlink("/etc/resolv.conf") + if Path("/etc/resolv.conf").is_symlink() else None),"RESOLVER_SYMLINK_MUTATED") + # No reboot, no network/Tailscale manipulation, and no second install. + need(not (app/"directmethod/state.json").exists(),"ACTIVE_ENGINE_UNEXPECTED") + command(["/usr/bin/systemctl","disable","--now",NAME+".timer"]) + print(json.dumps({ + "revision":"R554","status":"PASS_EPHEMERAL_ACTUAL_SYSTEMD_OWNED_GUARD_ROOT_USER_ROLLBACK", + "host":"ephemeral-github-ubuntu", + "nativeProtectedRoot":True,"rootAuthorizedInDisposableVM":True, + "guardArmedBeforeRootMutation":True, + "systemdServiceStartedInSeparateProcess":True, + "timerNativeLoadedAndEnabledBeforeMutation":True, + "rootAndUserByteExactRestoration":True, + "mutatedProductionHost":False,"modifiedRouteDnsOrTailscale":False, + "publishedStablePreserved":"v1.5.4", + "rootFileCount":len(protected), + "userFileCount":len(original_user), + "privateUserFileCount":len(private), + "serviceDurationSeconds":round(time.monotonic()-start,3), + "watchdogReceiptSHA256":sha(SEEN)},sort_keys=True)) +if __name__=="__main__": + try:run() + except BaseException as e: + print(json.dumps({"revision":"R554","status":"FAIL_CLOSED", + "reason":str(e)[:500]}),file=sys.stderr) + sys.exit(94) From 846dfba6a35f27382d10608ffd93806c5650002a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:11:58 +0330 Subject: [PATCH 032/188] test(linux): R555 exact production upgrade wrapper + independent two-sided rollback --- .github/workflows/ci.yml | 29 +++++++++++++++++++ tests/r554_native_guard_ephemeral_contract.py | 26 ++++++++++++----- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 380da2e9..25a6e729 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,6 +223,35 @@ jobs: # Disposable hosted GitHub Ubuntu VM only, no human host mutations. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-rc1-native-wrapper-ephemeral: + name: linux-rc1-native-wrapper-ephemeral + needs: linux-rc1-native-guard-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R555 rebuild exact byte-pinned Linux RC1 + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + - name: R555 pin the official stable baseline from its immutable release + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R555 native production upgrade wrapper arms actual two-sided Root User rollback + shell: bash + run: | + set -euo pipefail + # A DIFFERENT, empty GitHub-hosted Ubuntu VM; never production host. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 42082f53..a2010d4a 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -115,16 +115,27 @@ def run(): need((pending/"manifest.json").is_file(),"USER_STAGE") r529.unchanged_records(original_user) r529.unchanged_records(private) - # The exact installed native watchdog must be armed while Root is OLD. - armed=guard.arm(extracted,user.pw_uid) - need(armed["status"]=="ARMED_INDEPENDENT_ROOT_USER_WATCHDOG","WATCHDOG_ARM") + # Two separate disposable-VM paths: original direct product transaction + # and the ACTUAL production upgrade_system.sh wrapper and auto-arm. + # Both must install the same root-owned timer and restore exact baselines. + native_wrapper=os.environ.get("DIM_R555_EXACT_WRAPPER")=="true" + need(root_tx.status()["status"]=="NO_PENDING_UPGRADE","ROOT_MUTATED_BEFORE_ARM") + if native_wrapper: + wrapper=extracted/"system/upgrade_system.sh" + need(wrapper.is_file() and not wrapper.is_symlink(),"EXACT_WRAPPER") + out=command(["/bin/bash",str(wrapper),str(extracted),str(user.pw_uid)],80) + need("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED" in out + and "ROOT_R519_APPLIED_UNCOMMITTED" in out, + "PRODUCTION_WRAPPER_GUARD_ACCEPTANCE") + else: + armed=guard.arm(extracted,user.pw_uid) + need(armed["status"]=="ARMED_INDEPENDENT_ROOT_USER_WATCHDOG","WATCHDOG_ARM") + need(root_tx.status()["status"]=="NO_PENDING_UPGRADE","ROOT_MUTATED_BEFORE_ARM") + need(root_tx.stage(extracted)["status"]=="STAGED","ROOT_STAGE") + need(root_tx.apply()["status"]=="APPLIED","ROOT_APPLY") present=guard.verify_timer() need(present["uid"]==user.pw_uid and present["workerSha"]==sha(guard.WORKER), "SYSTEMD_TIMER_GUARD") - need(root_tx.status()["status"]=="NO_PENDING_UPGRADE","ROOT_MUTATED_BEFORE_ARM") - # The true production Root transaction now changes protected code. - need(root_tx.stage(extracted)["status"]=="STAGED","ROOT_STAGE") - need(root_tx.apply()["status"]=="APPLIED","ROOT_APPLY") need(root_tx.status()["status"]=="APPLIED","ROOT_PENDING") r529.as_runner(user,tx_user,"--apply","--source",extracted) manifest=json.loads((pending/"manifest.json").read_text()) @@ -184,6 +195,7 @@ def run(): "guardArmedBeforeRootMutation":True, "systemdServiceStartedInSeparateProcess":True, "timerNativeLoadedAndEnabledBeforeMutation":True, + "productionShellUpgradeWrapperExecuted":native_wrapper, "rootAndUserByteExactRestoration":True, "mutatedProductionHost":False,"modifiedRouteDnsOrTailscale":False, "publishedStablePreserved":"v1.5.4", From 345572e8d3b4c983a872c1aa54979b3176338fe6 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:19:35 +0330 Subject: [PATCH 033/188] docs(brain): upsert R554-R556 native guard, exact MMZ staging and persistent-commit gate --- PROJECT_BRAIN.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 5f45c0d7..f29f7966 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,11 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R553 (2026-10-16), unreleased, not physically installed:** the Linux protected upgrade wrapper now arms an independent root-owned systemd rollback timer before protected Stage/Apply, verifies exact owned guard/User stage/Root source SHA, and refuses an unguarded upgrade. New Root/User watchdog is part of the single Linux RC1 package: **27 payload members + manifest/checksums**, SHA256 `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805` (8,360,302 bytes). On physical MMZ the exact archive was reconstructed byte-identically in memory from old R545 plus SHA-pinned replacement sources with no installed/network change. Guard source SHA256 `8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43`, User transaction SHA256 `EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66`, wrapper SHA256 `E92955EBAEDF03F17D2E89A79B61BDCD393D8C6BED6B5795561C6FC1BD87BDDB`. Pending exact-head CI/native testing and physical authorized Root+GUI acceptance; **no public release promotion**, v1.5.4 Stable unchanged. The R545 ZIP `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` is superseded for new canaries; historical QA retained only as provenance. +**Current candidate R553 (2026-10-16) — source+hosted acceptance; PHYSICAL OPEN:** v1.5.5-rc1 remains private Draft PR #60. The one-install Linux ZIP now contains 27 payload files (29 total members); exact deterministic SHA256 `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805`, 8,360,302 bytes, originating from product commit `1201c73723075142f46c5e0c14e8561bc5926bbe`. The product-owned `upgrade_system.sh` arms a separate root-owned systemd timed Root+User rollback worker **before** protected Stage/Apply and fails closed on missing/foreign guard. Guard source SHA256 `8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43`, User transaction SHA256 `EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66`, wrapper SHA256 `E92955EBAEDF03F17D2E89A79B61BDCD393D8C6BED6B5795561C6FC1BD87BDDB`. The previous R545 private SHA `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` is superseded for NEW RC1 canaries, retained only as evidence for R545–R547. + +**R554/R555 hosted native guard acceptance:** At exact source+test head `846dfba6a35f27382d10608ffd93806c5650002a`, both [push CI 38811848360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811848360) and [PR CI 38811854504](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811854504) finished **SUCCESS**. An actual fresh disposable GitHub Ubuntu VM with UID 0 confirmed independent root-owned systemd timer/service armed before protected mutation, Root 1.5.2/User 1.5.4 → RC1 upgraded, and a separately run root-owned systemd service restored Root+User byte-for-byte; official stable source, private mutable user settings and resolver/hosts remained SHA-identical, with no production host touched. R555 additionally executed the **unmodified production `upgrade_system.sh` entrypoint**, rather than merely invoking RootTransaction directly: true native wrapper+auto-arm and rollback PASS on both hosted runs. R532 independently confirms monotonic systemd timer expiry; R554/R555 trigger the exact R553 worker through its native systemd service but do **not** prove exact 480s timer expiry or reboot timing on a physical computer. Windows native private v1.5.4→RC1→v1.5.4 PASS at exact push, Windows setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; public release still unchanged. + +**R556 private physical MMZ staging:** Exact R553 Linux ZIP was exclusively created on physical MMZ at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R553_PRIVATE_1.5.5-rc1_Linux_x86_64.zip`, 0600 with full hash and manifest validated; unique receipt `R541_QA/R556_PRIVATE_R553_READY.json` SHA256 `466A99A4FB3DDCEA432C2C2B9F7DF5B2422D773AA1CEAB9F7697C6C290D5F337`. Root v1.5.2/User v1.5.4, DNS, routes, Tailscale and user configuration remained unchanged. This is **private staging only**, not Root authorization, user DoH consent, real installation or network acceptance. **Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. The new private R545 ZIP was exclusively staged on physical MMZ as `R541_QA/R545_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600, pinned SHA), with original Root 1.5.2/User 1.5.4, no upgrade slots, route/DNS/Tailscale unchanged; unique receipt `R541_QA/R545_PRIVATE_READY.json` SHA256 `AD78F42CEE5E74F61EF57DE83943F1059900F29F7DED43B21C741EAF79695FC9`. Private staging is NOT installation, permission or DoH consent. @@ -20,7 +24,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. -**Critical dependency chain:** independent authorized production-host Root+User rollback worker NOT armed; no physical RC1 native install/activation and GUI Start→DoH/HTTPS→Stop/Recovery/rollback with full Tailscale and physical-DNS invariants. A previous attempt to create the physical rollback guard was denied by host safety policy: do not bypass. Keep PR Draft and public v1.5.4 as sole Stable; do not treat hosted/isolated PASS as physical FINAL. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. +**Critical dependency chain:** R553 independent Root/User guard and real production wrapper are now native-hosted **PASS**, but the exact worker is **NOT ARMED ON ANY USER PRODUCTION HOST**. Root privilege on MMZ still requires its own legitimate administrator authentication; previous physical guard attempt was denied by host safety policy and must not be bypassed. Real RC1 protected install, GUI Start→DoH/HTTPS→Stop/Recovery, Tailscale/physical-DNS invariants, reboot behavior, and an accepted **coordinated two-sided persistent Root/User commit** are OPEN. RootTransaction.commit and UserTransaction.commit currently have separate irreversible phase/backup operations, and hosted rollback PASS does not prove interruption-safe final acceptance/cleanup. Do not merge, create tag, publish v1.5.5, or claim whole-project FINAL until native physical acceptance plus safe promotion/rollback evidence. Public v1.5.4 Stable remains unchanged. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. **Authority map:** this current `PROJECT_BRAIN.md` notice is the source-branch singleton Index/Brain/State locator, with unique GitHub PR/CI and physical QA receipts as evidence links. The historical R0 lineage below and `BRAIN_R*.zip` snapshots remain provenance, not competing active authorities. Earlier Candidate/Final/signing-HOLD statements are superseded where conflicting. The owner’s DoD remains one-install in-place, preserved settings, verified rollback, active Linux/Windows runtime and a single trustworthy public release line. From a50cca24341b8e05fce70550c19f5ca81d79bb18 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:21:50 +0330 Subject: [PATCH 034/188] docs(brain): upsert R557 exact R553 physical shadow User rollback receipt --- PROJECT_BRAIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index f29f7966..c72df120 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,7 +6,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R554/R555 hosted native guard acceptance:** At exact source+test head `846dfba6a35f27382d10608ffd93806c5650002a`, both [push CI 38811848360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811848360) and [PR CI 38811854504](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811854504) finished **SUCCESS**. An actual fresh disposable GitHub Ubuntu VM with UID 0 confirmed independent root-owned systemd timer/service armed before protected mutation, Root 1.5.2/User 1.5.4 → RC1 upgraded, and a separately run root-owned systemd service restored Root+User byte-for-byte; official stable source, private mutable user settings and resolver/hosts remained SHA-identical, with no production host touched. R555 additionally executed the **unmodified production `upgrade_system.sh` entrypoint**, rather than merely invoking RootTransaction directly: true native wrapper+auto-arm and rollback PASS on both hosted runs. R532 independently confirms monotonic systemd timer expiry; R554/R555 trigger the exact R553 worker through its native systemd service but do **not** prove exact 480s timer expiry or reboot timing on a physical computer. Windows native private v1.5.4→RC1→v1.5.4 PASS at exact push, Windows setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; public release still unchanged. -**R556 private physical MMZ staging:** Exact R553 Linux ZIP was exclusively created on physical MMZ at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R553_PRIVATE_1.5.5-rc1_Linux_x86_64.zip`, 0600 with full hash and manifest validated; unique receipt `R541_QA/R556_PRIVATE_R553_READY.json` SHA256 `466A99A4FB3DDCEA432C2C2B9F7DF5B2422D773AA1CEAB9F7697C6C290D5F337`. Root v1.5.2/User v1.5.4, DNS, routes, Tailscale and user configuration remained unchanged. This is **private staging only**, not Root authorization, user DoH consent, real installation or network acceptance. +**R556 private physical MMZ staging:** Exact R553 Linux ZIP was exclusively created on physical MMZ at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R553_PRIVATE_1.5.5-rc1_Linux_x86_64.zip`, 0600 with full hash and manifest validated; unique receipt `R541_QA/R556_PRIVATE_R553_READY.json` SHA256 `466A99A4FB3DDCEA432C2C2B9F7DF5B2422D773AA1CEAB9F7697C6C290D5F337`. Root v1.5.2/User v1.5.4, DNS, routes, Tailscale and user configuration remained unchanged. This is **private staging only**, not Root authorization, user DoH consent, real installation or network acceptance. **R557 new exact-package shadow acceptance:** the staged R553 SHA-pinned ZIP was exercised on physical MMZ in a disposable synthetic HOME using its actual updated 27-member User transaction: user Stage→Apply→metadata attestation→byte-exact Rollback, preserved custom Provider/private settings, rejected concurrent mutable-data drift, no pending backups, **8/8 PASS**, full physical pre/post install/DNS/route/Tailscale unchanged. Unique QA receipt `R541_QA/R557_USER_SHADOW_RECEIPT.json` SHA256 `D7488E1481F125536E6A20666D821A89F4BB87E840EE34C545DC07E751D2D9D8`; source and installed real User v1.5.4/Root v1.5.2 remain unchanged. **Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. The new private R545 ZIP was exclusively staged on physical MMZ as `R541_QA/R545_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600, pinned SHA), with original Root 1.5.2/User 1.5.4, no upgrade slots, route/DNS/Tailscale unchanged; unique receipt `R541_QA/R545_PRIVATE_READY.json` SHA256 `AD78F42CEE5E74F61EF57DE83943F1059900F29F7DED43B21C741EAF79695FC9`. Private staging is NOT installation, permission or DoH consent. From 9faa525c6e87c6428301188d58f1fb1ea0190f5b Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:25:12 +0330 Subject: [PATCH 035/188] docs(brain): accept R558 exact physical rootless guard preflight evidence --- PROJECT_BRAIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index c72df120..4cedceac 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,7 +6,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R554/R555 hosted native guard acceptance:** At exact source+test head `846dfba6a35f27382d10608ffd93806c5650002a`, both [push CI 38811848360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811848360) and [PR CI 38811854504](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811854504) finished **SUCCESS**. An actual fresh disposable GitHub Ubuntu VM with UID 0 confirmed independent root-owned systemd timer/service armed before protected mutation, Root 1.5.2/User 1.5.4 → RC1 upgraded, and a separately run root-owned systemd service restored Root+User byte-for-byte; official stable source, private mutable user settings and resolver/hosts remained SHA-identical, with no production host touched. R555 additionally executed the **unmodified production `upgrade_system.sh` entrypoint**, rather than merely invoking RootTransaction directly: true native wrapper+auto-arm and rollback PASS on both hosted runs. R532 independently confirms monotonic systemd timer expiry; R554/R555 trigger the exact R553 worker through its native systemd service but do **not** prove exact 480s timer expiry or reboot timing on a physical computer. Windows native private v1.5.4→RC1→v1.5.4 PASS at exact push, Windows setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; public release still unchanged. -**R556 private physical MMZ staging:** Exact R553 Linux ZIP was exclusively created on physical MMZ at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R553_PRIVATE_1.5.5-rc1_Linux_x86_64.zip`, 0600 with full hash and manifest validated; unique receipt `R541_QA/R556_PRIVATE_R553_READY.json` SHA256 `466A99A4FB3DDCEA432C2C2B9F7DF5B2422D773AA1CEAB9F7697C6C290D5F337`. Root v1.5.2/User v1.5.4, DNS, routes, Tailscale and user configuration remained unchanged. This is **private staging only**, not Root authorization, user DoH consent, real installation or network acceptance. **R557 new exact-package shadow acceptance:** the staged R553 SHA-pinned ZIP was exercised on physical MMZ in a disposable synthetic HOME using its actual updated 27-member User transaction: user Stage→Apply→metadata attestation→byte-exact Rollback, preserved custom Provider/private settings, rejected concurrent mutable-data drift, no pending backups, **8/8 PASS**, full physical pre/post install/DNS/route/Tailscale unchanged. Unique QA receipt `R541_QA/R557_USER_SHADOW_RECEIPT.json` SHA256 `D7488E1481F125536E6A20666D821A89F4BB87E840EE34C545DC07E751D2D9D8`; source and installed real User v1.5.4/Root v1.5.2 remain unchanged. +**R556 private physical MMZ staging:** Exact R553 Linux ZIP was exclusively created on physical MMZ at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R553_PRIVATE_1.5.5-rc1_Linux_x86_64.zip`, 0600 with full hash and manifest validated; unique receipt `R541_QA/R556_PRIVATE_R553_READY.json` SHA256 `466A99A4FB3DDCEA432C2C2B9F7DF5B2422D773AA1CEAB9F7697C6C290D5F337`. Root v1.5.2/User v1.5.4, DNS, routes, Tailscale and user configuration remained unchanged. This is **private staging only**, not Root authorization, user DoH consent, real installation or network acceptance. **R557 new exact-package shadow acceptance:** the staged R553 SHA-pinned ZIP was exercised on physical MMZ in a disposable synthetic HOME using its actual updated 27-member User transaction: user Stage→Apply→metadata attestation→byte-exact Rollback, preserved custom Provider/private settings, rejected concurrent mutable-data drift, no pending backups, **8/8 PASS**, full physical pre/post install/DNS/route/Tailscale unchanged. Unique QA receipt `R541_QA/R557_USER_SHADOW_RECEIPT.json` SHA256 `D7488E1481F125536E6A20666D821A89F4BB87E840EE34C545DC07E751D2D9D8`; source and installed real User v1.5.4/Root v1.5.2 remain unchanged. **R558 exact R553 physical preflight:** actual production `upgrade_guard.py preflight` from the pinned 27-file private archive executed on MMZ as the ordinary user and returned `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY`; the same source rejected non-Root arming, unknown source layout and altered Root/User transaction bytes. Six checks PASS, network/Tailscale/metadata unchanged; **no privileged operation attempted or guard armed**. Receipt `R541_QA/R558_NATIVE_GUARD_PREFLIGHT.json` SHA256 `4DECFC4711AE1061BE713B63A7962153532A262D60A1F268771255B76718FD14`. **Current candidate R545 (2026-10-15):** v1.5.5-rc1, Draft PR #60, not published. Corrected the route-drift user-interface gap: a bounded GLib 15-second status-only refresh detects transitions to STALE without re-running website probes, changing DNS/routes/services or automatically calling Recovery. A closing window removes its timer; an unverified state fails closed. Source GUI SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8`, updated exact private Linux package 26-file/28-member ZIP SHA256 `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` (8,340,334 bytes), independently rebuilt byte-for-byte in RAM on physical MMZ from exact prior R541 source archive. Old R541 private ZIP SHA `0C5BA7F8DA540412E8417152D0E6FF75F1FFE4ADAB06EE0A1FE72223A94A4839` is historical only, superseded for any NEW canary after this source commit. No installed program was changed. The new private R545 ZIP was exclusively staged on physical MMZ as `R541_QA/R545_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600, pinned SHA), with original Root 1.5.2/User 1.5.4, no upgrade slots, route/DNS/Tailscale unchanged; unique receipt `R541_QA/R545_PRIVATE_READY.json` SHA256 `AD78F42CEE5E74F61EF57DE83943F1059900F29F7DED43B21C741EAF79695FC9`. Private staging is NOT installation, permission or DoH consent. From e14ad473e1eac36208de20e19cd8c817c3fd69cd Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:43:57 +0330 Subject: [PATCH 036/188] fix(linux): R559 persistent UTC rollback deadline survives timer restart and reboot --- .github/workflows/ci.yml | 12 ++++----- PROJECT_BRAIN.md | 2 +- linux/system/upgrade_guard.py | 18 ++++++++++--- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 18 +++++++++++-- tests/r554_native_guard_ephemeral_contract.py | 26 +++++++++++++++++-- 8 files changed, 64 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25a6e729..ad76c45d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805" - assert p.is_file() and p.stat().st_size==8360302 + expected="CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE" + assert p.is_file() and p.stat().st_size==8360956 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -144,7 +144,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -173,7 +173,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -206,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -235,7 +235,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 4cedceac..506f5c5f 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R553 (2026-10-16) — source+hosted acceptance; PHYSICAL OPEN:** v1.5.5-rc1 remains private Draft PR #60. The one-install Linux ZIP now contains 27 payload files (29 total members); exact deterministic SHA256 `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805`, 8,360,302 bytes, originating from product commit `1201c73723075142f46c5e0c14e8561bc5926bbe`. The product-owned `upgrade_system.sh` arms a separate root-owned systemd timed Root+User rollback worker **before** protected Stage/Apply and fails closed on missing/foreign guard. Guard source SHA256 `8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43`, User transaction SHA256 `EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66`, wrapper SHA256 `E92955EBAEDF03F17D2E89A79B61BDCD393D8C6BED6B5795561C6FC1BD87BDDB`. The previous R545 private SHA `27D5289236A9CDB125669E67269EF16A7C6FCD74AE9300E65B0C8AA2E10B5F70` is superseded for NEW RC1 canaries, retained only as evidence for R545–R547. +**Current candidate R559 (2026-10-16) — private RC1, reboot-recovery hardening, pending native acceptance:** The single Linux 1.5.5-rc1 package retains 27 payload files (29 ZIP members) and now records a fixed UTC rollback deadline in its Root-owned baseline. Its systemd timer combines `OnActiveSec=480s` for running-time protection with immutable `OnCalendar=` and `Persistent=true` for missed rollback catch-up after reboot. It validates the recorded timer's byte identity, avoiding accidental rearming with a different deadline. Decision based on systemd upstream timer documentation: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . The exact candidate was deterministically reassembled **in RAM only** on physical MMZ from the prior accepted R553 ZIP; new SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE`, **8,360,956 bytes**. Guard SHA256 `81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444`, wrapper SHA256 `FA7AE91F6725875936B51EC7E42B7B2011C5F8D1EE25952F0A4865678596ACAE`. Isolated method tests **14/14 PASS**. GitHub exact-head native systemd calendar parsing/restart tests remain to execute; these are not physical reboot acceptance. Previous R553 ZIP SHA `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805` (8,360,302 bytes) retains historical R554–R558 evidence but is superseded for any new private canary once R559 CI passes. Public Stable remains v1.5.4. No protected installed service, network route/DNS/Tailscale or user config was mutated by the R559 rehearsal. **R554/R555 hosted native guard acceptance:** At exact source+test head `846dfba6a35f27382d10608ffd93806c5650002a`, both [push CI 38811848360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811848360) and [PR CI 38811854504](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811854504) finished **SUCCESS**. An actual fresh disposable GitHub Ubuntu VM with UID 0 confirmed independent root-owned systemd timer/service armed before protected mutation, Root 1.5.2/User 1.5.4 → RC1 upgraded, and a separately run root-owned systemd service restored Root+User byte-for-byte; official stable source, private mutable user settings and resolver/hosts remained SHA-identical, with no production host touched. R555 additionally executed the **unmodified production `upgrade_system.sh` entrypoint**, rather than merely invoking RootTransaction directly: true native wrapper+auto-arm and rollback PASS on both hosted runs. R532 independently confirms monotonic systemd timer expiry; R554/R555 trigger the exact R553 worker through its native systemd service but do **not** prove exact 480s timer expiry or reboot timing on a physical computer. Windows native private v1.5.4→RC1→v1.5.4 PASS at exact push, Windows setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; public release still unchanged. diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 86a8bedc..19f98ca5 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -12,6 +12,7 @@ import hashlib import importlib.util import json +import datetime import os from pathlib import Path import pwd @@ -158,18 +159,21 @@ def guard_files(source:Path,uid:int,user:str,home:Path,um:Path,rm:Path): "oldUserMetaSha":sha_file(um),"oldResolverSha":sha_file(dns), "oldResolverLink":os.readlink(dns) if dns.is_symlink() else None, "createdUtc":time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime()), - "timerSeconds":480,"networkMutationByWatchdog":False} + "timerSeconds":480,"deadlineUnix":int(time.time())+480, + "networkMutationByWatchdog":False} write_new(BASELINE_FILE,canonical(baseline),0o600) return baseline -def unit_templates(): +def unit_templates(deadline_unix:int): + need(type(deadline_unix) is int and deadline_unix>0,"INVALID_CALENDAR_DEADLINE") + deadline=datetime.datetime.fromtimestamp(deadline_unix,datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") service=("[Unit]\nDescription=Direct Internet Method guarded root+user upgrade rollback\n" "After=local-fs.target\n" "[Service]\nType=oneshot\nUser=root\nGroup=root\n" "ExecStart=/usr/bin/python3 -B "+str(WORKER)+" rollback\n" "TimeoutStartSec=120\nNoNewPrivileges=false\nUMask=0077\n") timer=("[Unit]\nDescription=Direct Internet Method guarded upgrade timeout\n" - "[Timer]\nOnActiveSec=480s\nAccuracySec=1s\n" + "[Timer]\nOnActiveSec=480s\nOnCalendar="+deadline+"\nPersistent=true\nAccuracySec=1s\n" "Unit="+UNIT+".service\n" "[Install]\nWantedBy=timers.target\n") return service.encode(),timer.encode() @@ -204,7 +208,7 @@ def arm(source:Path,expected_uid:int): need(uid==expected_uid,"USER_IDENTITY_CHANGED") staged_user_manifest(home,uid,um) baseline=guard_files(source,uid,user,home,um,rm) - s,t=unit_templates() + s,t=unit_templates(baseline["deadlineUnix"]) write_new(SERVICE,s,0o644) write_new(TIMER,t,0o644) for cmd in ([ "/usr/bin/systemctl","daemon-reload"], @@ -227,6 +231,12 @@ def verify_guard_files(): and sha_file(ROOT_TX)==PIN_ROOT and sha_file(USER_TX)==PIN_USER, "GUARD_SOURCE_SHA") for p in (SERVICE,TIMER):regular(p,0,0o644) + need(j.get("timerSeconds")==480 and + type(j.get("deadlineUnix")) is int and + j["deadlineUnix"]>0,"BASELINE_DEADLINE") + service,timer=unit_templates(j["deadlineUnix"]) + need(SERVICE.read_bytes()==service and TIMER.read_bytes()==timer, + "ROOT_OWNED_TIMER_BYTES_CHANGED") return j def verify_timer(): diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index d8dfdec1..83165b1f 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43" +GUARD_SHA="81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 139f66c9..33faf034 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805"} +EXPECTED={"version":VERSION,"files":27,"sha256":"CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 638fd709..06ac5859 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="e30367ff492984face32dcd9ac34c09ac5cba7e18432cb5f1b4d939e91415805" +CANDIDATE_SHA="ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 48918330..6ac04b6f 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43" +SOURCE_SHA="81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) @@ -42,12 +42,26 @@ def synthetic_receipt(target,blob,mode): g.os.geteuid=lambda:0 self.addCleanup(lambda:setattr(g.os,"geteuid",self.old_geteuid)) def test_01_independent_timer_survives_reboot(self): - s,t=g.unit_templates() + deadline=1900000000 + s,t=g.unit_templates(deadline) self.assertIn(b"OnActiveSec=480s",t) + self.assertIn(b"OnCalendar=2030-03-17 17:46:40 UTC",t) + self.assertIn(b"Persistent=true",t) self.assertIn(b"WantedBy=timers.target",t) self.assertNotIn(b"OnBootSec=",t) self.assertIn(b"User=root",s) self.assertIn(b"rollback",s) + def test_13_calendar_deadline_fixed_across_timer_restart(self): + _,original=g.unit_templates(1900000000) + _,same=g.unit_templates(1900000000) + _,later=g.unit_templates(1900000060) + self.assertEqual(original,same) + self.assertNotEqual(original,later) + self.assertIn(b"Persistent=true",original) + self.assertNotIn(b"OnBootSec=",original) + def test_14_invalid_calendar_deadline_rejected(self): + for bad in ("1900000000",None,-1,0,True): + with self.assertRaises(RuntimeError):g.unit_templates(bad) def test_02_no_pending_does_not_change_system(self): tx=FakeTx("NO_PENDING_UPGRADE");g.imported_root=lambda:tx self.assertEqual(g.rollback_worker(),0) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index a2010d4a..4ad9449c 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805" +PIN_PACKAGE="CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" -PIN_GUARD="8651A4E32E8E4FCD832AD79BCD885F73B1DE6242B575EACE2C131C3B63749F43" +PIN_GUARD="81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") @@ -136,6 +136,27 @@ def run(): present=guard.verify_timer() need(present["uid"]==user.pw_uid and present["workerSha"]==sha(guard.WORKER), "SYSTEMD_TIMER_GUARD") + # R559: the calendar deadline is immutable across timer restart. On + # reboot the original calendar event and Persistent=true restore the + # missed rollback, rather than restarting OnActiveSec from zero. + deadline=present.get("deadlineUnix") + need(type(deadline) is int and deadline>0,"PERSISTENT_DEADLINE_IN_BASELINE") + timestamp=guard.datetime.datetime.fromtimestamp( + deadline,guard.datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") + timer_body=guard.TIMER.read_text("utf8") + need("OnCalendar="+timestamp+"\n" in timer_body and + "Persistent=true\n" in timer_body and + "OnActiveSec=480s\n" in timer_body,"PERSISTENT_WATCHDOG_TIMER_CONTENT") + command(["/usr/bin/systemd-analyze","calendar",timestamp],10) + next_cal_before=command(["/usr/bin/systemctl","show",NAME+".timer", + "-p","NextElapseUSecRealtime","--value"]) + need(next_cal_before and next_cal_before not in ("n/a","0"), + "CALENDAR_DEADLINE_NOT_SCHEDULED") + command(["/usr/bin/systemctl","restart",NAME+".timer"],12) + next_cal_after=command(["/usr/bin/systemctl","show",NAME+".timer", + "-p","NextElapseUSecRealtime","--value"]) + need(next_cal_after==next_cal_before,"RESTART_MOVED_ORIGINAL_DEADLINE") + guard.verify_timer() need(root_tx.status()["status"]=="APPLIED","ROOT_PENDING") r529.as_runner(user,tx_user,"--apply","--source",extracted) manifest=json.loads((pending/"manifest.json").read_text()) @@ -195,6 +216,7 @@ def run(): "guardArmedBeforeRootMutation":True, "systemdServiceStartedInSeparateProcess":True, "timerNativeLoadedAndEnabledBeforeMutation":True, + "calendarDeadlinePreservedAcrossTimerRestart":True, "productionShellUpgradeWrapperExecuted":native_wrapper, "rootAndUserByteExactRestoration":True, "mutatedProductionHost":False,"modifiedRouteDnsOrTailscale":False, From b4ec38a689bcb31db83627d0c881ad77d439f83e Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:51:47 +0330 Subject: [PATCH 037/188] docs(brain): reconcile R559 UTC persistent rollback hosted CI and MMZ R560/R561 evidence --- PROJECT_BRAIN.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 506f5c5f..bbef7d46 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R559 (2026-10-16) — private RC1, reboot-recovery hardening, pending native acceptance:** The single Linux 1.5.5-rc1 package retains 27 payload files (29 ZIP members) and now records a fixed UTC rollback deadline in its Root-owned baseline. Its systemd timer combines `OnActiveSec=480s` for running-time protection with immutable `OnCalendar=` and `Persistent=true` for missed rollback catch-up after reboot. It validates the recorded timer's byte identity, avoiding accidental rearming with a different deadline. Decision based on systemd upstream timer documentation: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . The exact candidate was deterministically reassembled **in RAM only** on physical MMZ from the prior accepted R553 ZIP; new SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE`, **8,360,956 bytes**. Guard SHA256 `81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444`, wrapper SHA256 `FA7AE91F6725875936B51EC7E42B7B2011C5F8D1EE25952F0A4865678596ACAE`. Isolated method tests **14/14 PASS**. GitHub exact-head native systemd calendar parsing/restart tests remain to execute; these are not physical reboot acceptance. Previous R553 ZIP SHA `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805` (8,360,302 bytes) retains historical R554–R558 evidence but is superseded for any new private canary once R559 CI passes. Public Stable remains v1.5.4. No protected installed service, network route/DNS/Tailscale or user config was mutated by the R559 rehearsal. +**Current candidate R559 (2026-10-16) — private v1.5.5-rc1; source+hosted native CI PASS, physical acceptance OPEN:** Exact single Linux ZIP has 27 payload files/29 ZIP members, size **8,360,956 bytes**, SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE`. Guard source SHA256 `81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444`; upgrade wrapper SHA256 `FA7AE91F6725875936B51EC7E42B7B2011C5F8D1EE25952F0A4865678596ACAE`. New Root-owned immutable UTC deadline combines systemd `OnActiveSec=480s` with `OnCalendar=` and `Persistent=true`; documented systemd catch-up after downtime applies to calendar timers (https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml). Fail-closed root-owned timer contents are checked against the initial deadline. Isolated 14/14 tests PASS on MMZ. Exact code commit `e14ad473e1eac36208de20e19cd8c817c3fd69cd`, [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS**: actual privileged Ubuntu-hosted native Root/User guarded installer and rollback, parse of absolute `OnCalendar`, persisted calendar deadline across **systemd timer restart**, unchanged network files, and exact native Windows private v1.5.4→RC1→v1.5.4 PASS. Windows Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`. This is **not** an actual reboot test or evidence of physical owner-host RC1 acceptance. Prior R553 private ZIP SHA256 `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805` remains historical R554–R558 evidence and is superseded for NEW canaries. + +**R560/R561 physical MMZ acceptance (nonprivileged scope only):** The exact R559 ZIP was independently reconstructed in memory from accepted R553 source, atomically staged at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R559_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600); receipt `R541_QA/R560_PRIVATE_R559_READY.json` SHA256 `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`. The true R559 `upgrade_guard.py preflight` from this ZIP ran unprivileged on MMZ, with six PASS including fail-closed modified Root/User payload, bad archive layout and denied non-Root arming; receipt `R541_QA/R561_NATIVE_GUARD_PREFLIGHT.json` SHA256 `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. Real installed Root v1.5.2/User v1.5.4, Tailscale, DNS, routes, application metadata and user consent were **unchanged**. No real new Guard service installed or armed. **R554/R555 hosted native guard acceptance:** At exact source+test head `846dfba6a35f27382d10608ffd93806c5650002a`, both [push CI 38811848360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811848360) and [PR CI 38811854504](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38811854504) finished **SUCCESS**. An actual fresh disposable GitHub Ubuntu VM with UID 0 confirmed independent root-owned systemd timer/service armed before protected mutation, Root 1.5.2/User 1.5.4 → RC1 upgraded, and a separately run root-owned systemd service restored Root+User byte-for-byte; official stable source, private mutable user settings and resolver/hosts remained SHA-identical, with no production host touched. R555 additionally executed the **unmodified production `upgrade_system.sh` entrypoint**, rather than merely invoking RootTransaction directly: true native wrapper+auto-arm and rollback PASS on both hosted runs. R532 independently confirms monotonic systemd timer expiry; R554/R555 trigger the exact R553 worker through its native systemd service but do **not** prove exact 480s timer expiry or reboot timing on a physical computer. Windows native private v1.5.4→RC1→v1.5.4 PASS at exact push, Windows setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; public release still unchanged. @@ -24,7 +26,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **Real physical baseline:** Emad Windows registered v1.5.4; GUI/Service binary SHA match exact public asset source, DirectInternetMethodSvc and Tailscale running. Emad Linux User 1.5.4/Root 1.5.2, owned NFQUEUE bound to old `wlp2s0` while current default route uses `enp1s0`; Tailscale active; Google HTTPS 204 and YouTube TLS curl 35. MMZ Linux User 1.5.4/Root 1.5.2 with no pending upgrade slots. All real product installations remain unchanged by this source/QA work. -**Critical dependency chain:** R553 independent Root/User guard and real production wrapper are now native-hosted **PASS**, but the exact worker is **NOT ARMED ON ANY USER PRODUCTION HOST**. Root privilege on MMZ still requires its own legitimate administrator authentication; previous physical guard attempt was denied by host safety policy and must not be bypassed. Real RC1 protected install, GUI Start→DoH/HTTPS→Stop/Recovery, Tailscale/physical-DNS invariants, reboot behavior, and an accepted **coordinated two-sided persistent Root/User commit** are OPEN. RootTransaction.commit and UserTransaction.commit currently have separate irreversible phase/backup operations, and hosted rollback PASS does not prove interruption-safe final acceptance/cleanup. Do not merge, create tag, publish v1.5.5, or claim whole-project FINAL until native physical acceptance plus safe promotion/rollback evidence. Public v1.5.4 Stable remains unchanged. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. +**Critical dependency chain:** R559 Root/User independent guard, real production wrapper and absolute-calendar persistence across timer restart are native-hosted **PASS**, but the new guard is **NOT ARMED ON ANY USER PRODUCTION HOST**. Root privilege on MMZ still requires its own legitimate administrator authentication; previous physical guard attempt was denied by host safety policy and must not be bypassed. Real RC1 protected install, GUI Start→DoH/HTTPS→Stop/Recovery, Tailscale/physical-DNS invariants, reboot behavior, and an accepted **coordinated two-sided persistent Root/User commit** are OPEN. RootTransaction.commit and UserTransaction.commit currently have separate irreversible phase/backup operations, and hosted rollback PASS does not prove interruption-safe final acceptance/cleanup. Do not merge, create tag, publish v1.5.5, or claim whole-project FINAL until native physical acceptance plus safe promotion/rollback evidence. Public v1.5.4 Stable remains unchanged. Ask only for indispensable guarded privilege/action when appropriate and continue independent tasks. **Authority map:** this current `PROJECT_BRAIN.md` notice is the source-branch singleton Index/Brain/State locator, with unique GitHub PR/CI and physical QA receipts as evidence links. The historical R0 lineage below and `BRAIN_R*.zip` snapshots remain provenance, not competing active authorities. Earlier Candidate/Final/signing-HOLD statements are superseded where conflicting. The owner’s DoD remains one-install in-place, preserved settings, verified rollback, active Linux/Windows runtime and a single trustworthy public release line. From bad874c125625076b1e253fd8c230f02aef96c24 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:14:24 +0330 Subject: [PATCH 038/188] fix(linux): R562 resume accepted Root/User pair after interrupted finalization --- .github/workflows/ci.yml | 42 ++++++-- PROJECT_BRAIN.md | 4 +- linux/system/upgrade_guard.py | 101 +++++++++++++----- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 87 ++++++++++++++- tests/r554_native_guard_ephemeral_contract.py | 95 ++++++++++++---- 8 files changed, 276 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad76c45d..812166ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE" - assert p.is_file() and p.stat().st_size==8360956 + expected="B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471" + assert p.is_file() and p.stat().st_size==8363886 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -144,7 +144,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -173,7 +173,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -206,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -235,7 +235,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -252,6 +252,36 @@ jobs: # A DIFFERENT, empty GitHub-hosted Ubuntu VM; never production host. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-rc1-accepted-finalization-ephemeral: + name: linux-rc1-accepted-finalization-ephemeral + needs: linux-rc1-native-guard-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R562 build exact guarded 27-file RC1 + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + - name: R562 download exact official v1.5.4 installed baseline + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R562 synthetic root acceptance interruption and independent user finalization + shell: bash + run: | + set -euo pipefail + # TEST-ONLY fabricated acceptance fixture on an EMPTY disposable VM. + # This is a transaction-crash regression, never owner/live approval. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true DIM_R562_SYNTHETIC_PROMOTION=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index bbef7d46..6f22c2c8 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R559 (2026-10-16) — private v1.5.5-rc1; source+hosted native CI PASS, physical acceptance OPEN:** Exact single Linux ZIP has 27 payload files/29 ZIP members, size **8,360,956 bytes**, SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE`. Guard source SHA256 `81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444`; upgrade wrapper SHA256 `FA7AE91F6725875936B51EC7E42B7B2011C5F8D1EE25952F0A4865678596ACAE`. New Root-owned immutable UTC deadline combines systemd `OnActiveSec=480s` with `OnCalendar=` and `Persistent=true`; documented systemd catch-up after downtime applies to calendar timers (https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml). Fail-closed root-owned timer contents are checked against the initial deadline. Isolated 14/14 tests PASS on MMZ. Exact code commit `e14ad473e1eac36208de20e19cd8c817c3fd69cd`, [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS**: actual privileged Ubuntu-hosted native Root/User guarded installer and rollback, parse of absolute `OnCalendar`, persisted calendar deadline across **systemd timer restart**, unchanged network files, and exact native Windows private v1.5.4→RC1→v1.5.4 PASS. Windows Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`. This is **not** an actual reboot test or evidence of physical owner-host RC1 acceptance. Prior R553 private ZIP SHA256 `E30367FF492984FACE32DCD9AC34C09AC5CBA7E18432CB5F1B4D939E91415805` remains historical R554–R558 evidence and is superseded for NEW canaries. +**Current candidate R562 (2026-10-16), UNRELEASED / acceptance pending:** Fixed the two-sided Root/User finalization crash window without altering normal rollback: when a privileged native Root `RootTransaction.commit` is already durably `COMMITTED` but the bounded User pending slot remains, the independently owned systemd guard worker now verifies all Root manifest/pinned binaries/metadata, the exact candidate User `INSTALL.json` identity and owned pending slot; ONLY THEN it invokes the SHA-pinned unprivileged User transaction `--commit` to complete the accepted pair. In a foreign/changed setting, rejected commit or corrupted Root state, it fails closed and retains evidence; it does not roll back an already-accepted Root or change network/Tailscale. Existing pre-acceptance autonomous Root/User rollback path remains separate. Exact replacement guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`; matching pinned Root upgrade wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`. Single **27-file/29-member** Linux RC1 archive deterministically reconstructed in RAM on MMZ with SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471`, 8,363,886 bytes; no physical install/network changes. Rootless extracted worker+negative fixtures **18/18 PASS**. A hosted native disposable Ubuntu test uses an explicitly marked **synthetic** acceptance receipt solely to simulate a crash after privileged Root commit and before User commit; it MUST NOT be confused with a real signed/owner physical acceptance. Exact-source GitHub CI and native systemd acceptance for R562 are **PENDING**, and previous R559 ZIP is superseded only for NEW canaries after PASS. No public release promotion is authorized. + +**R559 retained hosted/physical provenance:** [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS** at `e14ad473e1eac36208de20e19cd8c817c3fd69cd`. The independent Root/User upgrade watchdog retained immutable UTC timer deadline across native systemd timer restart with `OnActiveSec=480s`, `OnCalendar`, `Persistent=true`, and restored two-sided bytes on hosted Ubuntu. Exact private SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE` (8,360,956 bytes), staged on MMZ for R560 with receipt `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`; physical rootless R561 receipt `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. These are historical exact-scope PASSES and NOT R562 or physical guarded install acceptance. **R560/R561 physical MMZ acceptance (nonprivileged scope only):** The exact R559 ZIP was independently reconstructed in memory from accepted R553 source, atomically staged at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R559_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600); receipt `R541_QA/R560_PRIVATE_R559_READY.json` SHA256 `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`. The true R559 `upgrade_guard.py preflight` from this ZIP ran unprivileged on MMZ, with six PASS including fail-closed modified Root/User payload, bad archive layout and denied non-Root arming; receipt `R541_QA/R561_NATIVE_GUARD_PREFLIGHT.json` SHA256 `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. Real installed Root v1.5.2/User v1.5.4, Tailscale, DNS, routes, application metadata and user consent were **unchanged**. No real new Guard service installed or armed. diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 19f98ca5..7941d558 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -267,6 +267,46 @@ def verify_installed_after_restore(j): "DNS_CHANGED_EXTERNALLY") need(not (home/PENDING_REL).exists(),"USER_SLOT_REMAINS") +def finish_accepted_pair(j,tx,home:Path,pending:Path): + """Resume only a ROOT-ACCEPTED pair; never invent acceptance or rollback it. + + Root's COMMITTED manifest is a privileged durable decision. The original + user slot stays intact until the exact copied user transaction validates + all managed bytes, mutable settings and new INSTALL.json and commits. + An ambiguous/foreign state fails closed and retains its recovery evidence. + """ + m=tx.manifest() + need(m.get("phase")=="COMMITTED" and m.get("toVersion")==VERSION and + m.get("fromVersion")==BASELINE and m.get("uid")==j["uid"] and + m.get("home")==str(home),"ROOT_ACCEPTANCE_MANIFEST") + tx._validate_slot(m) + states,meta=tx._target_states(m) + need(set(states)==set(m["files"]) and + all(states[k]==m["files"][k]["new"] for k in states) and + meta==m["newMetadataSHA"],"ACCEPTED_ROOT_BINARY_OR_METADATA_DRIFT") + rootmeta=json.loads(tx.meta.read_text("utf8")) + need(rootmeta.get("version")==VERSION and rootmeta.get("uid")==j["uid"], + "ACCEPTED_ROOT_INSTALL_IDENTITY") + usermeta=home/USER_REL/"INSTALL.json" + regular(usermeta,j["uid"],0o600) + installed=json.loads(usermeta.read_text("utf8")) + need(installed.get("schema")==2 and installed.get("version")==VERSION and + installed.get("protectedBackendVersion")==VERSION, + "ACCEPTED_USER_INSTALL_IDENTITY") + was_pending=pending.exists() or pending.is_symlink() + if was_pending: + need(pending.is_dir() and not pending.is_symlink() and + pending.stat().st_uid==j["uid"],"ACCEPTED_USER_SLOT_FOREIGN") + job=command(["/usr/sbin/runuser","-u",j["user"],"--", + "/usr/bin/env","HOME="+j["home"], + "PYTHONDONTWRITEBYTECODE=1","/usr/bin/python3","-B", + str(USER_TX),"--commit"],timeout=45) + need(job.returncode==0,"USER_FORWARD_COMMIT_"+job.stderr[-180:].replace(" ","_")) + need(not pending.exists() and not pending.is_symlink(), + "ACCEPTED_USER_PENDING_REMAINS") + # No network actions; no root backups are destroyed by this worker. + return was_pending + def rollback_worker(): need(os.geteuid()==0 and Path(__file__).resolve()==WORKER,"OWNED_WORKER_ROOT_REQUIRED") start=time.monotonic() @@ -284,33 +324,45 @@ def rollback_worker(): tx=imported_root() need(tx.native,"REAL_ROOT_TRANSACTION") phase=tx.status()["status"] - # Never replace installed code underneath a live owned network engine. - # Such an ambiguous state requires local authorized Recovery first. - need(not (home/USER_REL/"directmethod/state.json").exists(), - "LIVE_ENGINE_REQUIRES_LOCAL_RECOVERY") report["rootPhaseBefore"]=phase if phase=="COMMITTED": - need(not pending.exists(),"COMMITTED_USER_PENDING") - report["status"]="NO_ROLLBACK_ALREADY_COMMITTED" - elif phase=="NO_PENDING_UPGRADE" and not pending.exists(): - report["status"]="NO_CHANGE_NO_PENDING" + # Root acceptance is already durable and cannot be rolled back. + # If its user partner was interrupted during finalization, resume + # exact user-only commit after validating both installed sides. + # An active engine may continue because this deletes ONLY the + # owned user code backup, not the active network or program files. + report["finalizationAttempted"]=pending.exists() + finished=finish_accepted_pair(j,tx,home,pending) + if finished: + report["userForwardCommit"]="PASS" + report["status"]="PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT" + else: + report["status"]="NO_ROLLBACK_ALREADY_COMMITTED" else: - need(phase in ("STAGED","APPLIED","DEGRADED","NO_PENDING_UPGRADE"),"UNKNOWN_ROOT_PHASE") - report["recoveryAttempted"]=True - if pending.exists(): - need(pending.is_dir() and not pending.is_symlink(),"UNSAFE_USER_SLOT") - p=command(["/usr/sbin/runuser","-u",j["user"],"--", - "/usr/bin/env","HOME="+j["home"], - "PYTHONDONTWRITEBYTECODE=1","/usr/bin/python3","-B", - str(USER_TX),"--rollback"],timeout=45) - need(p.returncode==0,"USER_ROLLBACK_"+p.stderr[-180:].replace(" ","_")) - report["userRollback"]="PASS" - if phase in ("STAGED","APPLIED","DEGRADED"): - returned=tx.rollback() - need(returned.get("status")=="ROLLED_BACK","ROOT_ROLLBACK") - report["rootRollback"]="PASS" - verify_installed_after_restore(j) - report["status"]="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" + # Before Root acceptance, replacing installed code under a live + # engine is unsafe; require authorized local Recovery. + need(not (home/USER_REL/"directmethod/state.json").exists(), + "LIVE_ENGINE_REQUIRES_LOCAL_RECOVERY") + if phase=="NO_PENDING_UPGRADE" and not pending.exists(): + report["status"]="NO_CHANGE_NO_PENDING" + else: + need(phase in ("STAGED","APPLIED","DEGRADED","NO_PENDING_UPGRADE"), + "UNKNOWN_ROOT_PHASE") + report["recoveryAttempted"]=True + if pending.exists(): + need(pending.is_dir() and not pending.is_symlink(),"UNSAFE_USER_SLOT") + p=command(["/usr/sbin/runuser","-u",j["user"],"--", + "/usr/bin/env","HOME="+j["home"], + "PYTHONDONTWRITEBYTECODE=1","/usr/bin/python3","-B", + str(USER_TX),"--rollback"],timeout=45) + need(p.returncode==0,"USER_ROLLBACK_"+p.stderr[-180:].replace(" ","_")) + report["userRollback"]="PASS" + if phase in ("STAGED","APPLIED","DEGRADED"): + returned=tx.rollback() + need(returned.get("status")=="ROLLED_BACK","ROOT_ROLLBACK") + report["rootRollback"]="PASS" + verify_installed_after_restore(j) + report["status"]="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" except Exception as ex: report["failure"]=type(ex).__name__+":"+str(ex)[:320] report["durationSeconds"]=round(time.monotonic()-start,3) @@ -318,6 +370,7 @@ def rollback_worker(): write_new(RESULT,canonical(report),0o600) print(json.dumps(report,sort_keys=True)) return 0 if report["status"] in ( + "PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT", "NO_ROLLBACK_ALREADY_COMMITTED","NO_CHANGE_NO_PENDING", "PASS_AUTONOMOUS_TWO_SIDED_RESTORATION") else 90 diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 83165b1f..21aba1d8 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444" +GUARD_SHA="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 33faf034..d0239d3c 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE"} +EXPECTED={"version":VERSION,"files":27,"sha256":"B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 06ac5859..347f8cb8 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="ca249d9792c69320e934c53888efb577729ab4a616a8951ef1736ed7161445de" +CANDIDATE_SHA="b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 6ac04b6f..903d4a1f 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444" +SOURCE_SHA="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) @@ -101,12 +101,39 @@ def test_06_rejected_user_rollback_never_attempts_root(self): self.assertEqual(g.rollback_worker(),90) self.assertEqual(tx.calls,["status"]) self.assertIn("USER_ROLLBACK",json.loads(self.result.read_text())["failure"]) + def _accepted_root_fixture(self): + app=self.home/g.USER_REL + app.mkdir(parents=True,exist_ok=True) + meta=app/"INSTALL.json" + meta.write_text(json.dumps({"schema":2,"version":g.VERSION, + "protectedBackendVersion":g.VERSION})) + meta.chmod(0o600) + rootmeta=self.home/"root-meta.json" + rootmeta.write_text(json.dumps({"version":g.VERSION,"uid":1000})) + class CommittedRoot: + native=True + meta=rootmeta + calls=[] + def status(self):self.calls.append("status");return {"status":"COMMITTED"} + def manifest(self): + return {"phase":"COMMITTED","fromVersion":g.BASELINE, + "toVersion":g.VERSION,"uid":1000,"home":str(self_home), + "files":{"helper":{"new":"EXPECTED"}},"newMetadataSHA":"ROOT_META_SHA"} + def _validate_slot(self,m):self.calls.append("validate") + def _target_states(self,m):self.calls.append("target_states");return {"helper":"EXPECTED"},"ROOT_META_SHA" + def rollback(self):raise AssertionError("ACCEPTED_ROOT_MUST_NEVER_ROLLBACK") + self_home=self.home + tx=CommittedRoot() + g.imported_root=lambda:tx + return tx,meta + def test_07_committed_does_not_rollback(self): - tx=FakeTx("COMMITTED");g.imported_root=lambda:tx + tx,_=self._accepted_root_fixture() self.assertEqual(g.rollback_worker(),0) self.assertEqual(json.loads(self.result.read_text())["status"], "NO_ROLLBACK_ALREADY_COMMITTED") - self.assertEqual(tx.calls,["status"]) + self.assertNotIn("user_command",self.events) + self.assertEqual(tx.calls,["status","validate","target_states"]) def test_08_idempotent_second_run_denied_by_owned_receipt(self): tx=FakeTx("NO_PENDING_UPGRADE");g.imported_root=lambda:tx self.assertEqual(g.rollback_worker(),0) @@ -114,6 +141,60 @@ def test_08_idempotent_second_run_denied_by_owned_receipt(self): self.assertEqual(json.loads(self.result.read_text())["status"], "NO_CHANGE_NO_PENDING") # Note second call currently computes but does not overwrite receipt. + def test_15_interrupted_accepted_root_finishes_exact_user_only(self): + tx,_=self._accepted_root_fixture() + pending=self.home/g.PENDING_REL + pending.mkdir(parents=True) + calls=[] + def confirm(args,timeout=15): + calls.append(tuple(args)) + self.assertEqual(args[-1],"--commit") + self.assertEqual(timeout,45) + pending.rmdir() + return types.SimpleNamespace(returncode=0,stdout="USER_COMMIT",stderr="") + g.command=confirm + self.assertEqual(g.rollback_worker(),0) + out=json.loads(self.result.read_text()) + self.assertEqual(out["status"],"PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT") + self.assertEqual(out["userForwardCommit"],"PASS") + self.assertTrue(out["finalizationAttempted"]) + self.assertFalse(pending.exists()) + self.assertEqual(len(calls),1) + self.assertEqual(tx.calls,["status","validate","target_states"]) + def test_16_rejected_user_commit_never_rolls_back_accepted_root(self): + tx,_=self._accepted_root_fixture() + pending=self.home/g.PENDING_REL;pending.mkdir(parents=True) + g.command=lambda args,timeout=15:types.SimpleNamespace( + returncode=84,stdout="",stderr="synthetic tampered config") + self.assertEqual(g.rollback_worker(),90) + doc=json.loads(self.result.read_text()) + self.assertEqual(doc["status"],"DEGRADED_UNRESOLVED") + self.assertIn("USER_FORWARD_COMMIT",doc["failure"]) + self.assertTrue(pending.exists()) + self.assertEqual(tx.calls,["status","validate","target_states"]) + def test_17_root_binary_drift_blocks_forward_commit(self): + tx,_=self._accepted_root_fixture() + pending=self.home/g.PENDING_REL;pending.mkdir(parents=True) + tx._target_states=lambda m:({"helper":"FOREIGN"},"ROOT_META_SHA") + g.command=lambda args,timeout=15: (_ for _ in ()).throw( + AssertionError("USER_COMMIT_MUST_NOT_RUN")) + self.assertEqual(g.rollback_worker(),90) + doc=json.loads(self.result.read_text()) + self.assertIn("ACCEPTED_ROOT_BINARY_OR_METADATA_DRIFT",doc["failure"]) + self.assertTrue(pending.exists()) + def test_18_active_engine_allows_only_accepted_backup_cleanup(self): + tx,_=self._accepted_root_fixture() + pending=self.home/g.PENDING_REL;pending.mkdir(parents=True) + state=self.home/g.USER_REL/"directmethod/state.json" + state.parent.mkdir(parents=True);state.write_text("{}") + g.command=lambda args,timeout=15:( + pending.rmdir() or types.SimpleNamespace(returncode=0,stdout="",stderr="")) + self.assertEqual(g.rollback_worker(),0) + self.assertTrue(state.is_file()) + self.assertNotIn("user_command",self.events) + self.assertEqual(json.loads(self.result.read_text())["status"], + "PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT") + def test_09_sources_are_not_host_install_commands(self): src=SRC.read_text() self.assertNotIn("sudo -S",src) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 4ad9449c..9a91251d 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE" +PIN_PACKAGE="B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" -PIN_GUARD="81A07EBB3E08CC5D4C9BF92EDFD2042F7464532F11851047C9F0867983B0D444" +PIN_GUARD="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") @@ -177,31 +177,80 @@ def run(): need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", "UPGRADED_ROOT_VERSION") need(root_tx.status()["status"]=="APPLIED","ROOT_STILL_PENDING") + # R562 tests interruption AFTER a deliberately simulated protected Root + # acceptance and BEFORE User commit. NOT real owner/live acceptance: + # a marked synthetic fixture exercises the fail-closed crash window only. + synthetic_promotion=os.environ.get("DIM_R562_SYNTHETIC_PROMOTION")=="true" + if synthetic_promotion: + m=root_tx.manifest() + fixture={ + "revision":"R519","status":"PASS_NATIVE_ROOT_UPGRADE", + "syntheticAcceptanceFixture":True, + "sourceRootSHA":rootmod.sha(rootmod.canonical(m["files"])), + "protectedMetadataSHA":m["newMetadataSHA"], + "nativeStartStopRecovery":True, + "tailscalePreserved":True, + "physicalDNSPreserved":True + } + acceptance=Path(temp)/"DIM_R562_TEST_ONLY_ACCEPTANCE.json" + need(not acceptance.exists() and not acceptance.is_symlink(), + "FOREIGN_TEST_ACCEPTANCE_EXISTS") + acceptance.write_text(json.dumps(fixture,sort_keys=True,indent=2)+"\n") + need(root_tx.commit(acceptance)["status"]=="COMMITTED", + "SYNTHETIC_ROOT_FINALIZATION") + need(root_tx.status()["status"]=="COMMITTED" and pending.is_dir(), + "ROOT_COMMITTED_USER_STILL_PENDING_WINDOW") # Actual independent root-owned systemd Service, rather than calling # guard.rollback_worker in-process. R532 separately proves timed firing. start=time.monotonic() command(["/usr/bin/systemctl","start",NAME+".service"],100) need(SEEN.is_file() and not SEEN.is_symlink(),"SYSTEMD_RECEIPT_MISSING") receipt=json.loads(SEEN.read_text()) - need(receipt.get("status")=="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" - and receipt.get("uid")==0 and receipt.get("userRollback")=="PASS" - and receipt.get("rootRollback")=="PASS" - and receipt.get("recoveryAttempted") is True, - "ROOT_OWNED_SERVICE_NOT_ACCEPTED") - need(root_tx.status()["status"]=="ROLLED_BACK","ROOT_PHASE_NOT_ROLLED_BACK") - need(not pending.exists(),"USER_PENDING_REMAINS") - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.4", - "USER_VERSION_NOT_RESTORED") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.2", - "ROOT_VERSION_NOT_RESTORED") - need(sha(app/"INSTALL.json")==before_user - and sha("/var/lib/directinternetmethod/install.json")==before_root, - "ORIGINAL_METADATA_SHA_DRIFT") - for target,want in original_user.items(): - need(sha(target).lower()==want,"USER_FILE_RESTORATION_"+Path(target).name) + if synthetic_promotion: + need(receipt.get("status")=="PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT" + and receipt.get("uid")==0 + and receipt.get("userForwardCommit")=="PASS" + and receipt.get("finalizationAttempted") is True, + "ACCEPTED_ROOT_USER_COMMIT_NOT_RECONCILED") + need(root_tx.status()["status"]=="COMMITTED","ACCEPTED_ROOT_PHASE_LOST") + need(not pending.exists(),"USER_BACKUP_NOT_RELEASED") + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1", + "COMMITTED_USER_VERSION") + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + "COMMITTED_ROOT_VERSION") + installed_manifest=json.loads((app/"INSTALL.json").read_text()) + for rel,row in manifest["managed"].items(): + dest=app/rel + need(dest.is_file() and sha(dest).lower()==row["sha"], + "COMMITTED_USER_CODE_DRIFT_"+rel) + for _,relative,target,oldsha,newsha,mode in rootmod.FILES: + dest=Path("/")/target + need(dest.is_file() and sha(dest)==newsha + and stat.S_IMODE(dest.stat().st_mode)==mode, + "COMMITTED_ROOT_CODE_DRIFT_"+dest.name) + for rel,want in rootmod.STATIC_ROOT.items(): + need(sha(Path("/")/rel)==want,"STATIC_ROOT_CHANGED_"+Path(rel).name) + need(root_tx.pending.is_dir(),"ROOT_AUDIT_SLOT_STILL_RETAINED") + else: + need(receipt.get("status")=="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" + and receipt.get("uid")==0 and receipt.get("userRollback")=="PASS" + and receipt.get("rootRollback")=="PASS" + and receipt.get("recoveryAttempted") is True, + "ROOT_OWNED_SERVICE_NOT_ACCEPTED") + need(root_tx.status()["status"]=="ROLLED_BACK","ROOT_PHASE_NOT_ROLLED_BACK") + need(not pending.exists(),"USER_PENDING_REMAINS") + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.4", + "USER_VERSION_NOT_RESTORED") + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.2", + "ROOT_VERSION_NOT_RESTORED") + need(sha(app/"INSTALL.json")==before_user + and sha("/var/lib/directinternetmethod/install.json")==before_root, + "ORIGINAL_METADATA_SHA_DRIFT") + for target,want in original_user.items(): + need(sha(target).lower()==want,"USER_FILE_RESTORATION_"+Path(target).name) + for target,want in protected.items(): + need(sha(target).lower()==want,"PROTECTED_FILE_RESTORATION_"+Path(target).name) r529.unchanged_records(private) - for target,want in protected.items(): - need(sha(target).lower()==want,"PROTECTED_FILE_RESTORATION_"+Path(target).name) for p,want in baseline_network.items(): need(sha(p)==want,"NETWORK_FILE_MUTATED") need(before_link==(os.readlink("/etc/resolv.conf") @@ -210,7 +259,9 @@ def run(): need(not (app/"directmethod/state.json").exists(),"ACTIVE_ENGINE_UNEXPECTED") command(["/usr/bin/systemctl","disable","--now",NAME+".timer"]) print(json.dumps({ - "revision":"R554","status":"PASS_EPHEMERAL_ACTUAL_SYSTEMD_OWNED_GUARD_ROOT_USER_ROLLBACK", + "syntheticRootAcceptanceOnly":synthetic_promotion, + "acceptedRootUserInterruptedResume":synthetic_promotion, + "revision":"R554","status":("PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION" if synthetic_promotion else "PASS_EPHEMERAL_ACTUAL_SYSTEMD_OWNED_GUARD_ROOT_USER_ROLLBACK"), "host":"ephemeral-github-ubuntu", "nativeProtectedRoot":True,"rootAuthorizedInDisposableVM":True, "guardArmedBeforeRootMutation":True, @@ -218,7 +269,7 @@ def run(): "timerNativeLoadedAndEnabledBeforeMutation":True, "calendarDeadlinePreservedAcrossTimerRestart":True, "productionShellUpgradeWrapperExecuted":native_wrapper, - "rootAndUserByteExactRestoration":True, + "rootAndUserByteExactRestoration":not synthetic_promotion, "mutatedProductionHost":False,"modifiedRouteDnsOrTailscale":False, "publishedStablePreserved":"v1.5.4", "rootFileCount":len(protected), From 07ca88f04fd572480be8cbce246f011b0fc1c68d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:15:31 +0330 Subject: [PATCH 039/188] test(linux): R562 fake accepted identity follows actual unprivileged runner uid --- tests/r552_native_upgrade_guard_contract.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 903d4a1f..16e5ba7d 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -33,7 +33,7 @@ def synthetic_receipt(target,blob,mode): target.chmod(0o600) g.write_new=synthetic_receipt g.SERVICE=p/"unit.service";g.TIMER=p/"unit.timer" - self.fake={"user":"runner","home":str(self.home),"uid":1000} + self.fake={"user":"runner","home":str(self.home),"uid":os.getuid()} g.verify_guard_files=lambda:self.fake self.events=[] g.verify_installed_after_restore=lambda j:self.events.append("validate_restored") @@ -109,7 +109,7 @@ def _accepted_root_fixture(self): "protectedBackendVersion":g.VERSION})) meta.chmod(0o600) rootmeta=self.home/"root-meta.json" - rootmeta.write_text(json.dumps({"version":g.VERSION,"uid":1000})) + rootmeta.write_text(json.dumps({"version":g.VERSION,"uid":os.getuid()})) class CommittedRoot: native=True meta=rootmeta @@ -117,7 +117,7 @@ class CommittedRoot: def status(self):self.calls.append("status");return {"status":"COMMITTED"} def manifest(self): return {"phase":"COMMITTED","fromVersion":g.BASELINE, - "toVersion":g.VERSION,"uid":1000,"home":str(self_home), + "toVersion":g.VERSION,"uid":os.getuid(),"home":str(self_home), "files":{"helper":{"new":"EXPECTED"}},"newMetadataSHA":"ROOT_META_SHA"} def _validate_slot(self,m):self.calls.append("validate") def _target_states(self,m):self.calls.append("target_states");return {"helper":"EXPECTED"},"ROOT_META_SHA" From 70a030e528d7e4c6d3dd4ade47f26b8de64d0828 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:19:35 +0330 Subject: [PATCH 040/188] docs(brain): upsert R562 native finalization PASS and MMZ R563-R564/root authorization boundary --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 6f22c2c8..336764f9 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R562 (2026-10-16), UNRELEASED / acceptance pending:** Fixed the two-sided Root/User finalization crash window without altering normal rollback: when a privileged native Root `RootTransaction.commit` is already durably `COMMITTED` but the bounded User pending slot remains, the independently owned systemd guard worker now verifies all Root manifest/pinned binaries/metadata, the exact candidate User `INSTALL.json` identity and owned pending slot; ONLY THEN it invokes the SHA-pinned unprivileged User transaction `--commit` to complete the accepted pair. In a foreign/changed setting, rejected commit or corrupted Root state, it fails closed and retains evidence; it does not roll back an already-accepted Root or change network/Tailscale. Existing pre-acceptance autonomous Root/User rollback path remains separate. Exact replacement guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`; matching pinned Root upgrade wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`. Single **27-file/29-member** Linux RC1 archive deterministically reconstructed in RAM on MMZ with SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471`, 8,363,886 bytes; no physical install/network changes. Rootless extracted worker+negative fixtures **18/18 PASS**. A hosted native disposable Ubuntu test uses an explicitly marked **synthetic** acceptance receipt solely to simulate a crash after privileged Root commit and before User commit; it MUST NOT be confused with a real signed/owner physical acceptance. Exact-source GitHub CI and native systemd acceptance for R562 are **PENDING**, and previous R559 ZIP is superseded only for NEW canaries after PASS. No public release promotion is authorized. +**Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) Linux jobs and test-only native promotion recovery successful, hosted private Windows lifecycle ongoing at record time. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. + +**R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. **R559 retained hosted/physical provenance:** [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS** at `e14ad473e1eac36208de20e19cd8c817c3fd69cd`. The independent Root/User upgrade watchdog retained immutable UTC timer deadline across native systemd timer restart with `OnActiveSec=480s`, `OnCalendar`, `Persistent=true`, and restored two-sided bytes on hosted Ubuntu. Exact private SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE` (8,360,956 bytes), staged on MMZ for R560 with receipt `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`; physical rootless R561 receipt `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. These are historical exact-scope PASSES and NOT R562 or physical guarded install acceptance. From df91979798b4809fd7d9466851ba6e4375e65cf0 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:31:44 +0330 Subject: [PATCH 041/188] docs(brain): accept exact R562 full CI and record MMZ host approval transport blocker --- PROJECT_BRAIN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 336764f9..270b06de 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,9 +2,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) Linux jobs and test-only native promotion recovery successful, hosted private Windows lifecycle ongoing at record time. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. +**Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) **SUCCESS**, including native Windows private Stable→RC1→Stable preservation/rollback; Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and acceptance receipt SHA256 `6A59269F19270C69B634BC41CB9FFB912B75BC73240ABE9261E9F395EA144BA7`. The subsequent exact-head Brain-only [Push 38819709423](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819709423) and [PR 38819715922](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819715922) also **SUCCESS**, with no candidate product-code change. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. -**R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. +**R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. **R559 retained hosted/physical provenance:** [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS** at `e14ad473e1eac36208de20e19cd8c817c3fd69cd`. The independent Root/User upgrade watchdog retained immutable UTC timer deadline across native systemd timer restart with `OnActiveSec=480s`, `OnCalendar`, `Persistent=true`, and restored two-sided bytes on hosted Ubuntu. Exact private SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE` (8,360,956 bytes), staged on MMZ for R560 with receipt `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`; physical rootless R561 receipt `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. These are historical exact-scope PASSES and NOT R562 or physical guarded install acceptance. From 73a97061c063e8959b5b67cd247bbe325f4e4c79 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:40:11 +0330 Subject: [PATCH 042/188] test(linux): R565 native systemd fail-closed on mutable drift after accepted Root --- .github/workflows/ci.yml | 31 ++++++++ tests/r554_native_guard_ephemeral_contract.py | 71 +++++++++++++++++-- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 812166ed..c2f0b068 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -282,6 +282,37 @@ jobs: # This is a transaction-crash regression, never owner/live approval. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true DIM_R562_SYNTHETIC_PROMOTION=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-rc1-accepted-drift-failclosed-ephemeral: + name: linux-rc1-accepted-drift-failclosed-ephemeral + needs: linux-rc1-accepted-finalization-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R565 build exact guarded 27-file RC1 + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + - name: R565 download exact official v1.5.4 installed baseline + shell: bash + run: | + set -euo pipefail + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R565 synthetic accepted Root rejects concurrent mutable User drift + shell: bash + run: | + set -euo pipefail + # TEST-ONLY fabricated acceptance fixture on an EMPTY disposable VM. + # NEGATIVE: exact root-owned service MUST fail closed, retain both slots, + # and never rollback an already COMMITTED synthetic Root. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true DIM_R562_SYNTHETIC_PROMOTION=true DIM_R565_SYNTHETIC_MUTABLE_DRIFT=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 9a91251d..b2f88d67 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -181,6 +181,9 @@ def run(): # acceptance and BEFORE User commit. NOT real owner/live acceptance: # a marked synthetic fixture exercises the fail-closed crash window only. synthetic_promotion=os.environ.get("DIM_R562_SYNTHETIC_PROMOTION")=="true" + synthetic_reject=os.environ.get("DIM_R565_SYNTHETIC_MUTABLE_DRIFT")=="true" + need(not synthetic_reject or synthetic_promotion, + "MUTABLE_NEGATIVE_REQUIRES_DISPOSABLE_ROOT_ACCEPTANCE_FIXTURE") if synthetic_promotion: m=root_tx.manifest() fixture={ @@ -200,13 +203,64 @@ def run(): "SYNTHETIC_ROOT_FINALIZATION") need(root_tx.status()["status"]=="COMMITTED" and pending.is_dir(), "ROOT_COMMITTED_USER_STILL_PENDING_WINDOW") + if synthetic_reject: + # The *only* deliberate user-data mutation happens on this disposable + # GitHub runner AFTER synthetically accepting Root and BEFORE user commit. + mutable=app/"settings.json" + old_mutable=mutable.read_bytes() + need(sha(mutable).lower()==private[str(mutable)], + "MUTABLE_TEST_PRESTATE_NOT_BASELINED") + mutable.write_bytes(old_mutable+b"R565_TEST_ONLY_MUTABLE_DRIFT\n") + need(sha(mutable).lower()!=private[str(mutable)], + "MUTABLE_DRIFT_FIXTURE_INEFFECTIVE") # Actual independent root-owned systemd Service, rather than calling # guard.rollback_worker in-process. R532 separately proves timed firing. start=time.monotonic() - command(["/usr/bin/systemctl","start",NAME+".service"],100) + if synthetic_reject: + # A failure is expected here. Real systemd must propagate the owned + # worker's nonzero exit rather than silently report successful commit. + attempt=subprocess.run( + ["/usr/bin/systemctl","start",NAME+".service"], + capture_output=True,text=True,check=False,timeout=100) + need(attempt.returncode!=0,"UNSAFE_USER_COMMIT_FALSE_SUCCESS") + else: + command(["/usr/bin/systemctl","start",NAME+".service"],100) need(SEEN.is_file() and not SEEN.is_symlink(),"SYSTEMD_RECEIPT_MISSING") receipt=json.loads(SEEN.read_text()) - if synthetic_promotion: + if synthetic_reject: + need(receipt.get("status")=="DEGRADED_UNRESOLVED" + and receipt.get("uid")==0 + and receipt.get("finalizationAttempted") is True + and receipt.get("recoveryAttempted") is False, + "FAILED_USER_FORWARD_COMMIT_WAS_NOT_RECORDED") + need("USER_FORWARD_COMMIT" in str(receipt.get("failure", "")) + and "MUTABLE_USER_DATA_CHANGED_CONCURRENTLY" in str(receipt.get("failure", "")), + "MUTABLE_DATA_DRIFT_WAS_NOT_REJECTED") + need(root_tx.status()["status"]=="COMMITTED" + and root_tx.pending.is_dir() and pending.is_dir(), + "ACCEPTED_ROOT_OR_USER_BACKUP_WAS_REVERTED") + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1" + and json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + "COMMITTED_VERSION_CHANGED_ON_FAILED_FINALIZATION") + for rel,row in manifest["managed"].items(): + dest=app/rel + need(dest.is_file() and sha(dest).lower()==row["sha"], + "USER_CODE_MUTATED_ON_REJECTED_COMMIT_"+rel) + for _,relative,target,oldsha,newsha,mode in rootmod.FILES: + dest=Path("/")/target + need(dest.is_file() and sha(dest)==newsha + and stat.S_IMODE(dest.stat().st_mode)==mode, + "PROTECTED_ROOT_MUTATED_ON_REJECTED_COMMIT_"+dest.name) + for rel,want in rootmod.STATIC_ROOT.items(): + need(sha(Path("/")/rel)==want, + "STATIC_ROOT_MUTATED_ON_REJECTED_USER_COMMIT_"+Path(rel).name) + for file,want in private.items(): + now=sha(file).lower() + if file==str(app/"settings.json"): + need(now!=want,"FIXTURE_MUTABLE_DRIFT_WAS_LOST") + else: + need(now==want,"UNRELATED_USER_MUTABLE_DRIFT_"+Path(file).name) + elif synthetic_promotion: need(receipt.get("status")=="PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT" and receipt.get("uid")==0 and receipt.get("userForwardCommit")=="PASS" @@ -250,7 +304,8 @@ def run(): need(sha(target).lower()==want,"USER_FILE_RESTORATION_"+Path(target).name) for target,want in protected.items(): need(sha(target).lower()==want,"PROTECTED_FILE_RESTORATION_"+Path(target).name) - r529.unchanged_records(private) + if not synthetic_reject: + r529.unchanged_records(private) for p,want in baseline_network.items(): need(sha(p)==want,"NETWORK_FILE_MUTATED") need(before_link==(os.readlink("/etc/resolv.conf") @@ -260,8 +315,14 @@ def run(): command(["/usr/bin/systemctl","disable","--now",NAME+".timer"]) print(json.dumps({ "syntheticRootAcceptanceOnly":synthetic_promotion, - "acceptedRootUserInterruptedResume":synthetic_promotion, - "revision":"R554","status":("PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION" if synthetic_promotion else "PASS_EPHEMERAL_ACTUAL_SYSTEMD_OWNED_GUARD_ROOT_USER_ROLLBACK"), + "acceptedRootUserInterruptedResume":synthetic_promotion and not synthetic_reject, + "expectedFailClosedSyntheticMutableDrift":synthetic_reject, + "revision":"R554","status":( + "PASS_EPHEMERAL_SYNTHETIC_MUTABLE_DRIFT_FAIL_CLOSED_AFTER_ACCEPTED_ROOT" + if synthetic_reject else + "PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION" + if synthetic_promotion else + "PASS_EPHEMERAL_ACTUAL_SYSTEMD_OWNED_GUARD_ROOT_USER_ROLLBACK"), "host":"ephemeral-github-ubuntu", "nativeProtectedRoot":True,"rootAuthorizedInDisposableVM":True, "guardArmedBeforeRootMutation":True, From 87ac6daedab2dad59806d80547f7520ad5d9c8f5 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:46:11 +0330 Subject: [PATCH 043/188] docs(brain): accept R565 native fail-closed mutable-drift CI and unresolved MMZ authorization --- PROJECT_BRAIN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 270b06de..dd907a94 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,6 +6,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. +**R565 native failure-path acceptance (2026-10-16), HOSTED PASS / PHYSICAL UNTESTED:** Production binaries, the Linux RC1 ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` and deterministic private Windows Setup `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` are unchanged. Test/CI-only commit `73a97061c063e8959b5b67cd247bbe325f4e4c79` added a separate disposable Ubuntu native systemd negative scenario: after **explicitly fabricated** Root acceptance but before User commit, mutate one synthetic `settings.json`; the exact protected worker must reject the forward User commit, return nonzero, report `DEGRADED_UNRESOLVED`, preserve both Root accepted phase and uncommitted User slot and leave all other managed files/resolver/hosts intact. [PR CI 38821938360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38821938360) and [Push CI 38821931613](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38821931613) fully **SUCCESS**, native negative job `PASS_EPHEMERAL_SYNTHETIC_MUTABLE_DRIFT_FAIL_CLOSED_AFTER_ACCEPTED_ROOT` on both independent runners. Push Windows stable→private RC1→stable rollback PASS; exact private Setup digest as above and unique receipt SHA256 `EEDE2C598B6362844C9C7FBB5BE3DC810DE0FC9813D7052FC3F1E3815404C01A`. This rejects silent user-data overwrite after irreversible Root acceptance; synthetic authorization is **not** user/physical approval. The production MMZ connector remains offline (`Tunnel-client has not been seen for 300 seconds`), and prior official `pkexec` requests remain blocked by its host policy. **No Root guard armed, no actual RC1 installed, no real reboot/DoH/HTTPS/Tailscale acceptance and no public release promotion.** + **R559 retained hosted/physical provenance:** [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS** at `e14ad473e1eac36208de20e19cd8c817c3fd69cd`. The independent Root/User upgrade watchdog retained immutable UTC timer deadline across native systemd timer restart with `OnActiveSec=480s`, `OnCalendar`, `Persistent=true`, and restored two-sided bytes on hosted Ubuntu. Exact private SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE` (8,360,956 bytes), staged on MMZ for R560 with receipt `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`; physical rootless R561 receipt `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. These are historical exact-scope PASSES and NOT R562 or physical guarded install acceptance. **R560/R561 physical MMZ acceptance (nonprivileged scope only):** The exact R559 ZIP was independently reconstructed in memory from accepted R553 source, atomically staged at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/R559_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600); receipt `R541_QA/R560_PRIVATE_R559_READY.json` SHA256 `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`. The true R559 `upgrade_guard.py preflight` from this ZIP ran unprivileged on MMZ, with six PASS including fail-closed modified Root/User payload, bad archive layout and denied non-Root arming; receipt `R541_QA/R561_NATIVE_GUARD_PREFLIGHT.json` SHA256 `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. Real installed Root v1.5.2/User v1.5.4, Tailscale, DNS, routes, application metadata and user consent were **unchanged**. No real new Guard service installed or armed. From 541af7c94a0d8fea687fc8372b492d1405808a64 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:53:06 +0330 Subject: [PATCH 044/188] test(linux): R566 native systemd persistent catch-up after inactive calendar deadline --- .github/workflows/ci.yml | 14 ++ .../r566_native_systemd_persistent_catchup.py | 138 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 tests/r566_native_systemd_persistent_catchup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2f0b068..ad94675c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -313,6 +313,20 @@ jobs: # and never rollback an already COMMITTED synthetic Root. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true DIM_R562_SYNTHETIC_PROMOTION=true DIM_R565_SYNTHETIC_MUTABLE_DRIFT=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-r566-calendar-catchup-ephemeral: + name: linux-r566-calendar-catchup-ephemeral + needs: linux-source + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R566 actual systemd Persistent calendar expiry during inactive timer + shell: bash + run: | + set -euo pipefail + # Unique synthetic root-owned unit on an EMPTY GitHub-hosted VM only. + sudo -n /usr/bin/env GITHUB_ACTIONS=true CI=true RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_REPOSITORY="$GITHUB_REPOSITORY" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" GITHUB_RUN_ID="$GITHUB_RUN_ID" /usr/bin/python3 -B tests/r566_native_systemd_persistent_catchup.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py new file mode 100644 index 00000000..02093fbd --- /dev/null +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""R566: systemd native missed-calendar catch-up, runner only. + +Exercises actual exact production timer directives OnActiveSec=480s, +OnCalendar= and Persistent=true. A different, uniquely named +synthetic one-shot systemd service writes only in /run on disposable +GitHub-hosted Ubuntu. No DIM installed application, network, Tailscale, +DNS, user-owned files, releases or root upgrade changes. +""" +from __future__ import annotations +import datetime as dt +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import subprocess +import sys +import tempfile +import time + +ROOT=Path(__file__).resolve().parents[1] +GUARD=ROOT/"linux/system/upgrade_guard.py" +PIN_GUARD="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" + +def need(value,reason): + if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) + +def run(*args,timeout=12): + p=subprocess.run(args,text=True,capture_output=True,timeout=timeout,check=False) + need(p.returncode==0,"COMMAND_"+str(p.returncode)+"_"+p.stderr[-170:].replace(" ","_")) + return p.stdout.strip() + +def preflight(): + need(os.geteuid()==0 and sys.platform=="linux","ROOT") + env=os.environ + need(env.get("GITHUB_ACTIONS")=="true" and env.get("CI")=="true" and + env.get("RUNNER_ENVIRONMENT")=="github-hosted" and + env.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod" and + Path(env.get("GITHUB_WORKSPACE","")).resolve()==ROOT,"HOSTED_RUNNER_ONLY") + need(Path("/proc/1/comm").read_text().strip()=="systemd","SYSTEMD_PID_ONE") + need(hashlib.sha256(GUARD.read_bytes()).hexdigest().upper()==PIN_GUARD,"PINNED_GUARD") + spec=importlib.util.spec_from_file_location("r566_exact_production_guard",GUARD) + need(spec is not None and spec.loader is not None,"IMPORT") + x=importlib.util.module_from_spec(spec);spec.loader.exec_module(x) + return x + +def main(): + guard=preflight() + identity=os.environ["GITHUB_RUN_ID"] + need(identity.isdecimal() and len(identity)<30,"RUN_ID") + name="dim-r566-catchup-"+identity + service=Path("/etc/systemd/system")/(name+".service") + timer=Path("/etc/systemd/system")/(name+".timer") + for target in (service,timer): + need(not target.exists() and not target.is_symlink(),"FOREIGN_SYSTEMD_UNIT") + with tempfile.TemporaryDirectory(prefix="dim-r566-"+identity+"-",dir="/run") as directory: + root=Path(directory) + os.chmod(root,0o700) + marker=root/"fired.json" + worker=root/"worker.py" + worker.write_text( + "from pathlib import Path\n" + "import json,os,time\n" + "out=Path("+repr(str(marker))+")\n" + "fd=os.open(out,os.O_CREAT|os.O_EXCL|os.O_WRONLY|os.O_NOFOLLOW,0o600)\n" + "with os.fdopen(fd,'w') as f:\n" + " f.write(json.dumps({'pid':os.getpid(),'utc':time.time()})+'\\n')\n" + " f.flush();os.fsync(f.fileno())\n", + encoding="utf-8") + os.chmod(worker,0o500) + deadline=int(time.time())+12 + original_service,original_timer=guard.unit_templates(deadline) + replacement=original_service.replace( + ("ExecStart=/usr/bin/python3 -B "+str(guard.WORKER)+" rollback").encode(), + ("ExecStart=/usr/bin/python3 -B "+str(worker)).encode()) + replacement_timer=original_timer.replace( + ("Unit="+guard.UNIT+".service").encode(),("Unit="+name+".service").encode()) + need(replacement!=original_service and replacement_timer!=original_timer, + "EXPECTED_TEST_ONLY_IDENTITY_REPLACEMENTS") + need(b"OnActiveSec=480s\n" in replacement_timer and + b"Persistent=true\n" in replacement_timer and + b"OnCalendar=" in replacement_timer,"PRODUCTION_TIMER_SEMANTICS") + service.write_bytes(replacement);timer.write_bytes(replacement_timer) + enabled=False + try: + run("/usr/bin/systemctl","daemon-reload") + run("/usr/bin/systemctl","start",name+".timer") + need(run("/usr/bin/systemctl","show",name+".timer", + "-p","ActiveState","--value")=="active","TIMER_STARTED") + need(not marker.exists(),"EARLY_TRIGGER") + time.sleep(1) + run("/usr/bin/systemctl","stop",name+".timer") + need(not marker.exists(),"TRIGGER_WHILE_INITIAL_TIMER_ACTIVE") + need(run("/usr/bin/systemctl","show",name+".timer", + "-p","ActiveState","--value")=="inactive","TIMER_STOPPED") + wait=deadline+2-time.time() + if wait>0:time.sleep(wait) + need(not marker.exists(),"TRIGGER_WHILE_TIMER_STOPPED") + started=time.monotonic() + run("/usr/bin/systemctl","start",name+".timer") + while time.monotonic()-started<8 and not marker.exists(): + time.sleep(0.15) + need(marker.is_file() and not marker.is_symlink(), + "PERSISTENT_MISSED_CALENDAR_CATCHUP_NOT_FIRED") + payload=json.loads(marker.read_text()) + need(type(payload.get("pid")) is int and type(payload.get("utc")) in (int,float), + "BAD_SERVICE_RECEIPT") + need(time.monotonic()-started<8,"MISSED_CATCHUP_DEADLINE") + print(json.dumps({ + "revision":"R566","status":"PASS_EPHEMERAL_SYSTEMD_PERSISTENT_MISSED_CALENDAR_CATCHUP", + "identity":"GITHUB_HOSTED_ONLY","productionGuardSha256":PIN_GUARD, + "productionUnitDirectiveIdentity":True, + "triggeredWhileInactive":False, + "triggeredAfterReactivation":True, + "elapsedSeconds":round(time.monotonic()-started,3), + "deadlineUnix":deadline, + "actualFullRebootTested":False, + "realUserMachineChanged":False, + "networkOrTailscaleChanged":False + },sort_keys=True)) + finally: + subprocess.run(["/usr/bin/systemctl","stop",name+".timer"], + capture_output=True,text=True,timeout=10,check=False) + subprocess.run(["/usr/bin/systemctl","stop",name+".service"], + capture_output=True,text=True,timeout=10,check=False) + for target,want in ((service,replacement),(timer,replacement_timer)): + if target.is_file() and not target.is_symlink() and target.read_bytes()==want: + target.unlink() + subprocess.run(["/usr/bin/systemctl","daemon-reload"], + capture_output=True,text=True,timeout=10,check=False) + +if __name__=="__main__": + try:main() + except BaseException as exc: + print(json.dumps({"revision":"R566","status":"FAIL_CLOSED", + "reason":str(exc)[:400]}),file=sys.stderr) + raise SystemExit(96) From 0c981eba0512b569696c09627e4297dca67cfb10 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:58:58 +0330 Subject: [PATCH 045/188] docs(brain): accept R566 native systemd missed-deadline catch-up and exact CI receipts --- PROJECT_BRAIN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index dd907a94..e07656c1 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,6 +6,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. +**R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. + **R565 native failure-path acceptance (2026-10-16), HOSTED PASS / PHYSICAL UNTESTED:** Production binaries, the Linux RC1 ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` and deterministic private Windows Setup `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` are unchanged. Test/CI-only commit `73a97061c063e8959b5b67cd247bbe325f4e4c79` added a separate disposable Ubuntu native systemd negative scenario: after **explicitly fabricated** Root acceptance but before User commit, mutate one synthetic `settings.json`; the exact protected worker must reject the forward User commit, return nonzero, report `DEGRADED_UNRESOLVED`, preserve both Root accepted phase and uncommitted User slot and leave all other managed files/resolver/hosts intact. [PR CI 38821938360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38821938360) and [Push CI 38821931613](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38821931613) fully **SUCCESS**, native negative job `PASS_EPHEMERAL_SYNTHETIC_MUTABLE_DRIFT_FAIL_CLOSED_AFTER_ACCEPTED_ROOT` on both independent runners. Push Windows stable→private RC1→stable rollback PASS; exact private Setup digest as above and unique receipt SHA256 `EEDE2C598B6362844C9C7FBB5BE3DC810DE0FC9813D7052FC3F1E3815404C01A`. This rejects silent user-data overwrite after irreversible Root acceptance; synthetic authorization is **not** user/physical approval. The production MMZ connector remains offline (`Tunnel-client has not been seen for 300 seconds`), and prior official `pkexec` requests remain blocked by its host policy. **No Root guard armed, no actual RC1 installed, no real reboot/DoH/HTTPS/Tailscale acceptance and no public release promotion.** **R559 retained hosted/physical provenance:** [Push CI 38815683495](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815683495) and [PR CI 38815691160](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38815691160) both **SUCCESS** at `e14ad473e1eac36208de20e19cd8c817c3fd69cd`. The independent Root/User upgrade watchdog retained immutable UTC timer deadline across native systemd timer restart with `OnActiveSec=480s`, `OnCalendar`, `Persistent=true`, and restored two-sided bytes on hosted Ubuntu. Exact private SHA256 `CA249D9792C69320E934C53888EFB577729AB4A616A8951EF1736ED7161445DE` (8,360,956 bytes), staged on MMZ for R560 with receipt `6EB5312B82563A12A301945B10F3E6456A6E7872C86D264E1DE2CB4CC0AD2EE9`; physical rootless R561 receipt `51CF09BA0DCDCF47D1350A346D71A8BB5AF0DDA25CBA6796E2E0D72EC4CE9FB1`. These are historical exact-scope PASSES and NOT R562 or physical guarded install acceptance. From 8b930ef1366fc7146cea2059a62bd5554341d49d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:07:46 +0330 Subject: [PATCH 046/188] test(linux): R567 fail-closed native User rollback on denied Polkit authorization --- .github/workflows/ci.yml | 26 +++ tests/r567_hosted_denied_polkit_rollback.py | 165 ++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 tests/r567_hosted_denied_polkit_rollback.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad94675c..8241cc21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -327,6 +327,32 @@ jobs: # Unique synthetic root-owned unit on an EMPTY GitHub-hosted VM only. sudo -n /usr/bin/env GITHUB_ACTIONS=true CI=true RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_REPOSITORY="$GITHUB_REPOSITORY" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" GITHUB_RUN_ID="$GITHUB_RUN_ID" /usr/bin/python3 -B tests/r566_native_systemd_persistent_catchup.py + linux-r567-denied-polkit-ephemeral: + name: linux-r567-denied-polkit-ephemeral + needs: linux-source + runs-on: ubuntu-latest + timeout-minutes: 8 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R567 build exact private RC1, unchanged public stable + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R567 original production Polkit-denied upgrade preserves stable Root User + shell: bash + run: | + set -euo pipefail + # Empty GitHub-hosted Ubuntu only. The shell-level Polkit stub + # RETURNS 126 without opening authentication or touching network. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r567_hosted_denied_polkit_rollback.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py new file mode 100644 index 00000000..19bfcd21 --- /dev/null +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""R567: exact production installer auth-denial branch with native User stage/rollback. + +Hosted ephemeral GitHub Ubuntu ONLY. Exercise original source snippets of +rollback_failed_install, backend_matches_current and the single Polkit gate in +a real unprivileged runner process. Native v1.5.4 user and protected v1.5.2 +baselines are bytes from the official published release. Polkit is a *shell +function stub that returns 126*; no admin prompt or privileged upgrade is +performed. A refused request MUST restore the staged User transaction exactly, +without a second prompt, protected source mutation, DNS, or user data changes. +""" +from __future__ import annotations +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import pwd +import re +import subprocess +import sys +import textwrap +BASE=Path(__file__).resolve().parents[1] +R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" +R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" +ARCHIVE="B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471" + +def require(v,code): + if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) + +def sha(path): + h=hashlib.sha256() + with Path(path).open("rb") as f: + for b in iter(lambda:f.read(1048576),b""):h.update(b) + return h.hexdigest().upper() + +def imported(path,label): + spec=importlib.util.spec_from_file_location(label,path) + require(spec is not None and spec.loader is not None,"IMPORT_"+label) + out=importlib.util.module_from_spec(spec) + spec.loader.exec_module(out) + return out + +def excerpt(data,start,following): + require(data.count(start)==1,"EXPECTED_SINGLE_"+str(len(start))) + tail=data.split(start,1)[1] + require(tail.count(following)==1,"END_ANCHOR_"+str(len(following))) + return start+tail.split(following,1)[0] + +def hosted(): + require(os.geteuid()==0 and sys.platform=="linux","ROOT_HOSTED") + e=os.environ + require(e.get("CI")=="true" and e.get("GITHUB_ACTIONS")=="true" and + e.get("RUNNER_ENVIRONMENT")=="github-hosted" and + e.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod" and + Path(e.get("GITHUB_WORKSPACE","")).resolve()==BASE, + "FRESH_GITHUB_VM_ONLY") + runner=pwd.getpwnam("runner") + require(runner.pw_dir=="/home/runner" and runner.pw_uid>0,"RUNNER") + for p in ("/var/lib/directinternetmethod","/usr/lib/directinternetmethod", + "/home/runner/.local/share/DirectInternetMethod", + "/home/runner/.local/state/DirectInternetMethod"): + require(not Path(p).exists(),"INITIAL_FOREIGN_INSTALL_"+Path(p).name) + return runner + +def main(): + runner=hosted() + r528=imported(R528,"r567_r528") + r529=imported(R529,"r567_r529") + rootmod=r528.module() + stable,pkg,temp,user=r528.prerequisites() + require(user.pw_uid==runner.pw_uid and sha(pkg)==ARCHIVE,"PINNED_ARCHIVE") + r529.require_clean_home(user) + extracted=r528.stage_exact_candidate(pkg,temp) + r529.make_source_runner_owned(extracted,user) + app,original,priv=r529.prepare_user_from_official(stable,user) + protected=r528.seed_real_protected_root(stable,user,rootmod) + dns={name:sha(name) for name in ("/etc/resolv.conf","/etc/hosts")} + symlink=os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None + user_tx=extracted/"app/user_update_transaction.py" + installer=extracted/"install.sh" + source=installer.read_text(encoding="utf8") + require(source.startswith("#!/usr/bin/env bash\nset -euo pipefail\n"), + "INSTALLER_IDENTITY") + rollback=excerpt(source,"rollback_failed_install() {","\n\nfor c in ") + backend=excerpt(source,"backend_matches_current() {", + "\n\n# Administrative authorization is required only") + auth=excerpt(source,"# Administrative authorization is required only", + "\nbackend_matches_current || ") + require(auth.count("pkexec /bin/bash")==2 and + 'ROOT_UPGRADE_STAGED=1' in auth,"POLKIT_BRANCH_PROVENANCE") + require("pkexec /usr/bin/python3" in rollback and + 'if [ "$ROOT_UPGRADE_STAGED" -eq 1 ]' in rollback, + "ROLLBACK_PROVENANCE") + require(rootmod.RootTransaction().status()["status"]=="NO_PENDING_UPGRADE", + "PROTECTED_PRESTATE") + r529.as_runner(user,user_tx,"--stage","--source",extracted) + pending=Path(user.pw_dir)/".local/state/DirectInternetMethod/update-pending" + require((pending/"manifest.json").is_file(),"USER_STAGE_MISSING") + r529.unchanged_records(original) + r529.unchanged_records(priv) + # Only Bash *functions* from original installed package are executed; + # the counterfeit "pkexec" function cannot invoke privileged actions. + script=( + "set -euo pipefail\n" + "HERE="+subprocess.list2cmdline([str(extracted)])+"\n" + "USER_TRANSACTION="+subprocess.list2cmdline([str(user_tx)])+"\n" + "ROOT_TRANSACTION="+subprocess.list2cmdline([str(extracted/"system/protected_upgrade_transaction.py")])+"\n" + "ROOT_UPGRADE_STAGED=0\nTRANSACTION_ACTIVE=1\n" + "pkexec() { printf 'R567_POLKIT_DENIED_BY_OWNER\\n' >&2; return 126; }\n" + +rollback+"\n\n"+backend+"\n\n" + "trap 'rollback_failed_install \"$?\"' ERR\n" + "trap 'rollback_failed_install 130' INT\n" + "trap 'rollback_failed_install 143' TERM\n" + "trap 'rollback_failed_install 129' HUP\n" + +auth+"\n" + "printf 'R567_DANGER_UNREACHABLE_AFTER_AUTH_DENIAL\\n'\n" + ) + process=subprocess.run( + ["/usr/sbin/runuser","-u",user.pw_name,"--","/usr/bin/env", + "HOME="+user.pw_dir,"PYTHONDONTWRITEBYTECODE=1", + "/bin/bash","-c",script],capture_output=True,text=True,timeout=30,check=False) + require(process.returncode==126,"WRONG_REFUSAL_EXIT_"+str(process.returncode)) + require(process.stderr.count("R567_POLKIT_DENIED_BY_OWNER")==1, + "MULTIPLE_PRIVILEGED_REQUESTS_ON_DENIAL") + require("R567_DANGER_UNREACHABLE_AFTER_AUTH_DENIAL" not in process.stdout, + "INSTALLER_IGNORED_AUTH_REFUSAL") + require(not pending.exists() and + rootmod.RootTransaction().status()["status"]=="NO_PENDING_UPGRADE", + "PENDING_LEFT_AFTER_REFUSED_AUTH") + r529.unchanged_records(original) + r529.unchanged_records(priv) + for name,expected in protected.items(): + require(sha(name).lower()==expected,"ROOT_MUTATION_"+Path(name).name) + for name,expected in dns.items(): + require(sha(name)==expected,"NETWORK_MUTATION_"+Path(name).name) + require((os.readlink("/etc/resolv.conf") if + Path("/etc/resolv.conf").is_symlink() else None)==symlink, + "RESOLVER_SYMLINK_MUTATION") + require(not Path("/var/lib/directinternetmethod/upgrade-guard").exists(), + "AUTH_REFUSAL_CREATED_ROOT_GUARD") + require(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.4", + "USER_VERSION_NOT_ROLLED_BACK") + require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.2", + "ROOT_VERSION_UNEXPECTED") + print(json.dumps({"revision":"R567", + "status":"PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK", + "productionInstallerOriginalAuthorizationBranch":True, + "nativeUnprivilegedTransactionStaged":True, + "officialStableRootAndUserBytesRestored":True, + "singleDeniedAuthorizationAttempt":True, + "correctDeniedExitCode":126, + "rootGuardAndRootPendingAbsent":True, + "privateSettingsPreserved":True, + "dnsAndHostsUnchanged":True, + "physicalOwnerHostUntouched":True, + "syntheticPolkitDenialOnly":True, + "actualAdminDialogOpened":False},sort_keys=True)) + +if __name__=="__main__": + try:main() + except BaseException as exc: + print(json.dumps({"revision":"R567","status":"FAIL_CLOSED", + "reason":str(exc)[:500]}),file=sys.stderr) + raise SystemExit(96) From 50914144a8dc1daa64cd4d0d1b214ed5c3deb011 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:11:49 +0330 Subject: [PATCH 047/188] docs(brain): capture R567 native single denied-Polkit rollback evidence and MMZ blocker --- PROJECT_BRAIN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index e07656c1..393cbae8 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,6 +6,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. +**R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. + **R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. **R565 native failure-path acceptance (2026-10-16), HOSTED PASS / PHYSICAL UNTESTED:** Production binaries, the Linux RC1 ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` and deterministic private Windows Setup `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` are unchanged. Test/CI-only commit `73a97061c063e8959b5b67cd247bbe325f4e4c79` added a separate disposable Ubuntu native systemd negative scenario: after **explicitly fabricated** Root acceptance but before User commit, mutate one synthetic `settings.json`; the exact protected worker must reject the forward User commit, return nonzero, report `DEGRADED_UNRESOLVED`, preserve both Root accepted phase and uncommitted User slot and leave all other managed files/resolver/hosts intact. [PR CI 38821938360](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38821938360) and [Push CI 38821931613](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38821931613) fully **SUCCESS**, native negative job `PASS_EPHEMERAL_SYNTHETIC_MUTABLE_DRIFT_FAIL_CLOSED_AFTER_ACCEPTED_ROOT` on both independent runners. Push Windows stable→private RC1→stable rollback PASS; exact private Setup digest as above and unique receipt SHA256 `EEDE2C598B6362844C9C7FBB5BE3DC810DE0FC9813D7052FC3F1E3815404C01A`. This rejects silent user-data overwrite after irreversible Root acceptance; synthetic authorization is **not** user/physical approval. The production MMZ connector remains offline (`Tunnel-client has not been seen for 300 seconds`), and prior official `pkexec` requests remain blocked by its host policy. **No Root guard armed, no actual RC1 installed, no real reboot/DoH/HTTPS/Tailscale acceptance and no public release promotion.** From 3d35c3d28e45d5b9d8447b8b43071d3486d3fc06 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:28:43 +0330 Subject: [PATCH 048/188] fix(linux): R568 explicitly retire SHA-attested terminal rollback slots without deleting unknown files --- .github/workflows/ci.yml | 45 ++++-- PROJECT_BRAIN.md | 2 + linux/system/upgrade_guard.py | 132 +++++++++++++++++- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 54 ++++++- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 10 files changed, 226 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8241cc21..7e80b784 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471" - assert p.is_file() and p.stat().st_size==8363886 + expected="DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994" + assert p.is_file() and p.stat().st_size==8370420 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -144,7 +144,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -173,7 +173,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -206,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -235,7 +235,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -264,7 +264,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -294,7 +294,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -339,7 +339,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -353,6 +353,33 @@ jobs: # RETURNS 126 without opening authentication or touching network. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r567_hosted_denied_polkit_rollback.py + linux-r568-retire-rolledback-ephemeral: + name: linux-r568-retire-rolledback-ephemeral + needs: linux-rc1-native-guard-ephemeral + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: R568 exact Linux RC1 package and official stable baseline + shell: bash + run: | + set -euo pipefail + python3 build_linux_package.py + printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" + curl --fail --show-error --silent --location --retry 3 --max-time 120 \ + 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ + --output "$RUNNER_TEMP/DIM_R528_STABLE.zip" + printf '%s %s\n' '4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7' "$RUNNER_TEMP/DIM_R528_STABLE.zip" | sha256sum --check --status + - name: R568 actual guarded rollback and operator-receipt SHA retirement + shell: bash + run: | + set -euo pipefail + # Native root-owned service on disposable hosted Ubuntu only. + # Post-rollback operator attests exact receipt; unknown foreign + # guard files and mismatched hashes are refused without deletion. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" DIM_R555_EXACT_WRAPPER=true DIM_R568_TEST_RETIRE_ROLLED_BACK=true /usr/bin/python3 -B tests/r554_native_guard_ephemeral_contract.py + linux-public-v154-regression: name: linux-public-v154-regression runs-on: ubuntu-latest diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 393cbae8..e2c31102 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,6 +6,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. +**Current R568 candidate (2026-10-16) — bounded post-rollback retirement; private QA and physical acceptance PENDING:** Source review found that after a successful native Root/User rollback the owned `upgrade-guard/` scripts+systemd timer/service and `upgrade-pending/` Root audit backups were deliberately preserved, but no operator-reviewed retirement existed; a future protected upgrade would fail closed on existing slots. R568 adds an explicit, **Root-only** `upgrade_guard.py retire-rolledback --receipt-sha256 ` maintenance action, NOT auto-cleanup and NOT authorization bypass. It requires the operator to export and attest the real `rollback-result.json` SHA before invoking; checks terminal Root `ROLLED_BACK`, independent `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION` with both sides PASS, restored exact Root/User metadata/code and DNS, no active DIM engine, service inactive, strict known root-owned slots/unit files with zero foreign nodes/symlinks/hardlinks; only then disables the timer and removes whitelisted guard/Root transaction files, leaving the original single installed v1.5.4 and its private data untouched. Any suspicious receipt/foreign ownership/unknown files **fails closed**. R568 scratch source was built deterministically on Emad Linux in a separate user-owned checkout, no installed/network changes, SHA256 guarded file `3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09`, wrapper `308CA07DF77F04792219C201894AA55455F2F612145265B3BD8AF963BAB17110`, private Linux 27-file package SHA256 `DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994` (8,370,420 bytes). Existing 18 source tests PASS in scratch; an exact native hosted R568 integration tests wrong receipt SHA and synthetic foreign-owned/extra entry rejection, then authorized retirement and next-update preflight. **No native R568 CI acceptance or real operator evidence yet**, so R568 stays private Draft, not an accepted physical maintenance method. The earlier R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is retained for historical source-matched native evidence, not current candidate replacement until R568 CI PASS. MMZ remains off at owner's instruction; Emad systems continue running public v1.5.4 and Tailscale unchanged. + **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. **R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 7941d558..ced8c845 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -374,11 +374,138 @@ def rollback_worker(): "NO_ROLLBACK_ALREADY_COMMITTED","NO_CHANGE_NO_PENDING", "PASS_AUTONOMOUS_TWO_SIDED_RESTORATION") else 90 +def _exact_owned_inventory(root:Path,files:set[str],dirs:set[str],mode:int): + """Allowlist every file/dir; no symlinks, foreign nodes or hardlinks.""" + need(root.is_dir() and not root.is_symlink(),"RETIRE_ROOT_UNSAFE") + st=root.stat() + need(st.st_uid==0 and stat.S_IMODE(st.st_mode)==mode, + "RETIRE_ROOT_OWNER_MODE") + actual_files=set();actual_dirs=set() + for item in root.rglob("*"): + rel=item.relative_to(root).as_posix() + s=item.lstat() + need(s.st_uid==0,"RETIRE_FOREIGN_OWNER_"+rel) + if stat.S_ISDIR(s.st_mode): + actual_dirs.add(rel) + elif stat.S_ISREG(s.st_mode): + need(s.st_nlink==1,"RETIRE_HARDLINK_"+rel) + actual_files.add(rel) + else: + raise RuntimeError("RETIRE_FOREIGN_NODE_"+rel) + need(actual_files==files and actual_dirs==dirs,"RETIRE_FOREIGN_ENTRY_SET") + return True + +def retire_rolledback(receipt_sha256:str): + """Narrow operator-authenticated *post-rollback* recovery-slot retirement. + + Never auto-delete audit or live installation. An administrator must first + export the unique rollback evidence to project Brain and supply the exact + receipt SHA256. Root/User baseline must be byte-perfect; the guard and + root-owned slots are only retired when already terminal ROLLED_BACK. + Terminal COMMITTED, ambiguous, partial, live or foreign states are refused. + """ + need(os.geteuid()==0 and Path(__file__).resolve()==WORKER, + "RETIRE_OWNED_ROOT_WORKER_REQUIRED") + need(type(receipt_sha256) is str and len(receipt_sha256)==64 and + all(ch in "0123456789abcdefABCDEF" for ch in receipt_sha256), + "RETIRE_EXACT_OPERATOR_RECEIPT_SHA_REQUIRED") + j=verify_guard_files() + regular(RESULT,0,0o600) + digest=sha_file(RESULT) + need(digest==receipt_sha256.upper(),"RETIRE_RECEIPT_SHA_CHANGED") + evidence=json.loads(RESULT.read_text("utf8")) + need(evidence.get("revision")=="R552" + and evidence.get("status")=="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" + and evidence.get("rootRollback")=="PASS" + and evidence.get("userRollback")=="PASS" + and evidence.get("recoveryAttempted") is True + and evidence.get("networkMutationAttempted") is False, + "RETIRE_ROLLBACK_EVIDENCE_NOT_ACCEPTED") + tx=imported_root() + need(tx.native and tx.status()["status"]=="ROLLED_BACK", + "RETIRE_NONTERMINAL_ROOT") + m=tx.manifest() + need(m.get("phase")=="ROLLED_BACK" and m.get("uid")==j["uid"] and + m.get("home")==j["home"] and m.get("fromVersion")==BASELINE and + m.get("toVersion")==VERSION,"RETIRE_FOREIGN_ROOT_TRANSACTION") + tx._validate_slot(m) + states,metadata=tx._target_states(m) + need(set(states)==set(m["files"]) and + all(states[name]==row["old"] for name,row in m["files"].items()) and + metadata==m["oldMetadataSHA"],"RETIRE_OLD_PROTECTED_BITS_NOT_RESTORED") + home=Path(j["home"]) + need(not (home/USER_REL/"directmethod/state.json").exists(), + "RETIRE_LIVE_DIRECT_ENGINE") + verify_installed_after_restore(j) + # Full inventory before deleting anything, including directories. + names=set(m["files"]) + slotfiles={"manifest.json","old/metadata","new/metadata"} + slotfiles.update("new/"+name for name in names) + slotfiles.update("old/"+name for name,row in m["files"].items() if row["old"]) + _exact_owned_inventory(tx.pending,slotfiles,{"old","new"},0o700) + guardfiles={"baseline.json","rollback-result.json", + "worker.py","root_tx.py","user_tx.py"} + _exact_owned_inventory(GUARD,guardfiles,set(),0o711) + unit_sha={} + for item in (SERVICE,TIMER): + regular(item,0,0o644) + need(item.stat().st_nlink==1,"RETIRE_HARDLINK_UNIT") + unit_sha[item.name]=sha_file(item) + for item in [*([tx.pending/p for p in slotfiles]), + BASELINE_FILE,RESULT,WORKER,ROOT_TX,USER_TX,SERVICE,TIMER]: + need(item.is_file() and not item.is_symlink() and item.stat().st_uid==0 + and item.stat().st_nlink==1,"RETIRE_NAMED_ENTRY_CHANGED_"+item.name) + state=command(["/usr/bin/systemctl","show",UNIT+".service", + "--property=ActiveState","--no-pager"]) + need(state.returncode==0 and + ("ActiveState=inactive" in state.stdout or + "ActiveState=failed" in state.stdout), + "RETIRE_SERVICE_IS_BUSY") + # No destructive operation before all checks above. After successful + # restoration, first DISARM the timer and only then remove exact owned + # transaction files. A partial maintenance interruption remains visibly + # fail-closed, never auto-overwrites foreign content on a retry. + disarmed=command(["/usr/bin/systemctl","disable","--now",UNIT+".timer"]) + need(disarmed.returncode==0,"RETIRE_TIMER_DISARM_FAILED") + for rel in sorted(slotfiles): + p=tx.pending/rel + need(p.is_file() and not p.is_symlink() and p.stat().st_uid==0, + "RETIRE_SLOT_RACE_"+rel) + p.unlink() + for name in ("new","old"): + (tx.pending/name).rmdir() + tx.pending.rmdir() + fsync_dir(tx.pending.parent) + for p in (ROOT_TX,USER_TX,BASELINE_FILE,WORKER,RESULT): + need(p.is_file() and not p.is_symlink() and p.stat().st_uid==0, + "RETIRE_GUARD_RACE_"+p.name) + p.unlink() + GUARD.rmdir() + fsync_dir(GUARD.parent) + for p in (SERVICE,TIMER): + need(p.is_file() and not p.is_symlink() and + sha_file(p)==unit_sha[p.name],"RETIRE_UNIT_CHANGED_"+p.name) + p.unlink() + fsync_dir(SERVICE.parent) + reload=command(["/usr/bin/systemctl","daemon-reload"]) + need(reload.returncode==0,"RETIRE_SYSTEMD_RELOAD_FAILED") + need(tx.status()["status"]=="NO_PENDING_UPGRADE" + and not GUARD.exists() and not TIMER.exists() and not SERVICE.exists(), + "RETIRE_POSTCLEANUP_SLOT_LEFT") + need(sha_file(Path("/etc/resolv.conf"))==j["oldResolverSha"], + "RETIRE_DNS_CHANGED") + return {"status":"PASS_AUTHORIZED_BOUNDED_ROLLBACK_RETIRE", + "receiptSha256":digest, + "oldRootVersion":BASELINE,"oldUserVersion":OLD_USER, + "fileScopesExact":True,"networkMutationAttempted":False, + "timerDisarmed":True,"repeatUpgradeUnblocked":True} + def main(): parser=argparse.ArgumentParser() - parser.add_argument("action",choices=("preflight","arm","status","rollback")) + parser.add_argument("action",choices=("preflight","arm","status","rollback","retire-rolledback")) parser.add_argument("--source",type=Path) parser.add_argument("--uid",type=int) + parser.add_argument("--receipt-sha256") opts=parser.parse_args() if opts.action in ("preflight","arm"): need(opts.source is not None,"SOURCE_REQUIRED") @@ -387,6 +514,9 @@ def main(): else: need(opts.uid is not None,"UID_REQUIRED") print(json.dumps(arm(opts.source,opts.uid),sort_keys=True)) + elif opts.action=="retire-rolledback": + need(opts.receipt_sha256 is not None,"RETIRE_RECEIPT_REQUIRED") + print(json.dumps(retire_rolledback(opts.receipt_sha256),sort_keys=True)) elif opts.action=="status": need(os.geteuid()==0,"ROOT_REQUIRED") print(json.dumps({"status":"ARMED",**verify_timer()},sort_keys=True)) diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 21aba1d8..83176e5e 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" +GUARD_SHA="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index d0239d3c..c0606e2e 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471"} +EXPECTED={"version":VERSION,"files":27,"sha256":"DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 347f8cb8..02828154 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="b2960e957d2314f43da719aafe097e48fdd3caa647a02844333818dc50a37471" +CANDIDATE_SHA="dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 16e5ba7d..2326229c 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" +SOURCE_SHA="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index b2f88d67..805f7821 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471" +PIN_PACKAGE="DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" -PIN_GUARD="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" +PIN_GUARD="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") @@ -310,12 +310,60 @@ def run(): need(sha(p)==want,"NETWORK_FILE_MUTATED") need(before_link==(os.readlink("/etc/resolv.conf") if Path("/etc/resolv.conf").is_symlink() else None),"RESOLVER_SYMLINK_MUTATED") + # R568 is an explicit root-authorized maintenance command AFTER verified + # terminal two-sided rollback; never invoked by a live timer or installer. + retire=os.environ.get("DIM_R568_TEST_RETIRE_ROLLED_BACK")=="true" + retired=False + if retire: + need(not synthetic_promotion and not synthetic_reject, + "RETIRE_TEST_NEEDS_REAL_ROLLBACK") + need(root_tx.status()["status"]=="ROLLED_BACK" + and root_tx.pending.is_dir() and guard.GUARD.is_dir(), + "RETIRE_TEST_MISSING_REAL_TERMINAL_SLOT") + sha_receipt=sha(SEEN) + cli=["/usr/bin/python3","-B",str(guard.WORKER), + "retire-rolledback","--receipt-sha256"] + import subprocess + denied=subprocess.run(cli+["0"*64],text=True,capture_output=True, + timeout=12,check=False) + need(denied.returncode!=0 and + "RETIRE_RECEIPT_SHA_CHANGED" in denied.stderr, + "RETIRE_BAD_EVIDENCE_NOT_REJECTED") + need(root_tx.pending.is_dir() and guard.GUARD.is_dir() + and guard.TIMER.exists(),"RETIRE_BAD_EVIDENCE_MUTATED_SLOT") + foreign=guard.GUARD/"r568-synthetic-foreign.txt" + need(not foreign.exists(),"RETIRE_SYNTHETIC_FOREIGN_PREEXISTS") + foreign.write_text("foreign-test") + foreign.chmod(0o600) + refused=subprocess.run(cli+[sha_receipt],text=True,capture_output=True, + timeout=12,check=False) + need(refused.returncode!=0 and + "RETIRE_FOREIGN_ENTRY_SET" in refused.stderr, + "RETIRE_FOREIGN_ENTRY_NOT_REJECTED") + need(root_tx.pending.is_dir() and guard.GUARD.is_dir() + and guard.TIMER.exists(),"RETIRE_FOREIGN_ENTRY_MUTATED_SLOT") + foreign.unlink() # remove ONLY our synthetic test-owned file + result=command(cli+[sha_receipt],40) + doc=json.loads(result.splitlines()[-1]) + need(doc["status"]=="PASS_AUTHORIZED_BOUNDED_ROLLBACK_RETIRE" + and doc["receiptSha256"]==sha_receipt, + "RETIRE_BOUNDED_RESULT_NOT_ACCEPTED") + need(root_tx.status()["status"]=="NO_PENDING_UPGRADE" + and not guard.GUARD.exists() and not guard.SERVICE.exists() + and not guard.TIMER.exists(),"RETIRE_LEFT_OWNED_ROOT_SLOT") + pf=guard.preflight(extracted) + need(pf["status"]=="SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY", + "RETIRE_NEXT_SAFE_UPDATE_STILL_BLOCKED") + retired=True # No reboot, no network/Tailscale manipulation, and no second install. need(not (app/"directmethod/state.json").exists(),"ACTIVE_ENGINE_UNEXPECTED") - command(["/usr/bin/systemctl","disable","--now",NAME+".timer"]) + if not retired: + command(["/usr/bin/systemctl","disable","--now",NAME+".timer"]) print(json.dumps({ "syntheticRootAcceptanceOnly":synthetic_promotion, "acceptedRootUserInterruptedResume":synthetic_promotion and not synthetic_reject, + "operatorAttestedRollbackSlotRetired":retired, + "repeatUpgradeSafelyUnblocked":retired, "expectedFailClosedSyntheticMutableDrift":synthetic_reject, "revision":"R554","status":( "PASS_EPHEMERAL_SYNTHETIC_MUTABLE_DRIFT_FAIL_CLOSED_AFTER_ACCEPTED_ROOT" diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 02093fbd..8a9044a3 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3" +PIN_GUARD="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 19bfcd21..1c8b3f0f 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471" +ARCHIVE="DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 1f169373c7ca34103a969c89c4c3d5eb937b245b Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:31:06 +0330 Subject: [PATCH 049/188] test(linux): R568 preserve watchdog receipt digest before attested retirement --- tests/r554_native_guard_ephemeral_contract.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 805f7821..556ce3e3 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -227,6 +227,7 @@ def run(): command(["/usr/bin/systemctl","start",NAME+".service"],100) need(SEEN.is_file() and not SEEN.is_symlink(),"SYSTEMD_RECEIPT_MISSING") receipt=json.loads(SEEN.read_text()) + receipt_sha_before_retirement=sha(SEEN) if synthetic_reject: need(receipt.get("status")=="DEGRADED_UNRESOLVED" and receipt.get("uid")==0 @@ -385,7 +386,7 @@ def run(): "userFileCount":len(original_user), "privateUserFileCount":len(private), "serviceDurationSeconds":round(time.monotonic()-start,3), - "watchdogReceiptSHA256":sha(SEEN)},sort_keys=True)) + "watchdogReceiptSHA256":receipt_sha_before_retirement},sort_keys=True)) if __name__=="__main__": try:run() except BaseException as e: From ccd18c16de1ab3de1aca97b72a4096bdac40ed83 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:35:12 +0330 Subject: [PATCH 050/188] fix(linux): R568 clean persistent systemd timer state during attested terminal rollback retirement --- .github/workflows/ci.yml | 20 +++++++++---------- PROJECT_BRAIN.md | 2 +- linux/system/upgrade_guard.py | 4 ++++ linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 7 +++++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 10 files changed, 26 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e80b784..dfe36066 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994" - assert p.is_file() and p.stat().st_size==8370420 + expected="9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC" + assert p.is_file() and p.stat().st_size==8370720 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -144,7 +144,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -173,7 +173,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -206,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -235,7 +235,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -264,7 +264,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -294,7 +294,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -339,7 +339,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -365,7 +365,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index e2c31102..813dc595 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,7 +6,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. -**Current R568 candidate (2026-10-16) — bounded post-rollback retirement; private QA and physical acceptance PENDING:** Source review found that after a successful native Root/User rollback the owned `upgrade-guard/` scripts+systemd timer/service and `upgrade-pending/` Root audit backups were deliberately preserved, but no operator-reviewed retirement existed; a future protected upgrade would fail closed on existing slots. R568 adds an explicit, **Root-only** `upgrade_guard.py retire-rolledback --receipt-sha256 ` maintenance action, NOT auto-cleanup and NOT authorization bypass. It requires the operator to export and attest the real `rollback-result.json` SHA before invoking; checks terminal Root `ROLLED_BACK`, independent `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION` with both sides PASS, restored exact Root/User metadata/code and DNS, no active DIM engine, service inactive, strict known root-owned slots/unit files with zero foreign nodes/symlinks/hardlinks; only then disables the timer and removes whitelisted guard/Root transaction files, leaving the original single installed v1.5.4 and its private data untouched. Any suspicious receipt/foreign ownership/unknown files **fails closed**. R568 scratch source was built deterministically on Emad Linux in a separate user-owned checkout, no installed/network changes, SHA256 guarded file `3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09`, wrapper `308CA07DF77F04792219C201894AA55455F2F612145265B3BD8AF963BAB17110`, private Linux 27-file package SHA256 `DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994` (8,370,420 bytes). Existing 18 source tests PASS in scratch; an exact native hosted R568 integration tests wrong receipt SHA and synthetic foreign-owned/extra entry rejection, then authorized retirement and next-update preflight. **No native R568 CI acceptance or real operator evidence yet**, so R568 stays private Draft, not an accepted physical maintenance method. The earlier R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is retained for historical source-matched native evidence, not current candidate replacement until R568 CI PASS. MMZ remains off at owner's instruction; Emad systems continue running public v1.5.4 and Tailscale unchanged. +**Current R568 candidate (2026-10-16) — bounded post-rollback retirement; private QA and physical acceptance PENDING:** Source review found that after a successful native Root/User rollback the owned `upgrade-guard/` scripts+systemd timer/service and `upgrade-pending/` Root audit backups were deliberately preserved, but no operator-reviewed retirement existed; a future protected upgrade would fail closed on existing slots. R568 adds an explicit, **Root-only** `upgrade_guard.py retire-rolledback --receipt-sha256 ` maintenance action, NOT auto-cleanup and NOT authorization bypass. It requires the operator to export and attest the real `rollback-result.json` SHA before invoking; checks terminal Root `ROLLED_BACK`, independent `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION` with both sides PASS, restored exact Root/User metadata/code and DNS, no active DIM engine, service inactive, strict known root-owned slots/unit files with zero foreign nodes/symlinks/hardlinks; only then disables the timer, invokes systemd's documented `systemctl clean --what=state` to remove the `Persistent=true` timestamp, and removes whitelisted guard/Root transaction files, leaving the original single installed v1.5.4 and its private data untouched. Source: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . Any suspicious receipt/foreign ownership/unknown files **fails closed**. R568 scratch source was built deterministically on Emad Linux in a separate user-owned checkout, no installed/network changes, SHA256 guarded file `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, wrapper `2584FDA48091458CC93CF89E74A2A03CE62108A2C61E93A58799F954E0030139`, private Linux 27-file package SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` (8,370,720 bytes). Existing 18 source tests PASS in scratch; an exact native hosted R568 integration tests wrong receipt SHA and synthetic foreign extra entry rejection, then authorized retirement, systemd timestamp cleaning, and next-update preflight. The first native CI run failed in its *test report* because it tried to rehash a properly retired receipt; the second run now preserves the pre-retirement digest. This is currently pending acceptance. **No native R568 CI acceptance or real operator evidence yet**, so R568 stays private Draft, not an accepted physical maintenance method. The earlier R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is retained for historical source-matched native evidence, not current candidate replacement until R568 CI PASS. MMZ remains off at owner's instruction; Emad systems continue running public v1.5.4 and Tailscale unchanged. **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index ced8c845..ea71facf 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -467,6 +467,10 @@ def retire_rolledback(receipt_sha256:str): # fail-closed, never auto-overwrites foreign content on a retry. disarmed=command(["/usr/bin/systemctl","disable","--now",UNIT+".timer"]) need(disarmed.returncode==0,"RETIRE_TIMER_DISARM_FAILED") + # systemd Persistent= leaves a separate on-disk last-trigger stamp. + # Use the vendor-supported state-clean command before unit unlinking. + stamp=command(["/usr/bin/systemctl","clean","--what=state",UNIT+".timer"]) + need(stamp.returncode==0,"RETIRE_PERSISTENT_TIMER_STATE_CLEAN_FAILED") for rel in sorted(slotfiles): p=tx.pending/rel need(p.is_file() and not p.is_symlink() and p.stat().st_uid==0, diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 83176e5e..4c984613 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" +GUARD_SHA="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index c0606e2e..9a083d34 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994"} +EXPECTED={"version":VERSION,"files":27,"sha256":"9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 02828154..4edef98e 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="dc80542a4594ab65168d1b4cb067c54741db2d058b33d135af2910db0b0ae994" +CANDIDATE_SHA="9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 2326229c..36379634 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" +SOURCE_SHA="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 556ce3e3..1d1cdcb6 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994" +PIN_PACKAGE="9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" -PIN_GUARD="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" +PIN_GUARD="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") @@ -352,6 +352,9 @@ def run(): need(root_tx.status()["status"]=="NO_PENDING_UPGRADE" and not guard.GUARD.exists() and not guard.SERVICE.exists() and not guard.TIMER.exists(),"RETIRE_LEFT_OWNED_ROOT_SLOT") + stamp=Path("/var/lib/systemd/timers/stamp-"+NAME+".timer") + need(not stamp.exists() and not stamp.is_symlink(), + "RETIRE_SYSTEMD_PERSISTENT_TIMER_STAMP_REMAINS") pf=guard.preflight(extracted) need(pf["status"]=="SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY", "RETIRE_NEXT_SAFE_UPDATE_STILL_BLOCKED") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 8a9044a3..835ae319 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="3E579455ACDB711F3D84F4F5869228DF8DDEE24BF7CBED952C9465C43A8FAC09" +PIN_GUARD="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 1c8b3f0f..dfa25566 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="DC80542A4594AB65168D1B4CB067C54741DB2D058B33D135AF2910DB0B0AE994" +ARCHIVE="9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 98e087fdfe5fd3e1abfde42a81ad881f32313dd7 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:36:21 +0330 Subject: [PATCH 051/188] test(linux): R568 remove shadowed subprocess binding from shared negative runner --- tests/r554_native_guard_ephemeral_contract.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 1d1cdcb6..912d37f5 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -324,7 +324,6 @@ def run(): sha_receipt=sha(SEEN) cli=["/usr/bin/python3","-B",str(guard.WORKER), "retire-rolledback","--receipt-sha256"] - import subprocess denied=subprocess.run(cli+["0"*64],text=True,capture_output=True, timeout=12,check=False) need(denied.returncode!=0 and From 0bd88e96705909bd630279487b909037ef220ab7 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:42:29 +0330 Subject: [PATCH 052/188] docs(brain): accept exact R568 native two-sided rollback retirement and full CI evidence --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 813dc595..1a5fda13 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -6,7 +6,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. -**Current R568 candidate (2026-10-16) — bounded post-rollback retirement; private QA and physical acceptance PENDING:** Source review found that after a successful native Root/User rollback the owned `upgrade-guard/` scripts+systemd timer/service and `upgrade-pending/` Root audit backups were deliberately preserved, but no operator-reviewed retirement existed; a future protected upgrade would fail closed on existing slots. R568 adds an explicit, **Root-only** `upgrade_guard.py retire-rolledback --receipt-sha256 ` maintenance action, NOT auto-cleanup and NOT authorization bypass. It requires the operator to export and attest the real `rollback-result.json` SHA before invoking; checks terminal Root `ROLLED_BACK`, independent `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION` with both sides PASS, restored exact Root/User metadata/code and DNS, no active DIM engine, service inactive, strict known root-owned slots/unit files with zero foreign nodes/symlinks/hardlinks; only then disables the timer, invokes systemd's documented `systemctl clean --what=state` to remove the `Persistent=true` timestamp, and removes whitelisted guard/Root transaction files, leaving the original single installed v1.5.4 and its private data untouched. Source: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . Any suspicious receipt/foreign ownership/unknown files **fails closed**. R568 scratch source was built deterministically on Emad Linux in a separate user-owned checkout, no installed/network changes, SHA256 guarded file `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, wrapper `2584FDA48091458CC93CF89E74A2A03CE62108A2C61E93A58799F954E0030139`, private Linux 27-file package SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` (8,370,720 bytes). Existing 18 source tests PASS in scratch; an exact native hosted R568 integration tests wrong receipt SHA and synthetic foreign extra entry rejection, then authorized retirement, systemd timestamp cleaning, and next-update preflight. The first native CI run failed in its *test report* because it tried to rehash a properly retired receipt; the second run now preserves the pre-retirement digest. This is currently pending acceptance. **No native R568 CI acceptance or real operator evidence yet**, so R568 stays private Draft, not an accepted physical maintenance method. The earlier R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is retained for historical source-matched native evidence, not current candidate replacement until R568 CI PASS. MMZ remains off at owner's instruction; Emad systems continue running public v1.5.4 and Tailscale unchanged. +**Current R568 candidate (2026-10-16), HOSTED NATIVE PASS / PHYSICAL ACCEPTANCE OPEN:** A real Linux lifecycle gap was closed in private source: after accepted two-sided Root/User `ROLLED_BACK`, the retained privileged audit slot and guard systemd units would prevent a subsequent in-place protected upgrade. R568 implements explicit **operator-attested** `upgrade_guard.py retire-rolledback --receipt-sha256 ` (never autonomous deletion). It fail-closes on wrong SHA, foreign nodes, symlinks/hardlinks, unknown owner, nonterminal/COMMITTED/DEGRADED Root, User/data/DNS drift or active network engine. After hash-verified `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION`, strict known Root/User baseline, and inactive service, it disables the timer, invokes official `systemctl clean --what=state` for the `Persistent=true` stamp, and removes **only whitelisted** owned snapshots and units; preflight then allows another protected upgrade with the preserved one stable installation and private settings. Official systemd persistent stamp documentation: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . The real operator must record receipt evidence externally in the single Project Brain before invoking retirement; future human-host cleanup is NOT authorized or completed. + +Exact new source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, wrapper SHA256 `2584FDA48091458CC93CF89E74A2A03CE62108A2C61E93A58799F954E0030139`, reproducible private Linux 27-payload/29-member ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` (8,370,720 bytes). Source commits `ccd18c16de1ab3de1aca97b72a4096bdac40ed83` plus QA-only report fix `98e087fdfe5fd3e1abfde42a81ad881f32313dd7` reached [PR CI 38827562274](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38827562274) and [Push CI 38827556470](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38827556470) both **SUCCESS**. Exact native Ubuntu Root/User Stage→Apply→rollback→operator-retire tests PASS independently on two GitHub-hosted runners: wrong SHA and synthetic foreign entry were refused, trusted receipt retirement and systemd stamp cleanup succeeded, and post-retirement `upgrade_guard.preflight` confirmed `repeatUpgradeSafelyUnblocked=true` with protected baseline, /etc/hosts and resolver byte-unchanged. Source-extracted 18/18 tests PASS. Windows private hosted Stable v1.5.4→RC1→Stable native rollback PASS with unchanged deterministic Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique acceptance receipt SHA256 `0AAC4020DE11175388F59DD971D51AF7CEF73E341039C4206B229623E610B693`. One separate user-owned 39 MB scratch clone on Emad Linux was used for read-only-by-design packaging tests; no running DIM, Tailscale, DNS, Root or public release was modified. Prior R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is historical accepted scoped evidence, superseded for **new** private canaries by R568. **Physical MMZ deliberately OFF by owner; no Root authorization / guarded RC1 installation / GUI DoH or HTTPS or Tailscale / real reboot acceptance.** Remain PR #60 Draft and v1.5.4 public Stable; no FINAL claim. **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. From cc0c6733922be7607c6dbe0fa6f6b24a581f1a5a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:44:21 +0330 Subject: [PATCH 053/188] docs(brain): record R569 MMZ exact RC1 rootless preflight --- PROJECT_BRAIN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 1a5fda13..50821117 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -10,6 +10,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t Exact new source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, wrapper SHA256 `2584FDA48091458CC93CF89E74A2A03CE62108A2C61E93A58799F954E0030139`, reproducible private Linux 27-payload/29-member ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` (8,370,720 bytes). Source commits `ccd18c16de1ab3de1aca97b72a4096bdac40ed83` plus QA-only report fix `98e087fdfe5fd3e1abfde42a81ad881f32313dd7` reached [PR CI 38827562274](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38827562274) and [Push CI 38827556470](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38827556470) both **SUCCESS**. Exact native Ubuntu Root/User Stage→Apply→rollback→operator-retire tests PASS independently on two GitHub-hosted runners: wrong SHA and synthetic foreign entry were refused, trusted receipt retirement and systemd stamp cleanup succeeded, and post-retirement `upgrade_guard.preflight` confirmed `repeatUpgradeSafelyUnblocked=true` with protected baseline, /etc/hosts and resolver byte-unchanged. Source-extracted 18/18 tests PASS. Windows private hosted Stable v1.5.4→RC1→Stable native rollback PASS with unchanged deterministic Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique acceptance receipt SHA256 `0AAC4020DE11175388F59DD971D51AF7CEF73E341039C4206B229623E610B693`. One separate user-owned 39 MB scratch clone on Emad Linux was used for read-only-by-design packaging tests; no running DIM, Tailscale, DNS, Root or public release was modified. Prior R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is historical accepted scoped evidence, superseded for **new** private canaries by R568. **Physical MMZ deliberately OFF by owner; no Root authorization / guarded RC1 installation / GUI DoH or HTTPS or Tailscale / real reboot acceptance.** Remain PR #60 Draft and v1.5.4 public Stable; no FINAL claim. +**R569 exact R568 RC1 physical rootless reproducibility/preflight — 2026-10-16 07:11:44–07:11:50 UTC:** On newly reconnected physical `mmz-LOQ-15IRX9`, a temporary non-root checkout of exact Draft PR #60 head `0bd88e96705909bd630279487b909037ef220ab7` rebuilt 27-file Linux v1.5.5-rc1 candidate ZIP **8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`** byte-identically to pinned R568. Original `tests/r519_versioned_package_contract.py` **15/15 PASS** and `tests/r552_native_upgrade_guard_contract.py` **18/18 PASS** on this physical host; extracted unmodified `system/upgrade_guard.py preflight --source` returned `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` with **installed Root v1.5.2 / User v1.5.4, guardArmed=false, needsRootAuthorization=true**. Pre/post hashes of installed Root/User metadata and `/etc/resolv.conf`, IPv4 default route, active Tailscale, absent DIM State, pending Root/User slots and guard were **identical**; no privileged call, network/service/root/installed mutation or real DoH consent, and the disposable /tmp checkout was removed. Full-completion Commander operation `bff19b4a-2289-460c-8db2-9a1f04e98e8a` exit 0, stdout SHA256 `5d03f5094d07cefd228afb152524a2cbd5a2fcd432e323508fac0a576a89e406`, stderr SHA256 `d28dbc153e1eb4ffe5c4b35a3ef2ad996e03e99dc9b772db3d6d65b2311983c6` (original logs retained in MMZ Commander durable operation store). **Scope PASS:** rootless exact-version package and physical preflight; **still OPEN:** approved production Root watchdog, protected real upgrade, explicit encrypted-DNS consent, native GUI Start/Stop, physical HTTPS/Tailscale recovery, real reboot and owner acceptance. MMZ is reachable by connector again but privileged Polkit invocation remains unavailable through the existing allowlist. Preserve public v1.5.4 Stable and PR #60 Draft; do not promote to FINAL. This is an evidence-only Brain delta and changes no product bytes. + **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. **R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. From 93cc060b5ee0b525b44efca06cda69a5ee0e9784 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:49:27 +0330 Subject: [PATCH 054/188] docs(brain): upsert R569 two-host direct HTTPS baseline --- PROJECT_BRAIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 50821117..7410dece 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -10,7 +10,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t Exact new source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, wrapper SHA256 `2584FDA48091458CC93CF89E74A2A03CE62108A2C61E93A58799F954E0030139`, reproducible private Linux 27-payload/29-member ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` (8,370,720 bytes). Source commits `ccd18c16de1ab3de1aca97b72a4096bdac40ed83` plus QA-only report fix `98e087fdfe5fd3e1abfde42a81ad881f32313dd7` reached [PR CI 38827562274](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38827562274) and [Push CI 38827556470](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38827556470) both **SUCCESS**. Exact native Ubuntu Root/User Stage→Apply→rollback→operator-retire tests PASS independently on two GitHub-hosted runners: wrong SHA and synthetic foreign entry were refused, trusted receipt retirement and systemd stamp cleanup succeeded, and post-retirement `upgrade_guard.preflight` confirmed `repeatUpgradeSafelyUnblocked=true` with protected baseline, /etc/hosts and resolver byte-unchanged. Source-extracted 18/18 tests PASS. Windows private hosted Stable v1.5.4→RC1→Stable native rollback PASS with unchanged deterministic Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique acceptance receipt SHA256 `0AAC4020DE11175388F59DD971D51AF7CEF73E341039C4206B229623E610B693`. One separate user-owned 39 MB scratch clone on Emad Linux was used for read-only-by-design packaging tests; no running DIM, Tailscale, DNS, Root or public release was modified. Prior R562 ZIP `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` is historical accepted scoped evidence, superseded for **new** private canaries by R568. **Physical MMZ deliberately OFF by owner; no Root authorization / guarded RC1 installation / GUI DoH or HTTPS or Tailscale / real reboot acceptance.** Remain PR #60 Draft and v1.5.4 public Stable; no FINAL claim. -**R569 exact R568 RC1 physical rootless reproducibility/preflight — 2026-10-16 07:11:44–07:11:50 UTC:** On newly reconnected physical `mmz-LOQ-15IRX9`, a temporary non-root checkout of exact Draft PR #60 head `0bd88e96705909bd630279487b909037ef220ab7` rebuilt 27-file Linux v1.5.5-rc1 candidate ZIP **8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`** byte-identically to pinned R568. Original `tests/r519_versioned_package_contract.py` **15/15 PASS** and `tests/r552_native_upgrade_guard_contract.py` **18/18 PASS** on this physical host; extracted unmodified `system/upgrade_guard.py preflight --source` returned `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` with **installed Root v1.5.2 / User v1.5.4, guardArmed=false, needsRootAuthorization=true**. Pre/post hashes of installed Root/User metadata and `/etc/resolv.conf`, IPv4 default route, active Tailscale, absent DIM State, pending Root/User slots and guard were **identical**; no privileged call, network/service/root/installed mutation or real DoH consent, and the disposable /tmp checkout was removed. Full-completion Commander operation `bff19b4a-2289-460c-8db2-9a1f04e98e8a` exit 0, stdout SHA256 `5d03f5094d07cefd228afb152524a2cbd5a2fcd432e323508fac0a576a89e406`, stderr SHA256 `d28dbc153e1eb4ffe5c4b35a3ef2ad996e03e99dc9b772db3d6d65b2311983c6` (original logs retained in MMZ Commander durable operation store). **Scope PASS:** rootless exact-version package and physical preflight; **still OPEN:** approved production Root watchdog, protected real upgrade, explicit encrypted-DNS consent, native GUI Start/Stop, physical HTTPS/Tailscale recovery, real reboot and owner acceptance. MMZ is reachable by connector again but privileged Polkit invocation remains unavailable through the existing allowlist. Preserve public v1.5.4 Stable and PR #60 Draft; do not promote to FINAL. This is an evidence-only Brain delta and changes no product bytes. +**R569 exact R568 RC1 physical rootless reproducibility/preflight — 2026-10-16 07:11:44–07:11:50 UTC:** On newly reconnected physical `mmz-LOQ-15IRX9`, a temporary non-root checkout of exact Draft PR #60 head `0bd88e96705909bd630279487b909037ef220ab7` rebuilt 27-file Linux v1.5.5-rc1 candidate ZIP **8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`** byte-identically to pinned R568. Original `tests/r519_versioned_package_contract.py` **15/15 PASS** and `tests/r552_native_upgrade_guard_contract.py` **18/18 PASS** on this physical host; extracted unmodified `system/upgrade_guard.py preflight --source` returned `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` with **installed Root v1.5.2 / User v1.5.4, guardArmed=false, needsRootAuthorization=true**. Pre/post hashes of installed Root/User metadata and `/etc/resolv.conf`, IPv4 default route, active Tailscale, absent DIM State, pending Root/User slots and guard were **identical**; no privileged call, network/service/root/installed mutation or real DoH consent, and the disposable /tmp checkout was removed. Full-completion Commander operation `bff19b4a-2289-460c-8db2-9a1f04e98e8a` exit 0, stdout SHA256 `5d03f5094d07cefd228afb152524a2cbd5a2fcd432e323508fac0a576a89e406`, stderr SHA256 `d28dbc153e1eb4ffe5c4b35a3ef2ad996e03e99dc9b772db3d6d65b2311983c6` (original logs retained in MMZ Commander durable operation store). **Scope PASS:** rootless exact-version package and physical preflight; **still OPEN:** approved production Root watchdog, protected real upgrade, explicit encrypted-DNS consent, native GUI Start/Stop, physical HTTPS/Tailscale recovery, real reboot and owner acceptance. MMZ is reachable by connector again but privileged Polkit invocation remains unavailable through the existing allowlist. Preserve public v1.5.4 Stable and PR #60 Draft; do not promote to FINAL. This is an evidence-only Brain delta and changes no product bytes. **R569 Windows physical direct-HTTPS baseline (same date, read-only):** Both real Windows hosts `Emad-PC` and `SAEID` registered installed **v1.5.4**, with `DirectInternetMethodSvc` Running but `ctrld` Stopped and no `winws` or `ctrld` process observed; this is *not* proof of the DPI engine being ACTIVE. Independently, `curl.exe --noproxy '*'` with normal certificate validation returned **Google generate_204 HTTP 204 / exit 0** and **GitHub HTTP 200 / exit 0** on *each* host, while **YouTube generate_204 HTTP 000 / TLS exit 35** on *each* host. Source candidate 1.5.5-rc1 was NOT installed or activated, so this observed network failure cannot be attributed to the candidate; native DPI/DoH Start→HTTPS→Stop still OPEN. No process/service/network/settings mutation. Durable Emad Windows op `b47f75e0-36fb-4f9d-b95f-f98e49ebbb53` stdout SHA256 `9870d9f382676fda54575a9391d45403c872030027b5e20bef02271cba263649`; SAEID op `7606ee0b-9a46-48c8-842a-73dcb2e90af0` stdout SHA256 `8b268ed294818ebc4ba01701f180d0bb6326c583c2afb8c313885a6f4b068a5e`. The previous R569 evidence-only GitHub CI was SUCCESS (run 38865953754). **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. From 2382050f42c599fc9a22ea86e04b5e0b0cf2b68e Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 11:37:34 +0330 Subject: [PATCH 055/188] docs(brain): record R570 current-RC1 physical isolated GTK4 pass --- PROJECT_BRAIN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 7410dece..9f937973 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -12,6 +12,8 @@ Exact new source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B3455495 **R569 exact R568 RC1 physical rootless reproducibility/preflight — 2026-10-16 07:11:44–07:11:50 UTC:** On newly reconnected physical `mmz-LOQ-15IRX9`, a temporary non-root checkout of exact Draft PR #60 head `0bd88e96705909bd630279487b909037ef220ab7` rebuilt 27-file Linux v1.5.5-rc1 candidate ZIP **8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`** byte-identically to pinned R568. Original `tests/r519_versioned_package_contract.py` **15/15 PASS** and `tests/r552_native_upgrade_guard_contract.py` **18/18 PASS** on this physical host; extracted unmodified `system/upgrade_guard.py preflight --source` returned `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` with **installed Root v1.5.2 / User v1.5.4, guardArmed=false, needsRootAuthorization=true**. Pre/post hashes of installed Root/User metadata and `/etc/resolv.conf`, IPv4 default route, active Tailscale, absent DIM State, pending Root/User slots and guard were **identical**; no privileged call, network/service/root/installed mutation or real DoH consent, and the disposable /tmp checkout was removed. Full-completion Commander operation `bff19b4a-2289-460c-8db2-9a1f04e98e8a` exit 0, stdout SHA256 `5d03f5094d07cefd228afb152524a2cbd5a2fcd432e323508fac0a576a89e406`, stderr SHA256 `d28dbc153e1eb4ffe5c4b35a3ef2ad996e03e99dc9b772db3d6d65b2311983c6` (original logs retained in MMZ Commander durable operation store). **Scope PASS:** rootless exact-version package and physical preflight; **still OPEN:** approved production Root watchdog, protected real upgrade, explicit encrypted-DNS consent, native GUI Start/Stop, physical HTTPS/Tailscale recovery, real reboot and owner acceptance. MMZ is reachable by connector again but privileged Polkit invocation remains unavailable through the existing allowlist. Preserve public v1.5.4 Stable and PR #60 Draft; do not promote to FINAL. This is an evidence-only Brain delta and changes no product bytes. **R569 Windows physical direct-HTTPS baseline (same date, read-only):** Both real Windows hosts `Emad-PC` and `SAEID` registered installed **v1.5.4**, with `DirectInternetMethodSvc` Running but `ctrld` Stopped and no `winws` or `ctrld` process observed; this is *not* proof of the DPI engine being ACTIVE. Independently, `curl.exe --noproxy '*'` with normal certificate validation returned **Google generate_204 HTTP 204 / exit 0** and **GitHub HTTP 200 / exit 0** on *each* host, while **YouTube generate_204 HTTP 000 / TLS exit 35** on *each* host. Source candidate 1.5.5-rc1 was NOT installed or activated, so this observed network failure cannot be attributed to the candidate; native DPI/DoH Start→HTTPS→Stop still OPEN. No process/service/network/settings mutation. Durable Emad Windows op `b47f75e0-36fb-4f9d-b95f-f98e49ebbb53` stdout SHA256 `9870d9f382676fda54575a9391d45403c872030027b5e20bef02271cba263649`; SAEID op `7606ee0b-9a46-48c8-842a-73dcb2e90af0` stdout SHA256 `8b268ed294818ebc4ba01701f180d0bb6326c583c2afb8c313885a6f4b068a5e`. The previous R569 evidence-only GitHub CI was SUCCESS (run 38865953754). +**R570 physical GTK4 latest RC1 isolated behavioral regression — 2026-10-16 08:05–08:06 UTC:** On real `mmz-LOQ-15IRX9`, an exact temporary GitHub clone of PR #60 head `93cc060b5ee0b525b44efca06cda69a5ee0e9784` reproduced the **unchanged** 27-payload Linux 1.5.5-rc1 ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`. The current packaged GUI `app/direct_internet_method.py` SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8` ran as an actual GTK4/Broadway process under a freshly disposable HOME/private D-Bus, with the existing reviewed R542 15-case harness **source- and app-path-rebound to current exact SHA**: **15/15 PASS**, including synthetic DoH consent/revocation, ACTIVE selected DoH, route-drift STALE, conflict detection and no outgoing HTTPS on route drift. In parallel on the same exact current source, original AST GUI periodic-status `tests/r545_periodic_gui_status_contract.py` **6/6 PASS** and real-source route-drift `tests/r541_route_interface_drift_contract.py` **11/11 PASS**. Harness-only initial failure op `1af5c1fa-27b9-4c1a-9704-4165a5376c14` was traced to a mistyped historical source SHA in the temporary harness substitution (test aborted before invoking GUI); corrected SHA binding and rerun succeeded with operation `ce3db80a-ce93-42ba-ba35-7000288e3076`, exit 0, complete stdout SHA256 `ce622d4e4aaae3c12e4e9fd3ace33509e90040cec7ead1919377b8cfcbe23b6b`. Temporary Git clone/source/extracted package/receipt and BroadWay instance were cleaned; harness verified physical DNS SHA, default route, Tailscale, installed v1.5.4 User and actual preference hashes identical pre/post, and no real User consent. **PASS scoped to physical-host isolated current GUI; not a real installed RC1/Root watchdog/native Start/Stop/YouTube HTTPS acceptance.** Keep production installs v1.5.4 and PR Draft, native privilege boundary intact. + **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. **R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. From 930d82071a52c87c97f3e84ad6111e8842b67297 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 11:44:24 +0330 Subject: [PATCH 056/188] docs(brain): record R571 native Windows QA timeout and safe recovery --- PROJECT_BRAIN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 9f937973..1a38248c 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -14,6 +14,8 @@ Exact new source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B3455495 **R570 physical GTK4 latest RC1 isolated behavioral regression — 2026-10-16 08:05–08:06 UTC:** On real `mmz-LOQ-15IRX9`, an exact temporary GitHub clone of PR #60 head `93cc060b5ee0b525b44efca06cda69a5ee0e9784` reproduced the **unchanged** 27-payload Linux 1.5.5-rc1 ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`. The current packaged GUI `app/direct_internet_method.py` SHA256 `732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8` ran as an actual GTK4/Broadway process under a freshly disposable HOME/private D-Bus, with the existing reviewed R542 15-case harness **source- and app-path-rebound to current exact SHA**: **15/15 PASS**, including synthetic DoH consent/revocation, ACTIVE selected DoH, route-drift STALE, conflict detection and no outgoing HTTPS on route drift. In parallel on the same exact current source, original AST GUI periodic-status `tests/r545_periodic_gui_status_contract.py` **6/6 PASS** and real-source route-drift `tests/r541_route_interface_drift_contract.py` **11/11 PASS**. Harness-only initial failure op `1af5c1fa-27b9-4c1a-9704-4165a5376c14` was traced to a mistyped historical source SHA in the temporary harness substitution (test aborted before invoking GUI); corrected SHA binding and rerun succeeded with operation `ce3db80a-ce93-42ba-ba35-7000288e3076`, exit 0, complete stdout SHA256 `ce622d4e4aaae3c12e4e9fd3ace33509e90040cec7ead1919377b8cfcbe23b6b`. Temporary Git clone/source/extracted package/receipt and BroadWay instance were cleaned; harness verified physical DNS SHA, default route, Tailscale, installed v1.5.4 User and actual preference hashes identical pre/post, and no real User consent. **PASS scoped to physical-host isolated current GUI; not a real installed RC1/Root watchdog/native Start/Stop/YouTube HTTPS acceptance.** Keep production installs v1.5.4 and PR Draft, native privilege boundary intact. +**R571 Saeid Windows independent source QA transport timeout — 2026-10-16 08:08–08:10 UTC:** On real `SAEID` Windows with unchanged public v1.5.4 registration, `DirectInternetMethodSvc:Running` and `ctrld:Stopped`, isolated non-install operation `2a54e59c-090f-4ffa-b933-ca178271535c` attempted to fetch exact source head `2382050f42c599fc9a22ea86e04b5e0b0cf2b68e` solely into a new random user TEMP directory for three source checks (.NET 10, Windows contract, release metadata). It **TIMED_OUT after 120 seconds inside Git clone**, before head verification or ANY of the three tests. Do not mark these native checks PASS or infer a product regression; clone transport's precise cause is UNVERIFIED. The only initial stdout confirmed host/installed baseline, SHA256 `8e8297a1af95807819b23dcc25958a80581a113232868e05d2e99d81887f40aa`. The exact operation-owned TEMP directory was located by name/creation time; no git process remained, symlink/reparse checks passed and guarded cleanup succeeded. A subsequent readback confirmed v1.5.4, unchanged services, and absence of the scratch directory. No app, service, DNS, route or release mutation. Hosted exact R570 documentation-only [CI 38871628438](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38871628438) **SUCCESS**, including Windows-source, Linux-source, root transaction and v1.5.4 regression jobs; skipped unsigned Windows-release job is NOT a physical/installer PASS. Native SAEID source QA is **UNRUN/TRANSPORT_BLOCKED**, not an additional necessary release gate beyond already-passed hosted source checks. Public v1.5.4 and Draft PR #60 preserved. + **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. **R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. From 122f075ffa60be5de2c2cfadbeae050d8d6eb214 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:08:07 +0330 Subject: [PATCH 057/188] docs(brain): upsert R572-R576 verified R568 canary stage and physical browser baseline --- PROJECT_BRAIN.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 1a38248c..609fcbef 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,6 +2,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . +**Latest verified position — 2026-10-16 R576:** public **v1.5.4 Stable**, PR #60 **Draft**; latest privately staged Linux **v1.5.5-rc1** source from exact R568 27-file ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`, now installed-*independent* in MMZ user-only private QA path. R574 rootless package/repro/native preflight PASS; R570 physical-host isolated GTK4 PASS; R576 physical Chrome YouTube unusable/unverified with DIM OFF. Actual production independent Root watchdog **NOT ARMED**, Root1.5.2/User1.5.4 unchanged, encrypted DNS real user consent **NOT GRANTED**. **FINAL_UNPROVEN**, owner-approved protected native upgrade, real GUI Start/Stop and browser+HTTPS/Tailscale rollback still required. This summary supersedes older per-revision 'latest candidate' captions below for the current state; preserve their historical evidence scope. + **Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) **SUCCESS**, including native Windows private Stable→RC1→Stable preservation/rollback; Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and acceptance receipt SHA256 `6A59269F19270C69B634BC41CB9FFB912B75BC73240ABE9261E9F395EA144BA7`. The subsequent exact-head Brain-only [Push 38819709423](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819709423) and [PR 38819715922](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819715922) also **SUCCESS**, with no candidate product-code change. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. @@ -16,6 +18,12 @@ Exact new source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B3455495 **R571 Saeid Windows independent source QA transport timeout — 2026-10-16 08:08–08:10 UTC:** On real `SAEID` Windows with unchanged public v1.5.4 registration, `DirectInternetMethodSvc:Running` and `ctrld:Stopped`, isolated non-install operation `2a54e59c-090f-4ffa-b933-ca178271535c` attempted to fetch exact source head `2382050f42c599fc9a22ea86e04b5e0b0cf2b68e` solely into a new random user TEMP directory for three source checks (.NET 10, Windows contract, release metadata). It **TIMED_OUT after 120 seconds inside Git clone**, before head verification or ANY of the three tests. Do not mark these native checks PASS or infer a product regression; clone transport's precise cause is UNVERIFIED. The only initial stdout confirmed host/installed baseline, SHA256 `8e8297a1af95807819b23dcc25958a80581a113232868e05d2e99d81887f40aa`. The exact operation-owned TEMP directory was located by name/creation time; no git process remained, symlink/reparse checks passed and guarded cleanup succeeded. A subsequent readback confirmed v1.5.4, unchanged services, and absence of the scratch directory. No app, service, DNS, route or release mutation. Hosted exact R570 documentation-only [CI 38871628438](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38871628438) **SUCCESS**, including Windows-source, Linux-source, root transaction and v1.5.4 regression jobs; skipped unsigned Windows-release job is NOT a physical/installer PASS. Native SAEID source QA is **UNRUN/TRANSPORT_BLOCKED**, not an additional necessary release gate beyond already-passed hosted source checks. Public v1.5.4 and Draft PR #60 preserved. +**R572 Windows Git-transport isolation — 2026-10-16:** On SAEID Windows `git ls-remote --heads` quickly verified exact PR head `930d82071a52c87c97f3e84ad6111e8842b67297` (Commander op `40da3866-08e2-4c88-85a2-8826716abb8b`, SUCCESS). Changed to filtered sparse clone in *unique private user TEMP*; commit identity verified but sparse source materialization again timed out after 90 s **before** three native source contracts started (op `7b80c0d5-04da-476c-95f5-41c359c4462b`, stdout SHA256 `bac8077fbbfe5681c4d4c8be67aa65ac0561963e90a57d1485de052320d6ef6a`). Different read-only command audited ownership/time and absence of git processes/reparse nodes; only its exactly named TEMP clone was deleted and absence verified (op `5305a03c-89ce-4027-b724-3fac78da6ad8`, PASS). Installed v1.5.4 and services unchanged. **Native SAEID three-source QA UNRUN due materialization transport; not evidence of a product bug.** Existing hosted Windows-source and private Windows Stable→RC1→Stable gates remain their own scoped evidence. Do not retry large remote clone without a materially different delivery method. + +**R573 bounded clone failure → R574 verified alternative package — 2026-10-16:** An owner-local temporary checkout of the exact R568 private source on MMZ timed out under Git transport without producing or publishing a package (Commander op `364f738e-ccd2-4c99-95e7-ec407688f371`; temporary directory cleaned, installed host unchanged). A controlled alternative used the **existing SHA-pinned R562 ZIP** `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes) and **only two proven changed payloads**: `linux/system/upgrade_guard.py` SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C` and `linux/system/upgrade_system.sh` SHA256 `2584FDA48091458CC93CF89E74A2A03CE62108A2C61E93A58799F954E0030139`, fetched via GitHub connector at exact PR head and validated with the GitHub commit diff. A one-shot in-memory deterministic ZIP rebuild verified **all 27 payload hashes, 29 member names, no traversal/symlink and exact R568 8,370,720-byte SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`**, matching the independently hosted R568 build. Only its one extracted, non-installed candidate directory was published with owner-only parent (0700) at `/home/mmz/source/repos/DirectInternetMethod/R541_QA/CURRENT_PRIVATE_RC1/DirectInternetMethod_1.5.5-rc1_Linux_x86_64`. Exact `PACKAGE_MANIFEST.json` SHA256 `8BA59FFB12F1A1253B4F3A9DD83079CCCAB1418D9AB8B0C07A5C4258ABD1FE35`; all 27 file hashes independently rechecked *after* publication; original candidate guard `preflight --source` returned `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY`, Root=1.5.2, User=1.5.4, `guardArmed:false` and `needsRootAuthorization:true`. Full MMZ pre/post metadata/DNS/route/Tailscale identity PASS; transient source copies and QA script removed after verified publish, earlier historical ZIP untouched. Commander op `add3c321-96ad-4601-9153-3bc83908a777` exit0 stdout SHA256 `b0a60e76b3bf47031e37891cba946768b872030b17ba2af6770c0e8075bc66b5`; independent postwrite readback PASS. **Private package ready for a local, official administrator-approved canary; NOT installed, NO Polkit approval requested/received, NO real user DoH consent**. Do not invoke the privileged installer from Commander via a disallowed command/shell circumvention. + +**R575–R576 independent physical browser baseline (DIM OFF) — 2026-10-16:** On MMZ, disposable fresh user profiles with Chrome **155.0.8059.84** in no-proxy headless mode returned Google homepage HTML (255,028 and 256,921 bytes, visible `Google`), while `https://www.youtube.com/` produced **zero DOM bytes** despite Chrome exit code 0, so *YouTube is NOT accepted usable*. Independent ephemeral Chrome netlog diagnostics reported mixed background-request net errors including `-102` (refused), `-105` (name-resolution), `-109` (unreachable) and `-3` (aborted); aggregated counts are **not** per-host/request attributions and do not identify an ISP or product cause. Both runs preserved real Root/User metadata, DNS SHA, route and Tailscale; all profile/netlog outputs were temporary and removed. Op `00dc46d7-6ec9-4f23-aa0c-af88a80dff82` stdout SHA256 `2ddaf484497f1daffa98c89e1cb61084639e1d106e199800a27133c094e88c0a`; op `e6bde495-a6cf-466d-a391-cdcbbc95b706` stdout SHA256 `eea5c78369f3713b5daf72101ad1349f1cd347a2bff1c5403b8cc2821bade061`. Official Chromium codes: https://chromium.googlesource.com/chromium/chromium/+/refs/heads/main/net/base/net_error_list.h . Official zapret notes that browser TLS fingerprints differ from curl; therefore curl-35 alone is not browser acceptance: https://github.com/bol-van/zapret/blob/master/docs/quick_start.md . Independent R530 physical SNI-reset evidence remains distinct. Tailscale official Linux DNS documents favor preserving the existing systemd-resolved stub symlink (verified MMZ), not forcing adapter DNS changes: https://tailscale.com/docs/reference/linux-dns . Polkit's native authentication agent requires explicit administrator confirmation in the genuine active user session: https://polkit.pages.freedesktop.org/polkit/pkexec.1.html . **Remain OPEN:** actual guarded RC1 Start/Stop/DNS/HTTPS/YouTube + Tailscale lifecycle. Do not weaken TLS verification, alter network ownership or claim a universal DPI fix. + **R567 single denied admin prompt recovery (2026-10-16), HOSTED PASS / OWNER PROMPT NOT DELIVERED:** Test-only commit `8b930ef1366fc7146cea2059a62bd5554341d49d` runs the original production Linux `install.sh` `backend_matches_current`, privileged authorization branch, and `rollback_failed_install` in a native unprivileged process on an *empty disposable GitHub-hosted Ubuntu* seeded from official public Stable v1.5.4 User code and Root v1.5.2. A test-only Bash `pkexec` stub returns explicit denial `126` without elevating or opening an admin dialog; the real User transaction had already Stage'd and the original ERR/rollback trap must restore all User managed bytes/settings, leave the protected Root metadata/binaries and resolver/hosts intact, create no guard, and issue **only one** denied authorization attempt. Exact native job `PASS_HOSTED_SINGLE_POLKIT_DENIAL_USER_ROLLBACK` on both independent runners [Push 38824729897](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824729897) and [PR 38824737064](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38824737064); the PR Actions run was **SUCCESS**, while hosted Push Windows private lifecycle was still running at initial R567 recording. No production source, Linux private ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` or public Stable v1.5.4 changed. This is a controlled fake refusal, **not** evidence of a real MMZ Polkit prompt/consent; MMZ Tunnel Client is offline and direct `pkexec` is blocked by Commander policy. Physical Root/User protected RC1 install, DoH/HTTPS/Tailscale, actual reboot and user approval remain OPEN. Emad Linux has active DIM/Tailscale and cannot safely substitute for the MMZ offline canary; Emad Windows stable and Tailscale remain running. **R566 persistent missed-calendar native activation (2026-10-16), HOSTED PASS, NO PHYSICAL REBOOT:** Test-only commit `541af7c94a0d8fea687fc8372b492d1405808a64` adds an actual root-owned systemd timer/service in a unique temporary namespace on each *disposable* GitHub Ubuntu runner; it reuses the unchanged production R562 `OnActiveSec=480s` + fixed UTC `OnCalendar` + `Persistent=true` directives with only the test unit/worker identity replaced. The timer is explicitly stopped **before** calendar expiry, verified not to run while inactive, then restarted after the missed deadline; systemd runs the synthetic service promptly (0.159s and 0.157s observed, exact-run receipts). [PR CI 38823271295](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823271295) and [Push CI 38823266523](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38823266523) both **SUCCESS**, including separate native Root/User pre-acceptance and synthetic post-acceptance/fail-closed tests; hosted private Windows 1.5.4→RC1→1.5.4 rollback PASS on Push. Native Windows private Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`; unique hosted Windows acceptance receipt SHA256 `18346CBA60FEDDAA2A80D27CA444BFA8D95E432E86EAACE35788EC13D64E4D1E`. The official v1.5.4 Stable and *single private Linux 27-file ZIP* SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` remain unchanged. R566 proves catch-up after timer inactivity on running systemd, **not** an actual system reboot, physical MMZ bridge authorization, real DoH/HTTPS, user consent, installed RC1 or production Root rollback. The MMZ tunnel remained unavailable at this check; previous `pkexec` allowlist denial must not be bypassed. From d0ee4e5ed652462cf7ee32bdf67ffd57dab98df8 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:52:42 +0330 Subject: [PATCH 058/188] docs(brain): consolidate R577-R581 native browser transport and Windows install evidence --- PROJECT_BRAIN.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 609fcbef..1fc0db55 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,11 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R576:** public **v1.5.4 Stable**, PR #60 **Draft**; latest privately staged Linux **v1.5.5-rc1** source from exact R568 27-file ZIP SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`, now installed-*independent* in MMZ user-only private QA path. R574 rootless package/repro/native preflight PASS; R570 physical-host isolated GTK4 PASS; R576 physical Chrome YouTube unusable/unverified with DIM OFF. Actual production independent Root watchdog **NOT ARMED**, Root1.5.2/User1.5.4 unchanged, encrypted DNS real user consent **NOT GRANTED**. **FINAL_UNPROVEN**, owner-approved protected native upgrade, real GUI Start/Stop and browser+HTTPS/Tailscale rollback still required. This summary supersedes older per-revision 'latest candidate' captions below for the current state; preserve their historical evidence scope. +**Latest verified position — 2026-10-16 R581:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R574 package/recovery *preflight* PASS; R581 two-host Windows **one installed registration/uninstaller pair** read-only discovery PASS. Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. + +**R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. + +**R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. **Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) **SUCCESS**, including native Windows private Stable→RC1→Stable preservation/rollback; Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and acceptance receipt SHA256 `6A59269F19270C69B634BC41CB9FFB912B75BC73240ABE9261E9F395EA144BA7`. The subsequent exact-head Brain-only [Push 38819709423](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819709423) and [PR 38819715922](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819715922) also **SUCCESS**, with no candidate product-code change. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. From ddaf83821464ac3b761056eb79c6f9831bc879fc Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:58:18 +0330 Subject: [PATCH 059/188] docs(brain): close R583 exact 681-file physical Windows Stable baseline --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 1fc0db55..48d7ac3f 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,14 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R581:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R574 package/recovery *preflight* PASS; R581 two-host Windows **one installed registration/uninstaller pair** read-only discovery PASS. Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R583:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R582→R583 exact two-host Stable v1.5.4 installed-manifest acceptance — 2026-10-16:** Before attempting any unpublished Windows RC1 upgrade, original **v1.5.4** installed `manifest.json` (schema 2, 681 entries) was read directly from both live Windows machines. First read-only R582 script incorrectly assumed every `scope=user|privileged` manifest path was under the user installation root; this gave 7 matching user members and 674 apparent `MISSING` on **both** hosts, solely because those 674 files reside under a different legitimate path. This was **test harness failure, not a product defect**; no product repair was performed. Official immutable v1.5.4 Inno source `windows/installer/DirectInternetMethod.iss` confirmed `user` files live at `%LOCALAPPDATA%/Programs/DirectInternetMethod` while `privileged` files live at `%ProgramFiles%/DirectInternetMethod/Privileged`, with same Inno AppId and one active Uninstaller. Corrected R583 strict no-reparse/no-path-traversal, scope-mapped SHA256 and size audit then matched **681/681 entries, zero size/hash/missing failures, 481,191,083 verified bytes on each of Emad Windows and SAEID Windows**, no service or installed-file mutations. Emad native operation `9bcc822f-2be0-4a99-97d7-df252fa11482` **SUCCEEDED**, stdout SHA256 `fa2df0eaac29b3e578d4fc00d2dc57b615ec5893ece5a0d47e8b5dd162551571`; SAEID op `2769a706-7329-4520-a742-13bdd47580ab` **SUCCEEDED**, stdout SHA256 `175ae2903fb75e24453b623a5f51c4e7d68521813206fe5343876efeae437bb0`. **Gate CLOSED:** both native public Stable install files match exact installed manifest, with one uninstall registration/pair. **Not proven:** actual native RC1 upgrade preserving mutable user settings or Windows UAC/network lifecycle. Stable source reference `https://github.com/GOD13emad/DirectInternetMethod/blob/v1.5.4/windows/installer/DirectInternetMethod.iss`. + **Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) **SUCCESS**, including native Windows private Stable→RC1→Stable preservation/rollback; Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and acceptance receipt SHA256 `6A59269F19270C69B634BC41CB9FFB912B75BC73240ABE9261E9F395EA144BA7`. The subsequent exact-head Brain-only [Push 38819709423](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819709423) and [PR 38819715922](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819715922) also **SUCCESS**, with no candidate product-code change. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. **R563/R564 physical MMZ read-only scope:** The exact R562 ZIP was reconstructed in RAM from prior pinned R559, atomically staged privately at `R541_QA/R562_PRIVATE_1.5.5-rc1_Linux_x86_64.zip` (0600), with receipt `R541_QA/R563_PRIVATE_R562_READY.json` SHA256 `EDAF35749FE6812F872ADBD105607A7269BD3AF32D06227A7A295D3B88F4C856`. The actual R562 `upgrade_guard.py preflight` executed unprivileged on MMZ; six checks including mutated Root/User source rejection, wrong archive layout, nonroot arming refusal and physical pre/post data equality **PASS**. Unique `R541_QA/R564_NATIVE_GUARD_PREFLIGHT.json` SHA256 `EF4517B4DAD69475D274F5DD0525832EF260E84E34F8D0F4A64F002BB91DF2CA`. Installed User v1.5.4, Root v1.5.2, route/DNS/Tailscale, consent and owned network resources unchanged. **Neither Root permission nor actual guarded physical install was obtained.** The Remote Commander direct `pkexec` and `notify-send` programs were blocked by its allowlist (observed 2026-10-16); GUI takeover permission does not confer Root privilege, and the MMZ conversation is unbound. An attempted single text-only admin approval request write was **NOT_CONFIRMED** because the MMZ tunnel client ceased polling; the subsequent system-status probe also failed. No administrative prompt was displayed or approved through these tools. An authorized host policy change or native owner-approved privilege workflow is required. No bypass by another execution path. From a41759c0da9cd3ad5d9c47f6f08b518ec97364df Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:16:08 +0330 Subject: [PATCH 060/188] docs(brain): close R584-R586 exact RC1 DoH, native preflight, and public tag provenance --- PROJECT_BRAIN.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 48d7ac3f..7822b329 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,20 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R583:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R586:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R584 current-RC1 actual encrypted target-DNS transport — 2026-10-16:** On live `mmz-LOQ-15IRX9` running production Root v1.5.2/User v1.5.4 with DirectMethod OFF, an exact physical-host **user-local** execution loaded unchanged R568 1.5.5-rc1 package `CURRENT_PRIVATE_RC1`, verified **all 27 payload SHA-256 entries**, `runtime/ctrld v1.5.7` SHA256 `CA64579A2BC866CF72F3E46663E551162AFEFF0D448E899A01817A095E45B071` and policy source SHA256 `BCADC01C4A0DEDA0AB7C678155BE6F6960D3B68D9CFE1307DF85A94F18785663`. The exact bundled binary ran with the original policy's `build_secure_targeted_dns_config` over UDP **127.0.0.1:53587 only**, no live resolver or link changes. Actual `dig` A results: `www.youtube.com` **8/8 public global IP**, `redirector.googlevideo.com` **1/1 public global IP**, unrelated physical-router fallback `www.cloudflare.com` **2/2 public global IP**; all exit0 while isolated ctrld remained alive. Test-owned process terminated, port verified free, temporary HOME/config removed, live Root/User metadata, resolver SHA/symlink, physical route/DNS and Tailscale fingerprint **identical pre/post**. Commander durable operation `0451febf-d290-430a-b7a2-0c6f069d3fa1` **SUCCEEDED**, stdout SHA256 `7D86C74C7D703EB5C110139A007BE1C0395132D54FA19B0701B350EB97E1652F`. **Gate PASS restricted to actual unprivileged RC1 DoH transport**, not system-integrated per-link DNS, physical YouTube HTTPS, installation, or user consent. + +**R585 exact source real physical per-link controller preflight — 2026-10-16:** Ran *unmodified functions* `network_preflight("enp7s0")`, `runtime_tunnel_guard("enp7s0")`, `policy_bytes(router)` and `require_consent(uid,home)` from exact staged `system/encrypted_selective_dns.py` SHA256 `6DA9BAB94E1F4AF5C9FBE0742F96B3E2B5A0392294B27EDA3F4012095D9993DA`. Only the module's constant paths pointing to the **uninstalled** root-owned binary/policy/controller were rebound **in process memory** to the exact immutable *staged* files; all actual network and owner checks queried real MMZ host. Real network route `enp7s0`, single RFC1918 physical resolver, Tailscale split mode, no foreign tunnel/collision and dynamic selected policy validated. Owned private interface policy SHA256 `32E697BDE3254AA774F80A6730B9C674CE89D411938D2FF1B22516366C6E9B99`. The **real current user consent token was absent** and original consent guard returned `FAIL_CLOSED_EXPLICIT_ENCRYPTED_CONSENT_MISSING` as required. Native pre/post Root/User hash, resolver/link, route, physical and Tailscale DNS identical. Commander operation `e1563a56-3cf3-4b32-ae8f-5a38a582e3bc` **SUCCEEDED**, stdout SHA256 `4B52468882A5A71C026A2683A6A66C77EF39D387A61C912385ABEBEC48B953AD`. **PASS read-only native-state/consent-negative preflight only**; no Root authorisation, installed source, SetLinkDNSEx, systemd timer, GUI Start/Stop or actual physical HTTPS approval. + +**R586 two-host v1.5.4 installed manifest → public tag provenance — 2026-10-16:** Independent connected GitHub fetch of `windows/manifest.json` at exact **public v1.5.4 tag** was structurally compared in memory (all **681 scoped records**, including SHA/size) with two actual on-disk Windows installed manifests. **All 681 records and every top-level field match** across public tag, `Emad-PC` and `SAEID`; both installed files are byte-identical SHA256 `9BF9CBAC2588484DD237559CB0F1FA7D343BE52ABEFC07963BCB3654F51BCD6F`, 198,827 bytes. Public repository file uses LF line endings and installed copies use CRLF; normalized text/parsed JSON identity are exact, but **raw public tag blob is NOT byte-identical**. Coupled to R583's independent **681/681 actual installed file SHA matches on both machines**, this extends scoped authenticity to the public tagged source manifest, without claiming signed-publisher authenticity or an installed RC1. `RELEASE.json` inside both Windows Stable installs is mutually byte-identical but **differs in structure/content from public tagged source** (`CANDIDATE_V154_UNPUBLISHED` installed vs `CANDIDATE_V154_UNPUBLISHED_UNPROVEN` tag). Treat this as existing historical build/release metadata drift, **not an installed manifest or program checksum failure**; provenance tests use exact tagged manifest, not the differing release descriptive metadata. Both Windows installed apps and services unchanged, no settings or OS mutations. + +**Research/decision R586 — current upstream ctrld:** Upstream `Control-D-Inc/ctrld v1.5.8` dated **2026-10-07** includes DNS64/DNSSEC AD-bit behavior hardening and redaction of upstream keys from retained logs; prior physical R534 rootless loopback testing of its exact binary PASS applies only to that separate binary. Current R568 candidate intentionally pins **ctrld v1.5.7** and the current physical MMZ IPv4-only targeted fixed endpoint policy carries no operator-defined upstream key. Therefore no unverified source/binary replacement was performed: swapping v1.5.8 now changes package hash, dependencies and activation acceptance without directly solving the proven YouTube TLS reset. Retain the upstream security update as a version-scoped next candidate task if its affected features apply; current Release RC1 remains **unpublished**. Primary references: https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8 and https://pkg.go.dev/github.com/Control-D-Inc/ctrld@v1.5.8 ; external release synopsis https://newreleases.io/project/github/Control-D-Inc/ctrld/release/v1.5.8 . OS authorization remains native PolicyKit with owner-entered credentials; cannot be assumed from Commander full-filesystem access; official reference https://polkit.pages.freedesktop.org/polkit/pkexec.1.html . + **R582→R583 exact two-host Stable v1.5.4 installed-manifest acceptance — 2026-10-16:** Before attempting any unpublished Windows RC1 upgrade, original **v1.5.4** installed `manifest.json` (schema 2, 681 entries) was read directly from both live Windows machines. First read-only R582 script incorrectly assumed every `scope=user|privileged` manifest path was under the user installation root; this gave 7 matching user members and 674 apparent `MISSING` on **both** hosts, solely because those 674 files reside under a different legitimate path. This was **test harness failure, not a product defect**; no product repair was performed. Official immutable v1.5.4 Inno source `windows/installer/DirectInternetMethod.iss` confirmed `user` files live at `%LOCALAPPDATA%/Programs/DirectInternetMethod` while `privileged` files live at `%ProgramFiles%/DirectInternetMethod/Privileged`, with same Inno AppId and one active Uninstaller. Corrected R583 strict no-reparse/no-path-traversal, scope-mapped SHA256 and size audit then matched **681/681 entries, zero size/hash/missing failures, 481,191,083 verified bytes on each of Emad Windows and SAEID Windows**, no service or installed-file mutations. Emad native operation `9bcc822f-2be0-4a99-97d7-df252fa11482` **SUCCEEDED**, stdout SHA256 `fa2df0eaac29b3e578d4fc00d2dc57b615ec5893ece5a0d47e8b5dd162551571`; SAEID op `2769a706-7329-4520-a742-13bdd47580ab` **SUCCEEDED**, stdout SHA256 `175ae2903fb75e24453b623a5f51c4e7d68521813206fe5343876efeae437bb0`. **Gate CLOSED:** both native public Stable install files match exact installed manifest, with one uninstall registration/pair. **Not proven:** actual native RC1 upgrade preserving mutable user settings or Windows UAC/network lifecycle. Stable source reference `https://github.com/GOD13emad/DirectInternetMethod/blob/v1.5.4/windows/installer/DirectInternetMethod.iss`. **Current candidate R562 (2026-10-16) — Root/User accepted-commit crash recovery, hosted scope PASS, physical scope OPEN:** A formerly unresolved finalization window was closed in the *private* 1.5.5-rc1 source: after a privileged accepted Root transaction becomes durably `COMMITTED`, an interrupted pending User commit is resumed by the separate root-owned systemd worker **only after** immutable Root manifest, installed protected SHA/metadata, User version/metadata and owned backup-slot validation; an unknown actor, changed Root binary or mutable user-data mismatch fails closed and retains recovery evidence, without rolling back an accepted Root or altering networking. Existing pre-acceptance two-sided rollback remains distinct. New guard SHA256 `3B4900DB3251A81BF5776EE19D63A4CCDC032585DEFCC22DDFE06331B44BADD3`, wrapper SHA256 `28D421478D24E668A126B4A26BE5C49ACDE958C4DC3BE91E2901E0D50BF760A6`, exact Linux ZIP SHA256 `B2960E957D2314F43DA719AAFE097E48FDD3CAA647A02844333818DC50A37471` (8,363,886 bytes, 27 payload/29 ZIP members). The extracted method tests **18/18 PASS**. Exact code/QA head `07ca88f04fd572480be8cbce246f011b0fc1c68d`: [PR CI 38819270971](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819270971) **SUCCESS**; [Push CI 38819266213](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819266213) **SUCCESS**, including native Windows private Stable→RC1→Stable preservation/rollback; Setup SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and acceptance receipt SHA256 `6A59269F19270C69B634BC41CB9FFB912B75BC73240ABE9261E9F395EA144BA7`. The subsequent exact-head Brain-only [Push 38819709423](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819709423) and [PR 38819715922](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38819715922) also **SUCCESS**, with no candidate product-code change. Both separate GitHub-hosted systemd workers produced `PASS_EPHEMERAL_SYNTHETIC_ROOT_ACCEPTANCE_USER_FINALIZATION` with `syntheticRootAcceptanceOnly=true`. The acceptance fixture is *fabricated solely for the disposable transaction interruption test*, **not** real Start/Stop/Tailscale/DoH or production release approval. Public Stable v1.5.4 and installed hosts untouched. From 291d0f6bfd091d10ee1a7c70ad4bdb644e6ded65 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:20:49 +0330 Subject: [PATCH 061/188] docs(brain): record R587 exact-current native User Stage Apply Rollback --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 7822b329..478af1b3 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,14 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R586:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R587:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS, including byte-exact rollback and private-data preservation**; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R587 exact pinned 1.5.5-rc1 real physical-host User transaction, disposable HOME — 2026-10-16:** On MMZ User v1.5.4 / Root v1.5.2 with DIM OFF, no guard/consent and active Tailscale, the **R568 29-member ZIP** was deterministically rebuilt solely in private temporary memory/file storage from independently SHA-pinned R562 metadata and 29 current staged package files; exact 8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`. The previously reviewed original `R557_USER_SHADOW_TRANSACTION.py` (SHA256 `694FCD6CD966A9620E5101B178CFB7D37BAC73B81991B0AB22241CE4712435CF`) ran on the same *real MMZ host*, with **only test source ZIP/expected digest and disposable QA/receipt paths** changed for R568. Actual unmodified `app/user_update_transaction.py` exercised a synthetic private HOME seeded from exact published Stable v1.5.4 ZIP SHA256 `4784FCD44ED3D1B40103BCA140B2C0066784F794D07C3AE1F525334766A553C7`. **Eight of eight native user-code scenarios PASS**: Stage, no early code/config overwrite, Apply exact source hashes, metadata attestation, byte-identical Rollback, fail-closed on concurrent mutable settings drift, preserve custom provider/private settings/custom hosts, no accumulated backup slots after a second cycle. Synthetic receipt SHA256 `5672EF4A16067113835102359F77FDDACF372D5B06AB376F45383E9812F8A4D4`, Commander operation `f5bf8bd9-d520-4aaf-84e9-19574d684852` **SUCCEEDED exit0** and stdout SHA256 `FEBDA1196FFF4FB7F343A1BE98D4341F233D30FCFC13456FB12C7295CEF91038`. Temporary source ZIP and synthetic HOME/receipt were deleted; actual user/Root metadata, resolver, routes and Tailscale remained unchanged. **PASS scoped only to exact-current-R568 user transaction in synthetic HOME on physical host**, NOT production User or Root installation, independent native rollback, GUI/HTTPS/DoH consent or owner approval. + **R584 current-RC1 actual encrypted target-DNS transport — 2026-10-16:** On live `mmz-LOQ-15IRX9` running production Root v1.5.2/User v1.5.4 with DirectMethod OFF, an exact physical-host **user-local** execution loaded unchanged R568 1.5.5-rc1 package `CURRENT_PRIVATE_RC1`, verified **all 27 payload SHA-256 entries**, `runtime/ctrld v1.5.7` SHA256 `CA64579A2BC866CF72F3E46663E551162AFEFF0D448E899A01817A095E45B071` and policy source SHA256 `BCADC01C4A0DEDA0AB7C678155BE6F6960D3B68D9CFE1307DF85A94F18785663`. The exact bundled binary ran with the original policy's `build_secure_targeted_dns_config` over UDP **127.0.0.1:53587 only**, no live resolver or link changes. Actual `dig` A results: `www.youtube.com` **8/8 public global IP**, `redirector.googlevideo.com` **1/1 public global IP**, unrelated physical-router fallback `www.cloudflare.com` **2/2 public global IP**; all exit0 while isolated ctrld remained alive. Test-owned process terminated, port verified free, temporary HOME/config removed, live Root/User metadata, resolver SHA/symlink, physical route/DNS and Tailscale fingerprint **identical pre/post**. Commander durable operation `0451febf-d290-430a-b7a2-0c6f069d3fa1` **SUCCEEDED**, stdout SHA256 `7D86C74C7D703EB5C110139A007BE1C0395132D54FA19B0701B350EB97E1652F`. **Gate PASS restricted to actual unprivileged RC1 DoH transport**, not system-integrated per-link DNS, physical YouTube HTTPS, installation, or user consent. **R585 exact source real physical per-link controller preflight — 2026-10-16:** Ran *unmodified functions* `network_preflight("enp7s0")`, `runtime_tunnel_guard("enp7s0")`, `policy_bytes(router)` and `require_consent(uid,home)` from exact staged `system/encrypted_selective_dns.py` SHA256 `6DA9BAB94E1F4AF5C9FBE0742F96B3E2B5A0392294B27EDA3F4012095D9993DA`. Only the module's constant paths pointing to the **uninstalled** root-owned binary/policy/controller were rebound **in process memory** to the exact immutable *staged* files; all actual network and owner checks queried real MMZ host. Real network route `enp7s0`, single RFC1918 physical resolver, Tailscale split mode, no foreign tunnel/collision and dynamic selected policy validated. Owned private interface policy SHA256 `32E697BDE3254AA774F80A6730B9C674CE89D411938D2FF1B22516366C6E9B99`. The **real current user consent token was absent** and original consent guard returned `FAIL_CLOSED_EXPLICIT_ENCRYPTED_CONSENT_MISSING` as required. Native pre/post Root/User hash, resolver/link, route, physical and Tailscale DNS identical. Commander operation `e1563a56-3cf3-4b32-ae8f-5a38a582e3bc` **SUCCEEDED**, stdout SHA256 `4B52468882A5A71C026A2683A6A66C77EF39D387A61C912385ABEBEC48B953AD`. **PASS read-only native-state/consent-negative preflight only**; no Root authorisation, installed source, SetLinkDNSEx, systemd timer, GUI Start/Stop or actual physical HTTPS approval. From 8b60abedaeefc86b6ee924b33bd036efa94861af Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:24:20 +0330 Subject: [PATCH 062/188] docs(brain): close R588 exact RC1 user Uninstaller ownership and preservation --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 478af1b3..b0f06839 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,14 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R587:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS, including byte-exact rollback and private-data preservation**; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R588:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R588 native exact RC1 user Uninstaller synthetic HOME preservation — 2026-10-16:** On physical MMZ with installed User v1.5.4/Root v1.5.2 and Tailscale preserved, the exact staged **R568** `app/user_uninstall_preserve.py` SHA256 `6691AA7262307F41DDAD1B5EB409E1C7AAC3B2171B5285156045849D969F6B8A` was imported into a one-time **disposable, owner-only synthetic HOME**. Its unchanged public `verify`, `load_owned`, `remove_after_backend` and `remove_known_files` executed against synthetic version 1.5.5-rc1 `INSTALL.json`, real current code payload bytes, four synthetic launcher files, synthetic private settings/custom hosts/Router Gateway provider data. **6/6 PASS:** exactly **11** owned code/launcher files recognized without mutation; `--remove-after-backend` rejected while real protected backend still exists, with zero files changed; deliberately modified owned program source was rejected without deletion; after restoration, original bounded `remove_known_files` deleted only the 11 known synthetic code/launchers + synthetic INSTALL metadata; four classes of synthetic private data and its application directory remained byte-identical, and temp HOME was removed. First harness invocation `a0fd5f29-c092-4170-a2b6-4805027c04db` failed because harness-generated expected source SHA was **uppercase** whereas the original source contract uses lowercase, a synthetic-fixture mismatch and NOT a product regression. The corrected run `3d4ca48f-21b5-4904-96e1-d7fae28609ae` **SUCCEEDED exit0**, stdout SHA256 `80BCC06BF633B5ADB8E8384814EDFFD03867B0FEC8FAF0817139A6A50C707303`; real Root/User metadata, default route/resolver/Tailscale before/after unchanged. **PASS scoped to original current-RC1 uninstaller user-file ownership and synthetic preservation logic only**; installed Root uninstall, GUI, UAC/PolicyKit and user acceptance remain OPEN. Previous exact R587 documentation-only CI [38883252033](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38883252033) **SUCCESS**, skipped native Windows Release job not a new install PASS. + **R587 exact pinned 1.5.5-rc1 real physical-host User transaction, disposable HOME — 2026-10-16:** On MMZ User v1.5.4 / Root v1.5.2 with DIM OFF, no guard/consent and active Tailscale, the **R568 29-member ZIP** was deterministically rebuilt solely in private temporary memory/file storage from independently SHA-pinned R562 metadata and 29 current staged package files; exact 8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`. The previously reviewed original `R557_USER_SHADOW_TRANSACTION.py` (SHA256 `694FCD6CD966A9620E5101B178CFB7D37BAC73B81991B0AB22241CE4712435CF`) ran on the same *real MMZ host*, with **only test source ZIP/expected digest and disposable QA/receipt paths** changed for R568. Actual unmodified `app/user_update_transaction.py` exercised a synthetic private HOME seeded from exact published Stable v1.5.4 ZIP SHA256 `4784FCD44ED3D1B40103BCA140B2C0066784F794D07C3AE1F525334766A553C7`. **Eight of eight native user-code scenarios PASS**: Stage, no early code/config overwrite, Apply exact source hashes, metadata attestation, byte-identical Rollback, fail-closed on concurrent mutable settings drift, preserve custom provider/private settings/custom hosts, no accumulated backup slots after a second cycle. Synthetic receipt SHA256 `5672EF4A16067113835102359F77FDDACF372D5B06AB376F45383E9812F8A4D4`, Commander operation `f5bf8bd9-d520-4aaf-84e9-19574d684852` **SUCCEEDED exit0** and stdout SHA256 `FEBDA1196FFF4FB7F343A1BE98D4341F233D30FCFC13456FB12C7295CEF91038`. Temporary source ZIP and synthetic HOME/receipt were deleted; actual user/Root metadata, resolver, routes and Tailscale remained unchanged. **PASS scoped only to exact-current-R568 user transaction in synthetic HOME on physical host**, NOT production User or Root installation, independent native rollback, GUI/HTTPS/DoH consent or owner approval. **R584 current-RC1 actual encrypted target-DNS transport — 2026-10-16:** On live `mmz-LOQ-15IRX9` running production Root v1.5.2/User v1.5.4 with DirectMethod OFF, an exact physical-host **user-local** execution loaded unchanged R568 1.5.5-rc1 package `CURRENT_PRIVATE_RC1`, verified **all 27 payload SHA-256 entries**, `runtime/ctrld v1.5.7` SHA256 `CA64579A2BC866CF72F3E46663E551162AFEFF0D448E899A01817A095E45B071` and policy source SHA256 `BCADC01C4A0DEDA0AB7C678155BE6F6960D3B68D9CFE1307DF85A94F18785663`. The exact bundled binary ran with the original policy's `build_secure_targeted_dns_config` over UDP **127.0.0.1:53587 only**, no live resolver or link changes. Actual `dig` A results: `www.youtube.com` **8/8 public global IP**, `redirector.googlevideo.com` **1/1 public global IP**, unrelated physical-router fallback `www.cloudflare.com` **2/2 public global IP**; all exit0 while isolated ctrld remained alive. Test-owned process terminated, port verified free, temporary HOME/config removed, live Root/User metadata, resolver SHA/symlink, physical route/DNS and Tailscale fingerprint **identical pre/post**. Commander durable operation `0451febf-d290-430a-b7a2-0c6f069d3fa1` **SUCCEEDED**, stdout SHA256 `7D86C74C7D703EB5C110139A007BE1C0395132D54FA19B0701B350EB97E1652F`. **Gate PASS restricted to actual unprivileged RC1 DoH transport**, not system-integrated per-link DNS, physical YouTube HTTPS, installation, or user consent. From 8e22402f31cd135ae369ea467539d52a47d6ac50 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:33:47 +0330 Subject: [PATCH 063/188] docs(brain): verify R589 physical two-host stable service and updater metadata --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index b0f06839..f20b427f 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,14 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R588:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R589:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R589 two physical Windows Stable service self-test and public release discovery provenance — 2026-10-16:** On actual `EMAD-PC-ULTIMAT` and `SAEID`, direct on-disk `app/DirectInternetMethod.Service.exe` (under the protected Program Files install root) matched each installed manifest's **public v1.5.4 SHA256 `2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41`**. The unmodified real service binary's verified read-only `--console-selftest` completed **exit 0** on BOTH hosts, with separate existing `DirectInternetMethodSvc` left untouched. Independently, standard Windows `curl.exe` on BOTH computers successfully fetched the official `GET https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest` (no auth, normal TLS verification and short bounds): `tag_name=v1.5.4`, `draft=false`, `prerelease=false`, exactly **one Windows Installer asset** and **one SHA256SUMS.txt asset**, GitHub API Windows asset `digest=sha256:ffb9a2ae19c2e89338ee12ee8da133f5a16dbf4dcf961678c33d77d0c3843133`. Both independently fetched ONLY the small official `v1.5.4/SHA256SUMS.txt`, SHA256 `68E70329E8B9DAC6B738CE9B90D99A0010030980CD9C17CDE45A5199A60692D8`, and each found exactly **one** Windows entry SHA256 `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` matching the asset digest (two total checksum lines). Native direct-host self-test/API operator runs Emad `10fc052e-2993-4200-8301-005eb25ea8d9` stdout SHA256 `4D96497FB13B8A26AA67209DF82A5121D287AC0B74378F352BB7E48EC01E8913`; SAEID `822e19b5-fbe8-47fc-8f31-8fc3f383c808` stdout SHA256 `F87C473761770D912818B51296A14355B07BBCA9F25E3A5B1F64BB2E39A67C2C`. Corrected official-checksums native ops Emad `f9106279-3224-4f36-8f58-88c829dbfb46` stdout SHA256 `9C7D6F25A0D094C8C64CEB0DDDE12CB7E598F3518D78213153ED381BA7C52F52`; SAEID `91b70689-faf3-44dd-9b5b-7203cd557a22` stdout SHA256 `672B5672F17D590C47250F0F4989B183AC4C89F9B2B5630A677CE0AD32A09D2A`. Earlier checksum attempt failed solely because a PowerShell report literal `publishedStable=v1.5.4` was unquoted; **not a product or transport failure**; clean corrected operations passed and owned temp JSON/text files removed. **Gate PASS only for two actual native installed Stable services' local self-test, latest public release API discoverability and official SHA256SUMS/API digest consistency**; no installer binary was downloaded, no GUI update button, production service `CTRL_UPDATE`, protected update handoff or native RC1 UAC install was executed. Do not infer online-update completion from a read-only request. Public Stable remains v1.5.4 and Draft PR #60 unopened for promotion. Official release checksum URL: https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/SHA256SUMS.txt . + **R588 native exact RC1 user Uninstaller synthetic HOME preservation — 2026-10-16:** On physical MMZ with installed User v1.5.4/Root v1.5.2 and Tailscale preserved, the exact staged **R568** `app/user_uninstall_preserve.py` SHA256 `6691AA7262307F41DDAD1B5EB409E1C7AAC3B2171B5285156045849D969F6B8A` was imported into a one-time **disposable, owner-only synthetic HOME**. Its unchanged public `verify`, `load_owned`, `remove_after_backend` and `remove_known_files` executed against synthetic version 1.5.5-rc1 `INSTALL.json`, real current code payload bytes, four synthetic launcher files, synthetic private settings/custom hosts/Router Gateway provider data. **6/6 PASS:** exactly **11** owned code/launcher files recognized without mutation; `--remove-after-backend` rejected while real protected backend still exists, with zero files changed; deliberately modified owned program source was rejected without deletion; after restoration, original bounded `remove_known_files` deleted only the 11 known synthetic code/launchers + synthetic INSTALL metadata; four classes of synthetic private data and its application directory remained byte-identical, and temp HOME was removed. First harness invocation `a0fd5f29-c092-4170-a2b6-4805027c04db` failed because harness-generated expected source SHA was **uppercase** whereas the original source contract uses lowercase, a synthetic-fixture mismatch and NOT a product regression. The corrected run `3d4ca48f-21b5-4904-96e1-d7fae28609ae` **SUCCEEDED exit0**, stdout SHA256 `80BCC06BF633B5ADB8E8384814EDFFD03867B0FEC8FAF0817139A6A50C707303`; real Root/User metadata, default route/resolver/Tailscale before/after unchanged. **PASS scoped to original current-RC1 uninstaller user-file ownership and synthetic preservation logic only**; installed Root uninstall, GUI, UAC/PolicyKit and user acceptance remain OPEN. Previous exact R587 documentation-only CI [38883252033](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38883252033) **SUCCESS**, skipped native Windows Release job not a new install PASS. **R587 exact pinned 1.5.5-rc1 real physical-host User transaction, disposable HOME — 2026-10-16:** On MMZ User v1.5.4 / Root v1.5.2 with DIM OFF, no guard/consent and active Tailscale, the **R568 29-member ZIP** was deterministically rebuilt solely in private temporary memory/file storage from independently SHA-pinned R562 metadata and 29 current staged package files; exact 8,370,720 bytes / SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC`. The previously reviewed original `R557_USER_SHADOW_TRANSACTION.py` (SHA256 `694FCD6CD966A9620E5101B178CFB7D37BAC73B81991B0AB22241CE4712435CF`) ran on the same *real MMZ host*, with **only test source ZIP/expected digest and disposable QA/receipt paths** changed for R568. Actual unmodified `app/user_update_transaction.py` exercised a synthetic private HOME seeded from exact published Stable v1.5.4 ZIP SHA256 `4784FCD44ED3D1B40103BCA140B2C0066784F794D07C3AE1F525334766A553C7`. **Eight of eight native user-code scenarios PASS**: Stage, no early code/config overwrite, Apply exact source hashes, metadata attestation, byte-identical Rollback, fail-closed on concurrent mutable settings drift, preserve custom provider/private settings/custom hosts, no accumulated backup slots after a second cycle. Synthetic receipt SHA256 `5672EF4A16067113835102359F77FDDACF372D5B06AB376F45383E9812F8A4D4`, Commander operation `f5bf8bd9-d520-4aaf-84e9-19574d684852` **SUCCEEDED exit0** and stdout SHA256 `FEBDA1196FFF4FB7F343A1BE98D4341F233D30FCFC13456FB12C7295CEF91038`. Temporary source ZIP and synthetic HOME/receipt were deleted; actual user/Root metadata, resolver, routes and Tailscale remained unchanged. **PASS scoped only to exact-current-R568 user transaction in synthetic HOME on physical host**, NOT production User or Root installation, independent native rollback, GUI/HTTPS/DoH consent or owner approval. From bfbbe910dcf344205d022438f02b6c2942cb38a2 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:00:07 +0330 Subject: [PATCH 064/188] docs(brain): R590 physical protected baseline, R591 systemd and Emad conflict evidence --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index f20b427f..59e06946 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,18 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R589:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; R581 two-host Windows single-install registration PASS; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R591:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; **R590 original current-RC1 RootTransaction native installed baseline PASS** (nine static, two old managed, two absent new); **R591 exact guard systemd 259 Timer/Service syntax PASS** without activation; R581 two-host Windows single-install registration PASS; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Emad Linux real state remains stored ACTIVE, actual GUI CONFLICT, default interface switched from wlp2s0 to enp1s0 and a NetworkManager VPN profile is active; do not modify user network remotely without independent recovery. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R590 exact physical protected Root source baseline and administrative boundary — 2026-10-16:** With exact unchanged private 1.5.5-rc1 Root source SHA256 `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` and source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, executed the **original `RootTransaction()._baseline()` read-only method against actual MMZ `root=/`** as the nonprivileged owner, after exact host and source SHA preconditions. `_baseline()` verified installed privileged metadata version **v1.5.2** and actual owner, **9/9 pinned static protected members**, **2/2 old versioned files matching pinned SHA and POSIX mode**, **2/2 reserved new target files absent**; Root/User metadata sha, `/etc/resolv.conf` bytes+symlink, IPv4 route, physical DNS and Tailscale DNS/service were byte-identical before and after. **PASS_ROOT_BASELINE_READONLY; no `stage`, `apply`, Root action lock, `pkexec`, network or installed mutation.** Commander operation `64fd886b-1d88-43db-9d4a-d8389d1f57b9` **SUCCEEDED** stdout SHA256 `1EC4FB2B11C630AB95C6785CA0AF787CAA3603300C7D4AFEC5E43801B745E86C`. A separate native direct Commander `pkexec --version` capability probe (nonmutating) was still rejected by the server's own execution policy with **`program not allowed: pkexec`**; this is not an OS prompt, credential request, or authorization. Official Polkit `pkexec` requires a registered authentication agent in the caller's session and administrator approval; its error code 126 denotes dismissal, 127 inability to authorize. Reference: https://polkit.pages.freedesktop.org/polkit/pkexec.1.html . **Real guarded install remains blocked pending authorized local owner action.** + +**R591 actual MMZ systemd 259 compatibility of exact R568 independent Root/User watchdog — 2026-10-16:** Loaded unchanged `system/upgrade_guard.py` under MMZ user scope and invoked its original `unit_templates(deadlineUnix)` with a future UTC +480s deadline, yielding **289-byte Service** and **241-byte Timer** in one disposable /tmp directory. Verified `OnActiveSec=480s`, frozen `OnCalendar`, `Persistent=true`, `AccuracySec=1s`; the **actual MMZ systemd 259 (259.5-0ubuntu3.4)** `systemd-analyze calendar` accepted the UTC deadline and `systemd-analyze verify ` **exit 0 with empty stderr**, without installing or starting either unit. Source artifact, installed Root/User baseline, guard and network untouched; temporary files removed. Commander operation `037044ad-92df-40db-a4a8-c0da7614f565` **SUCCEEDED**, stdout SHA256 `CCF3689A86872B9712DA0E7F2EA9EA3252DB9DF1DA81713F703191E4B111ED6C`. **PASS for exact physical-host systemd unit syntax and UTC parsing; not physical timer firing, reboot or successful Root rollback.** systemd primary documentation confirms `Persistent=true` catches missed `OnCalendar` after timer inactivity and `systemctl clean --what=state` removes its stored last-trigger stamp: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . + +**R591 Emad Linux live state divergence / independent safety blocker — 2026-10-16:** Read-only imported **currently installed v1.5.4** source `~/.local/share/DirectInternetMethod/direct_internet_method.py` (no subprocess/GUI mutation) and called original `load_state()` on Emad Linux. Source-facing native state.json still says `status=ACTIVE` and physical interface **`wlp2s0`**, but live route uses **`enp1s0`**. Original UI reports **`mode=CONFLICT`** (not healthy ACTIVE); `_external_tunnel_active()=true`, `_tailscale_split_safe()=false`, `_coexistence_backend_ready()=true` on a host with active system `directinternetmethod-nfqws-1000.service` and Tailscale service. `nmcli -t -f TYPE,DEVICE connection show --active` reports a separate **active `vpn:enp1s0` profile** along with ethernet/wifi and Tailscale `tun`; this connection type is a genuine VPN-class NetworkManager active profile per official docs https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/nm-settings-nmcli.html . This presence explains the original conservative tunnel conflict **without** proving which VPN provider or whether the physical interface drift alone caused it. Real user code/state/resolver SHA before-after unchanged. Do not silently stop VPN/Tailscale/NFQWS, rewrite state.json, or trigger unattended recovery because the independent connectivity/recovery path is not attested. **Emad Linux live healthy connectivity remains OPEN** and needs an owner-attended, confirmed independent channel before native Recovery and re-Start. + **R589 two physical Windows Stable service self-test and public release discovery provenance — 2026-10-16:** On actual `EMAD-PC-ULTIMAT` and `SAEID`, direct on-disk `app/DirectInternetMethod.Service.exe` (under the protected Program Files install root) matched each installed manifest's **public v1.5.4 SHA256 `2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41`**. The unmodified real service binary's verified read-only `--console-selftest` completed **exit 0** on BOTH hosts, with separate existing `DirectInternetMethodSvc` left untouched. Independently, standard Windows `curl.exe` on BOTH computers successfully fetched the official `GET https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest` (no auth, normal TLS verification and short bounds): `tag_name=v1.5.4`, `draft=false`, `prerelease=false`, exactly **one Windows Installer asset** and **one SHA256SUMS.txt asset**, GitHub API Windows asset `digest=sha256:ffb9a2ae19c2e89338ee12ee8da133f5a16dbf4dcf961678c33d77d0c3843133`. Both independently fetched ONLY the small official `v1.5.4/SHA256SUMS.txt`, SHA256 `68E70329E8B9DAC6B738CE9B90D99A0010030980CD9C17CDE45A5199A60692D8`, and each found exactly **one** Windows entry SHA256 `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` matching the asset digest (two total checksum lines). Native direct-host self-test/API operator runs Emad `10fc052e-2993-4200-8301-005eb25ea8d9` stdout SHA256 `4D96497FB13B8A26AA67209DF82A5121D287AC0B74378F352BB7E48EC01E8913`; SAEID `822e19b5-fbe8-47fc-8f31-8fc3f383c808` stdout SHA256 `F87C473761770D912818B51296A14355B07BBCA9F25E3A5B1F64BB2E39A67C2C`. Corrected official-checksums native ops Emad `f9106279-3224-4f36-8f58-88c829dbfb46` stdout SHA256 `9C7D6F25A0D094C8C64CEB0DDDE12CB7E598F3518D78213153ED381BA7C52F52`; SAEID `91b70689-faf3-44dd-9b5b-7203cd557a22` stdout SHA256 `672B5672F17D590C47250F0F4989B183AC4C89F9B2B5630A677CE0AD32A09D2A`. Earlier checksum attempt failed solely because a PowerShell report literal `publishedStable=v1.5.4` was unquoted; **not a product or transport failure**; clean corrected operations passed and owned temp JSON/text files removed. **Gate PASS only for two actual native installed Stable services' local self-test, latest public release API discoverability and official SHA256SUMS/API digest consistency**; no installer binary was downloaded, no GUI update button, production service `CTRL_UPDATE`, protected update handoff or native RC1 UAC install was executed. Do not infer online-update completion from a read-only request. Public Stable remains v1.5.4 and Draft PR #60 unopened for promotion. Official release checksum URL: https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/SHA256SUMS.txt . **R588 native exact RC1 user Uninstaller synthetic HOME preservation — 2026-10-16:** On physical MMZ with installed User v1.5.4/Root v1.5.2 and Tailscale preserved, the exact staged **R568** `app/user_uninstall_preserve.py` SHA256 `6691AA7262307F41DDAD1B5EB409E1C7AAC3B2171B5285156045849D969F6B8A` was imported into a one-time **disposable, owner-only synthetic HOME**. Its unchanged public `verify`, `load_owned`, `remove_after_backend` and `remove_known_files` executed against synthetic version 1.5.5-rc1 `INSTALL.json`, real current code payload bytes, four synthetic launcher files, synthetic private settings/custom hosts/Router Gateway provider data. **6/6 PASS:** exactly **11** owned code/launcher files recognized without mutation; `--remove-after-backend` rejected while real protected backend still exists, with zero files changed; deliberately modified owned program source was rejected without deletion; after restoration, original bounded `remove_known_files` deleted only the 11 known synthetic code/launchers + synthetic INSTALL metadata; four classes of synthetic private data and its application directory remained byte-identical, and temp HOME was removed. First harness invocation `a0fd5f29-c092-4170-a2b6-4805027c04db` failed because harness-generated expected source SHA was **uppercase** whereas the original source contract uses lowercase, a synthetic-fixture mismatch and NOT a product regression. The corrected run `3d4ca48f-21b5-4904-96e1-d7fae28609ae` **SUCCEEDED exit0**, stdout SHA256 `80BCC06BF633B5ADB8E8384814EDFFD03867B0FEC8FAF0817139A6A50C707303`; real Root/User metadata, default route/resolver/Tailscale before/after unchanged. **PASS scoped to original current-RC1 uninstaller user-file ownership and synthetic preservation logic only**; installed Root uninstall, GUI, UAC/PolicyKit and user acceptance remain OPEN. Previous exact R587 documentation-only CI [38883252033](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38883252033) **SUCCESS**, skipped native Windows Release job not a new install PASS. From 84ba9daf7766f7565532d580f4604d6345f10f76 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:22:50 +0330 Subject: [PATCH 065/188] docs(brain): verify R592-R593 native Win10 RC1 build and service failclosed selftest --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 59e06946..83ed78d4 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,14 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R591:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; **R590 original current-RC1 RootTransaction native installed baseline PASS** (nine static, two old managed, two absent new); **R591 exact guard systemd 259 Timer/Service syntax PASS** without activation; R581 two-host Windows single-install registration PASS; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Emad Linux real state remains stored ACTIVE, actual GUI CONFLICT, default interface switched from wlp2s0 to enp1s0 and a NetworkManager VPN profile is active; do not modify user network remotely without independent recovery. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R593:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; **R592 exact PR-head Windows WPF GUI and Service local .NET 10 build PASS and R593 real native candidate Service fail-closed self-test PASS (disposable source);** **R590 original current-RC1 RootTransaction native installed baseline PASS** (nine static, two old managed, two absent new); **R591 exact guard systemd 259 Timer/Service syntax PASS** without activation; R581 two-host Windows single-install registration PASS; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Emad Linux real state remains stored ACTIVE, actual GUI CONFLICT, default interface switched from wlp2s0 to enp1s0 and a NetworkManager VPN profile is active; do not modify user network remotely without independent recovery. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R592–R593 exact candidate native Windows build and self-test — 2026-10-16:** On active physical `EMAD-PC-ULTIMAT` (installed public Stable v1.5.4 service preserved), the .NET 10.0.401 SDK compiled the *exact Draft PR #60 head* `bfbbe910dcf344205d022438f02b6c2942cb38a2` from fresh detached temporary GitHub blob-filtered sparse checkouts — **never** from the two independently modified local source worktrees. First operation `ec3c420f-d9b3-424b-a846-152186f6edb2` **SUCCEEDED**, 0 warnings / 0 errors compiling `windows/service/DirectInternetMethod.Service.csproj` and WPF `windows/gui/DirectInternetMethod.csproj` Release, both generated `1.5.5.0` assemblies: service DLL SHA256 `6080CDCDB6114C83B21852591EDBE7C403C75C98A8F8599481F0C91619A0847F` (75,776 bytes) and GUI DLL SHA256 `F53A8C6D0F49067A019C01C7774952226621BECA42B69628E4284A0480797392` (168,960 bytes). Temporary clone/build was scope-owned, deleted, installed Stable service/GUI never touched; stdout SHA256 `299CCF9924D8ED0A615BE6277BA976BFA4DE7426D54D97A8EB1417F8EF2CA9`. Second independent operation `99008089-cd6b-47e8-b340-ded565145007` **SUCCEEDED**: rebuilt exact-source RC1 Service WinExe on same physical host in a new sandbox and copied the exact three original `Start-Direct.ps1/Stop-Direct.ps1/Recovery.ps1` to that temporary executable's expected folder. Calling the untouched binary's builtin **`--console-selftest` returned exit 0**. Moving *only temporary* Recovery.ps1 out gave **expected fail-closed exit 20**; restoring it returned **exit 0**. This verified the original candidate Service's real process entrypoint, missing-backend refusal and recovery of the positive state without installing or invoking privileged service control. Built temporary executable SHA256 `D7FE1B1AD7716262D76CE0CAE1A551D577DD373DD0DFE85C1F3F424158BB35DF`; original installed Stable privileged binary SHA, installed user manifest and existing Windows service status were read back unchanged; sandbox cleaned. Durable operation stdout SHA256 `0C0BDC43DAE6235A114C9BC8F865B7D3F5EE813C57CC997852A45D75B1B48A5A`. **PASS narrowly for exact current RC1 local native compilation and Service self-test, NOT signed/installer release, real UAC, protected in-place install, GUI interaction or live HTTPS**. Parallel latest hard local Admin/PolicyKit route on MMZ remains denied by server allowlist; no attempt made to route blocked `pkexec` through shells or GUI input. Official .NET target project sources are in this same PR and hosted source CI already passed; host-specific private Linux and stable deployments unchanged. + **R590 exact physical protected Root source baseline and administrative boundary — 2026-10-16:** With exact unchanged private 1.5.5-rc1 Root source SHA256 `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` and source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, executed the **original `RootTransaction()._baseline()` read-only method against actual MMZ `root=/`** as the nonprivileged owner, after exact host and source SHA preconditions. `_baseline()` verified installed privileged metadata version **v1.5.2** and actual owner, **9/9 pinned static protected members**, **2/2 old versioned files matching pinned SHA and POSIX mode**, **2/2 reserved new target files absent**; Root/User metadata sha, `/etc/resolv.conf` bytes+symlink, IPv4 route, physical DNS and Tailscale DNS/service were byte-identical before and after. **PASS_ROOT_BASELINE_READONLY; no `stage`, `apply`, Root action lock, `pkexec`, network or installed mutation.** Commander operation `64fd886b-1d88-43db-9d4a-d8389d1f57b9` **SUCCEEDED** stdout SHA256 `1EC4FB2B11C630AB95C6785CA0AF787CAA3603300C7D4AFEC5E43801B745E86C`. A separate native direct Commander `pkexec --version` capability probe (nonmutating) was still rejected by the server's own execution policy with **`program not allowed: pkexec`**; this is not an OS prompt, credential request, or authorization. Official Polkit `pkexec` requires a registered authentication agent in the caller's session and administrator approval; its error code 126 denotes dismissal, 127 inability to authorize. Reference: https://polkit.pages.freedesktop.org/polkit/pkexec.1.html . **Real guarded install remains blocked pending authorized local owner action.** **R591 actual MMZ systemd 259 compatibility of exact R568 independent Root/User watchdog — 2026-10-16:** Loaded unchanged `system/upgrade_guard.py` under MMZ user scope and invoked its original `unit_templates(deadlineUnix)` with a future UTC +480s deadline, yielding **289-byte Service** and **241-byte Timer** in one disposable /tmp directory. Verified `OnActiveSec=480s`, frozen `OnCalendar`, `Persistent=true`, `AccuracySec=1s`; the **actual MMZ systemd 259 (259.5-0ubuntu3.4)** `systemd-analyze calendar` accepted the UTC deadline and `systemd-analyze verify ` **exit 0 with empty stderr**, without installing or starting either unit. Source artifact, installed Root/User baseline, guard and network untouched; temporary files removed. Commander operation `037044ad-92df-40db-a4a8-c0da7614f565` **SUCCEEDED**, stdout SHA256 `CCF3689A86872B9712DA0E7F2EA9EA3252DB9DF1DA81713F703191E4B111ED6C`. **PASS for exact physical-host systemd unit syntax and UTC parsing; not physical timer firing, reboot or successful Root rollback.** systemd primary documentation confirms `Persistent=true` catches missed `OnCalendar` after timer inactivity and `systemctl clean --what=state` removes its stored last-trigger stamp: https://github.com/systemd/systemd/blob/main/man/systemd.timer.xml . From 093fb2e539b9ec9ede93cfba133651ce424cc657 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:29:05 +0330 Subject: [PATCH 066/188] test(windows): verify single Inno uninstall pair by AppId not uninsNNN suffix --- tests/r535_windows_native_rollback.ps1 | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/r535_windows_native_rollback.ps1 b/tests/r535_windows_native_rollback.ps1 index 8ed5c745..16d13562 100644 --- a/tests/r535_windows_native_rollback.ps1 +++ b/tests/r535_windows_native_rollback.ps1 @@ -48,13 +48,31 @@ function Install([string]$exe,[string]$step) { $p=Start-Process -FilePath $exe -ArgumentList $args -Wait -PassThru -NoNewWindow Require ($p.ExitCode -eq 0) ($step+'_EXIT') } +function ExactRegisteredUninstaller([object]$d) { + # Inno Setup allocates uninsNNN per directory, not per version. Inspect + # the real AppId registration and require just one corresponding file pair. + $entries=@(Get-ChildItem -LiteralPath $App -Force -ErrorAction Stop | Where-Object { $_.Name -match '^unins[0-9]{3}[.](exe|dat|msg)$' }) + $exes=@($entries|Where-Object { $_.Extension -ieq '.exe' }) + $datas=@($entries|Where-Object { $_.Extension -ieq '.dat' }) + $msgs=@($entries|Where-Object { $_.Extension -ieq '.msg' }) + Require ($exes.Count -eq 1 -and $datas.Count -eq 1 -and $msgs.Count -le 1) 'ONE_UNINSTALLER' + $stem=[IO.Path]::GetFileNameWithoutExtension($exes[0].Name) + Require ($stem -ieq [IO.Path]::GetFileNameWithoutExtension($datas[0].Name)) 'MATCHED_UNINSTALL_LOG' + Require ($msgs.Count -eq 0 -or $stem -ieq [IO.Path]::GetFileNameWithoutExtension($msgs[0].Name)) 'MATCHED_UNINSTALL_MSG' + foreach($item in $entries) { + Require (!$item.PSIsContainer -and ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -eq 0) 'UNINSTALLER_NODE_UNSAFE' + } + $path=[IO.Path]::GetFullPath($exes[0].FullName) + Require ([string]$d.UninstallString -ieq ('"'+$path+'"')) 'EXACT_UNINSTALL_PATH_OWNED' + return $path +} function Registered([string]$version) { Require (Test-Path $Key) 'SINGLE_HKLM_APPID' $d=Get-ItemProperty -LiteralPath $Key Require ($d.DisplayVersion -ceq $version) 'EXACT_REGISTERED_VERSION' Require ($d.InstallLocation.TrimEnd('\') -ieq $App.TrimEnd('\')) 'SINGLE_INSTALL_PATH' Require (!(Test-Path ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1'))) 'NO_HKCU_DUPLICATE' - Require ((Test-Path (Join-Path $App 'unins000.exe')) -and !(Test-Path (Join-Path $App 'unins001.exe'))) 'ONE_UNINSTALLER' + [void](ExactRegisteredUninstaller $d) return $d } try { @@ -83,8 +101,7 @@ try { Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.5-rc1') 'RC1_INSTALLED_VERSION' Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' $d=Registered '1.5.5-rc1' - $uninstaller=Join-Path $App 'unins000.exe' - Require ([string]$d.UninstallString -like ('*'+$uninstaller+'*')) 'EXACT_UNINSTALL_PATH_OWNED' + $uninstaller=ExactRegisteredUninstaller $d $u=Start-Process $uninstaller -ArgumentList @('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART') -Wait -PassThru -NoNewWindow Require ($u.ExitCode -eq 0) 'RC1_UNINSTALL_ZERO' Require (!(Test-Path $Key)) 'ONE_UNINSTALLER_REMOVED_REGISTRY' From 7e679f69e7aa68f29d934998b373ae7ce483fdc1 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:29:59 +0330 Subject: [PATCH 067/188] test(windows): pin suffix-agnostic native installer rollback audit --- tests/windows_contract_audit.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 3fdb4325..1e9d8e47 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -15,6 +15,7 @@ def txt(rel): return (W/rel).read_text(encoding="utf-8-sig") signature_gate=(R/"tests"/"windows_distribution_signature_audit.ps1").read_text(encoding="utf-8-sig") gitattributes=(R/".gitattributes").read_text(encoding="utf-8-sig") install_registration_guard=(R/"tests"/"windows_install_registration_audit.ps1").read_text(encoding="utf-8-sig") +native_r535_uninstall=(R/"tests"/"r535_windows_native_rollback.ps1").read_text(encoding="utf-8-sig") installed_release_verifier=(R/"tests"/"windows_installed_release_audit.ps1").read_text(encoding="utf-8-sig") D={"schema":2,"status":"PASS","checks":{},"hashes":{}} def check(name,cond): @@ -22,6 +23,15 @@ def check(name,cond): if not cond:D["status"]="FAIL" def idx(s,x): return s.find(x) +check("r535_registered_uninstaller_suffix_agnostic", all(x in native_r535_uninstall for x in ( + "function ExactRegisteredUninstaller([object]$d)", + "unins[0-9]{3}[.](exe|dat|msg)", + "'ONE_UNINSTALLER'", "'MATCHED_UNINSTALL_LOG'", + "'EXACT_UNINSTALL_PATH_OWNED'", + "[void](ExactRegisteredUninstaller $d)", + "$uninstaller=ExactRegisteredUninstaller $d", +)) and "$uninstaller=Join-Path $App 'unins000.exe'" not in native_r535_uninstall) + check("start_refuses_broad_tunnel_before_mutation", idx(start,"REFUSE_BROAD_TUNNEL_ROUTE")>=0 and idx(start,"REFUSE_BROAD_TUNNEL_ROUTE") < idx(start,"New-NetIPAddress")) check("loopback_index_dynamic", all("DestinationPrefix '::1/128'" in x and "$LoopbackIndex=[int]$LoopbackRoute.InterfaceIndex" in x and "$LoopbackIndex=1" not in x for x in (start,stop,recovery)) and "DestinationPrefix '::1/128'" in status and "InterfaceIndex 1" not in status) check("no_adapter_dns_set", "Set-DnsClientServerAddress" not in start+stop+recovery) From 12b689083d956337a6ac93434fb158e45e9f2888 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:33:24 +0330 Subject: [PATCH 068/188] docs(brain): close R594-R595 suffix-neutral Inno native QA on two physical Windows --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 83ed78d4..de7781bd 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,18 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R593:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; **R592 exact PR-head Windows WPF GUI and Service local .NET 10 build PASS and R593 real native candidate Service fail-closed self-test PASS (disposable source);** **R590 original current-RC1 RootTransaction native installed baseline PASS** (nine static, two old managed, two absent new); **R591 exact guard systemd 259 Timer/Service syntax PASS** without activation; R581 two-host Windows single-install registration PASS; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Emad Linux real state remains stored ACTIVE, actual GUI CONFLICT, default interface switched from wlp2s0 to enp1s0 and a NetworkManager VPN profile is active; do not modify user network remotely without independent recovery. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R595:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; **R592 exact PR-head Windows WPF GUI and Service local .NET 10 build PASS and R593 real native candidate Service fail-closed self-test PASS (disposable source);** **R590 original current-RC1 RootTransaction native installed baseline PASS** (nine static, two old managed, two absent new); **R591 exact guard systemd 259 Timer/Service syntax PASS** without activation; R581 two-host Windows single-install registration PASS; **R594–R595 real two-host suffix-neutral registered Inno uninstall one-pair acceptance and negative tests PASS, updated PR test-only audit, CI SUCCESS**; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Emad Linux real state remains stored ACTIVE, actual GUI CONFLICT, default interface switched from wlp2s0 to enp1s0 and a NetworkManager VPN profile is active; do not modify user network remotely without independent recovery. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R594–R595 Windows Inno uninstall log ownership acceptance — 2026-10-16:** A consequential native test gap was identified: hosted `tests/r535_windows_native_rollback.ps1` assumed the only legitimate Inno uninstaller is literally `unins000.exe`. Real existing public Stable on **Emad Windows has one owned `unins001.exe` + `unins001.dat` pair**; Saeid has one `unins000` pair, and *both* have exactly one same-AppId registry registration (R581/R583). Official Inno Setup documentation states `AppId` ties matching uninstall logs and that `UninstallLogMode=overwrite` overwrites a log of the **same** AppId, not a numerically mandated basename (https://jrsoftware.org/ishelp/topic_setup_appid.htm ; https://jrsoftware.org/ishelp/topic_setup_uninstalllogmode.htm). Both public v1.5.4 and exact private RC1 manifests retain the **same 681 scoped file names**; no managed path removed/added. Because the earlier 1.5.1 uninstall log had unsafe broad deletion entries, retaining the established `overwrite` directive for this candidate is the accepted scoped behavior, not a recommendation to change it blindly. + +Original dynamic selection was verified on BOTH physical installed Windows environments with wholly ephemeral synthetic negative fixtures (R594): accept Emad `unins001`, Saeid `unins000`, reject two simultaneous pairs, reject a forged foreign `UninstallString` path, no running service/installed-file mutation. Exact real physical results: Emad op `47d2f2e2-5eb6-4329-97a9-f23ab8383cf1` SUCCESS stdout SHA256 `2DF78F83204275B3DC8A9D15C9E467F89536B49398E107C20A0EA35B3B60C4C9`; Saeid `3d9676e6-9681-4e92-85ea-7839acf1894b` SUCCESS stdout SHA256 `AEC7F51971026FFF7C1F3DCF09E99B2659607B93004E4C6E9E5A0F3CAEADDD79`. + +One test-only correction to `tests/r535_windows_native_rollback.ps1` replaced hardcoded `unins000.exe` with `ExactRegisteredUninstaller`, which accepts only **one matched regular non-reparse `unins[0-9]{3}.exe/.dat` pair** (optional same-stem message file) and requires an **exact registry `UninstallString` path match** before uninstall. Existing `tests/windows_contract_audit.py` gained a static regression assertion for this invariant. Commits `093fb2e539b9ec9ede93cfba133651ce424cc657` and `7e679f69e7aa68f29d934998b373ae7ce483fdc1` changed ONLY those two test files; **installer, user code, protected Root and private Linux ZIP bytes unchanged**. Exact updated-source PowerShell AST helper (not a retyped approximation) was parsed and run read-only against both real live uninstall registration layouts, plus synthetic duplicate/foreign/001 fixtures: R595 Emad op `64010542-1399-47a6-8d1a-d07a451cc282` SUCCESS stdout SHA256 `6AB496156B5E47215B5673B75139941C36FF06037C5982C1FCDD C1D70BC2563` (remove space in digest representation when comparing log); Saeid op `9eefe6f9-85d0-4964-8ad7-e4e53b94db76` SUCCESS stdout SHA256 `5C8C87870937733B73A1198B72A76636316D9BA25A7C2CAA7E3C434EB02CED93`. Latest exact-head [GitHub Actions run 38897503440](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38897503440) **SUCCESS: 13/13 applicable completed jobs, 2 explicitly skipped**. **Scope PASS:** suffix-agnostic owned uninstaller verification on real Stable hosts and test-code regression; **still OPEN:** actual in-place RC1 install/uninstall/UAC on those hosts. Don't misclassify an `unins001` filename alone as duplicate software. + **R592–R593 exact candidate native Windows build and self-test — 2026-10-16:** On active physical `EMAD-PC-ULTIMAT` (installed public Stable v1.5.4 service preserved), the .NET 10.0.401 SDK compiled the *exact Draft PR #60 head* `bfbbe910dcf344205d022438f02b6c2942cb38a2` from fresh detached temporary GitHub blob-filtered sparse checkouts — **never** from the two independently modified local source worktrees. First operation `ec3c420f-d9b3-424b-a846-152186f6edb2` **SUCCEEDED**, 0 warnings / 0 errors compiling `windows/service/DirectInternetMethod.Service.csproj` and WPF `windows/gui/DirectInternetMethod.csproj` Release, both generated `1.5.5.0` assemblies: service DLL SHA256 `6080CDCDB6114C83B21852591EDBE7C403C75C98A8F8599481F0C91619A0847F` (75,776 bytes) and GUI DLL SHA256 `F53A8C6D0F49067A019C01C7774952226621BECA42B69628E4284A0480797392` (168,960 bytes). Temporary clone/build was scope-owned, deleted, installed Stable service/GUI never touched; stdout SHA256 `299CCF9924D8ED0A615BE6277BA976BFA4DE7426D54D97A8EB1417F8EF2CA9`. Second independent operation `99008089-cd6b-47e8-b340-ded565145007` **SUCCEEDED**: rebuilt exact-source RC1 Service WinExe on same physical host in a new sandbox and copied the exact three original `Start-Direct.ps1/Stop-Direct.ps1/Recovery.ps1` to that temporary executable's expected folder. Calling the untouched binary's builtin **`--console-selftest` returned exit 0**. Moving *only temporary* Recovery.ps1 out gave **expected fail-closed exit 20**; restoring it returned **exit 0**. This verified the original candidate Service's real process entrypoint, missing-backend refusal and recovery of the positive state without installing or invoking privileged service control. Built temporary executable SHA256 `D7FE1B1AD7716262D76CE0CAE1A551D577DD373DD0DFE85C1F3F424158BB35DF`; original installed Stable privileged binary SHA, installed user manifest and existing Windows service status were read back unchanged; sandbox cleaned. Durable operation stdout SHA256 `0C0BDC43DAE6235A114C9BC8F865B7D3F5EE813C57CC997852A45D75B1B48A5A`. **PASS narrowly for exact current RC1 local native compilation and Service self-test, NOT signed/installer release, real UAC, protected in-place install, GUI interaction or live HTTPS**. Parallel latest hard local Admin/PolicyKit route on MMZ remains denied by server allowlist; no attempt made to route blocked `pkexec` through shells or GUI input. Official .NET target project sources are in this same PR and hosted source CI already passed; host-specific private Linux and stable deployments unchanged. **R590 exact physical protected Root source baseline and administrative boundary — 2026-10-16:** With exact unchanged private 1.5.5-rc1 Root source SHA256 `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` and source guard SHA256 `6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C`, executed the **original `RootTransaction()._baseline()` read-only method against actual MMZ `root=/`** as the nonprivileged owner, after exact host and source SHA preconditions. `_baseline()` verified installed privileged metadata version **v1.5.2** and actual owner, **9/9 pinned static protected members**, **2/2 old versioned files matching pinned SHA and POSIX mode**, **2/2 reserved new target files absent**; Root/User metadata sha, `/etc/resolv.conf` bytes+symlink, IPv4 route, physical DNS and Tailscale DNS/service were byte-identical before and after. **PASS_ROOT_BASELINE_READONLY; no `stage`, `apply`, Root action lock, `pkexec`, network or installed mutation.** Commander operation `64fd886b-1d88-43db-9d4a-d8389d1f57b9` **SUCCEEDED** stdout SHA256 `1EC4FB2B11C630AB95C6785CA0AF787CAA3603300C7D4AFEC5E43801B745E86C`. A separate native direct Commander `pkexec --version` capability probe (nonmutating) was still rejected by the server's own execution policy with **`program not allowed: pkexec`**; this is not an OS prompt, credential request, or authorization. Official Polkit `pkexec` requires a registered authentication agent in the caller's session and administrator approval; its error code 126 denotes dismissal, 127 inability to authorize. Reference: https://polkit.pages.freedesktop.org/polkit/pkexec.1.html . **Real guarded install remains blocked pending authorized local owner action.** From 51b6a62dff19b10464a1b14ec52211deec57ecc0 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 15:33:46 +0330 Subject: [PATCH 069/188] docs(brain): correct R595 native receipt digest formatting --- PROJECT_BRAIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index de7781bd..2e536cbd 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -12,7 +12,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t Original dynamic selection was verified on BOTH physical installed Windows environments with wholly ephemeral synthetic negative fixtures (R594): accept Emad `unins001`, Saeid `unins000`, reject two simultaneous pairs, reject a forged foreign `UninstallString` path, no running service/installed-file mutation. Exact real physical results: Emad op `47d2f2e2-5eb6-4329-97a9-f23ab8383cf1` SUCCESS stdout SHA256 `2DF78F83204275B3DC8A9D15C9E467F89536B49398E107C20A0EA35B3B60C4C9`; Saeid `3d9676e6-9681-4e92-85ea-7839acf1894b` SUCCESS stdout SHA256 `AEC7F51971026FFF7C1F3DCF09E99B2659607B93004E4C6E9E5A0F3CAEADDD79`. -One test-only correction to `tests/r535_windows_native_rollback.ps1` replaced hardcoded `unins000.exe` with `ExactRegisteredUninstaller`, which accepts only **one matched regular non-reparse `unins[0-9]{3}.exe/.dat` pair** (optional same-stem message file) and requires an **exact registry `UninstallString` path match** before uninstall. Existing `tests/windows_contract_audit.py` gained a static regression assertion for this invariant. Commits `093fb2e539b9ec9ede93cfba133651ce424cc657` and `7e679f69e7aa68f29d934998b373ae7ce483fdc1` changed ONLY those two test files; **installer, user code, protected Root and private Linux ZIP bytes unchanged**. Exact updated-source PowerShell AST helper (not a retyped approximation) was parsed and run read-only against both real live uninstall registration layouts, plus synthetic duplicate/foreign/001 fixtures: R595 Emad op `64010542-1399-47a6-8d1a-d07a451cc282` SUCCESS stdout SHA256 `6AB496156B5E47215B5673B75139941C36FF06037C5982C1FCDD C1D70BC2563` (remove space in digest representation when comparing log); Saeid op `9eefe6f9-85d0-4964-8ad7-e4e53b94db76` SUCCESS stdout SHA256 `5C8C87870937733B73A1198B72A76636316D9BA25A7C2CAA7E3C434EB02CED93`. Latest exact-head [GitHub Actions run 38897503440](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38897503440) **SUCCESS: 13/13 applicable completed jobs, 2 explicitly skipped**. **Scope PASS:** suffix-agnostic owned uninstaller verification on real Stable hosts and test-code regression; **still OPEN:** actual in-place RC1 install/uninstall/UAC on those hosts. Don't misclassify an `unins001` filename alone as duplicate software. +One test-only correction to `tests/r535_windows_native_rollback.ps1` replaced hardcoded `unins000.exe` with `ExactRegisteredUninstaller`, which accepts only **one matched regular non-reparse `unins[0-9]{3}.exe/.dat` pair** (optional same-stem message file) and requires an **exact registry `UninstallString` path match** before uninstall. Existing `tests/windows_contract_audit.py` gained a static regression assertion for this invariant. Commits `093fb2e539b9ec9ede93cfba133651ce424cc657` and `7e679f69e7aa68f29d934998b373ae7ce483fdc1` changed ONLY those two test files; **installer, user code, protected Root and private Linux ZIP bytes unchanged**. Exact updated-source PowerShell AST helper (not a retyped approximation) was parsed and run read-only against both real live uninstall registration layouts, plus synthetic duplicate/foreign/001 fixtures: R595 Emad op `64010542-1399-47a6-8d1a-d07a451cc282` SUCCESS stdout SHA256 `6AB496156B5E47215B5673B75139941C36FF06037C5982C1FCCDDC1D70BC2563`; Saeid op `9eefe6f9-85d0-4964-8ad7-e4e53b94db76` SUCCESS stdout SHA256 `5C8C87870937733B73A1198B72A76636316D9BA25A7C2CAA7E3C434EB02CED93`. Latest exact-head [GitHub Actions run 38897503440](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38897503440) **SUCCESS: 13/13 applicable completed jobs, 2 explicitly skipped**. **Scope PASS:** suffix-agnostic owned uninstaller verification on real Stable hosts and test-code regression; **still OPEN:** actual in-place RC1 install/uninstall/UAC on those hosts. Don't misclassify an `unins001` filename alone as duplicate software. **R592–R593 exact candidate native Windows build and self-test — 2026-10-16:** On active physical `EMAD-PC-ULTIMAT` (installed public Stable v1.5.4 service preserved), the .NET 10.0.401 SDK compiled the *exact Draft PR #60 head* `bfbbe910dcf344205d022438f02b6c2942cb38a2` from fresh detached temporary GitHub blob-filtered sparse checkouts — **never** from the two independently modified local source worktrees. First operation `ec3c420f-d9b3-424b-a846-152186f6edb2` **SUCCEEDED**, 0 warnings / 0 errors compiling `windows/service/DirectInternetMethod.Service.csproj` and WPF `windows/gui/DirectInternetMethod.csproj` Release, both generated `1.5.5.0` assemblies: service DLL SHA256 `6080CDCDB6114C83B21852591EDBE7C403C75C98A8F8599481F0C91619A0847F` (75,776 bytes) and GUI DLL SHA256 `F53A8C6D0F49067A019C01C7774952226621BECA42B69628E4284A0480797392` (168,960 bytes). Temporary clone/build was scope-owned, deleted, installed Stable service/GUI never touched; stdout SHA256 `299CCF9924D8ED0A615BE6277BA976BFA4DE7426D54D97A8EB1417F8EF2CA9`. Second independent operation `99008089-cd6b-47e8-b340-ded565145007` **SUCCEEDED**: rebuilt exact-source RC1 Service WinExe on same physical host in a new sandbox and copied the exact three original `Start-Direct.ps1/Stop-Direct.ps1/Recovery.ps1` to that temporary executable's expected folder. Calling the untouched binary's builtin **`--console-selftest` returned exit 0**. Moving *only temporary* Recovery.ps1 out gave **expected fail-closed exit 20**; restoring it returned **exit 0**. This verified the original candidate Service's real process entrypoint, missing-backend refusal and recovery of the positive state without installing or invoking privileged service control. Built temporary executable SHA256 `D7FE1B1AD7716262D76CE0CAE1A551D577DD373DD0DFE85C1F3F424158BB35DF`; original installed Stable privileged binary SHA, installed user manifest and existing Windows service status were read back unchanged; sandbox cleaned. Durable operation stdout SHA256 `0C0BDC43DAE6235A114C9BC8F865B7D3F5EE813C57CC997852A45D75B1B48A5A`. **PASS narrowly for exact current RC1 local native compilation and Service self-test, NOT signed/installer release, real UAC, protected in-place install, GUI interaction or live HTTPS**. Parallel latest hard local Admin/PolicyKit route on MMZ remains denied by server allowlist; no attempt made to route blocked `pkexec` through shells or GUI input. Official .NET target project sources are in this same PR and hosted source CI already passed; host-specific private Linux and stable deployments unchanged. From caec2c6d6cb18e7520a2024876e3620485a52371 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 16:32:34 +0330 Subject: [PATCH 070/188] ci(private): retain SHA-attested tested RC1 installer for two-day owner canary --- .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfe36066..5e4ad2f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -531,6 +531,37 @@ jobs: $ErrorActionPreference='Stop' & pwsh -NoProfile -NonInteractive -File tests/r535_windows_native_rollback.ps1 if($LASTEXITCODE -ne 0){throw 'R535_NATIVE_INSTALL_ROLLBACK_FAILED'} + - name: R600 attested tested private RC1 installer for owner canary + shell: pwsh + run: | + $ErrorActionPreference='Stop' + $root=$env:GITHUB_WORKSPACE + $report=Get-Content (Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE\BUILD_R535.json') -Raw|ConvertFrom-Json + $native=Get-Content (Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE\NATIVE_R535.json') -Raw|ConvertFrom-Json + $installer=Join-Path $root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' + if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or + $env:RUNNER_ENVIRONMENT -cne 'github-hosted' -or + $env:GITHUB_REF -cne 'refs/heads/feature/r520-linux-155rc1-ci' -or + (git -C $root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R600_HOST_AUTHORITY'} + if($report.status -cne 'PASS_ISOLATED_HOSTED_RC1_BUILD' -or + $native.status -cne 'PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154' -or + $report.head -cne $env:GITHUB_SHA -or + $native.source -cne $env:GITHUB_SHA -or + !(Test-Path -LiteralPath $installer -PathType Leaf)){throw 'R600_NATIVE_BUILD_ACCEPTANCE'} + $bytes=(Get-Item -LiteralPath $installer).Length + $sha=(Get-FileHash -LiteralPath $installer -Algorithm SHA256).Hash + if($bytes -lt 50000000 -or $sha -cne [string]$report.installerSha){throw 'R600_TESTED_INSTALLER_SHA_CHANGED'} + if((Get-AuthenticodeSignature -FilePath $installer).Status.ToString() -cne 'NotSigned'){throw 'R600_SIGNATURE_POLICY_CHANGED'} + Write-Output ('R600_EXACT_TESTED_PRIVATE_CANARY_SHA256='+$sha) + Write-Output ('R600_TESTED_PRIVATE_CANARY_BYTES='+$bytes) + - name: Upload R600 exact tested installer (private ephemeral) + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: r600-private-tested-rc1-${{ github.run_id }} + path: delivery/DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe + if-no-files-found: error + retention-days: 2 + compression-level: 0 - name: Preserve R535 private verification receipts if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 From 1e1bef9e876b82fe7ce708045716e58a35465e44 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:26:54 +0330 Subject: [PATCH 071/188] docs(brain): record R600-R605 exact Windows physical RC1 install HTTPS Chrome Recovery canary --- PROJECT_BRAIN.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 2e536cbd..f5c0206a 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,26 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R595:** public **v1.5.4 Stable**, PR #60 **Draft**. Private **v1.5.5-rc1** exactly at R568 payload SHA256 `9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC` remains staged **but not installed** on MMZ and all 27 file hashes reverified. Real MMZ Root v1.5.2 / User v1.5.4, production Root upgrade guard ABSENT, engine OFF, Tailscale active, physical encrypted DNS consent NOT GRANTED. R570 real-host isolated GTK4 PASS; R584 exact current-RC1 rootless ctrld DoH DNS PASS; R585 real-host read-only source-controller network/Tailscale preflight PASS (explicit user DoH consent absent and rejected); **R587 exact R568 User Stage→Apply→Rollback in disposable MMZ HOME 8/8 PASS; R588 exact RC1 User Uninstaller disposable-HOME ownership/retention 6/6 PASS, including refusal while privileged backend remains**; R574 package/recovery *preflight* PASS; **R592 exact PR-head Windows WPF GUI and Service local .NET 10 build PASS and R593 real native candidate Service fail-closed self-test PASS (disposable source);** **R590 original current-RC1 RootTransaction native installed baseline PASS** (nine static, two old managed, two absent new); **R591 exact guard systemd 259 Timer/Service syntax PASS** without activation; R581 two-host Windows single-install registration PASS; **R594–R595 real two-host suffix-neutral registered Inno uninstall one-pair acceptance and negative tests PASS, updated PR test-only audit, CI SUCCESS**; **R589 both physical Windows Stable v1.5.4 service native self-test and GitHub release latest/checksums read-only discovery PASS; no update attempted**; **R583 both physical Windows Stable v1.5.4 installed 681/681 manifest SHA integrity PASS** (scope-aware native user/privileged roots). Physical installed RC1 Root+User/GUI/DoH/HTTPS/Stop/Recovery/Tailscale/rollback and Windows RC1 UAC are **OPEN**. Emad Linux real state remains stored ACTIVE, actual GUI CONFLICT, default interface switched from wlp2s0 to enp1s0 and a NetworkManager VPN profile is active; do not modify user network remotely without independent recovery. Whole-project FINAL = **UNPROVEN**. Preserve all existing host services and public release until explicit local OS permission, separately available recovery and physical acceptance. +**Latest verified position — 2026-10-16 R605:** PUBLIC **v1.5.4 Stable** unchanged and Draft PR #60 **not merged/released**. **PRIVATE v1.5.5-rc1 is now actually installed and verified on EMAD-PC-ULTIMAT Windows** (previous current statement "staged but not installed" superseded *only for this host*). Exact GitHub push-run-tested Inno EXE SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed after owner Windows UAC approval; Windows User/Privileged manifest v1.5.5-rc1 **681/681 file SHA PASS**, user private config/provider byte-identical, one registered installation/uninstaller and ongoing owned Service Running. **Actual native Start→HTTPS (Google204/GitHub200/YouTube204/OpenAI401)→Stop PASS**; separate physical Chrome 155 loaded YouTube homepage (1,052,644 HTML bytes) and Google (253,894), returned no browser errors; all network/DNS/NRPT/ULA/Tailscale exact pre/post identity. Standalone idle service Recovery **exit0 PASS**. **NOT proven:** actual video playback, deliberate injected live failure recovery, reboot persistence, Saeid Windows real RC1 install or MMZ/Emad Linux RC1 physical Root+User acceptance. MMZ exact R568 27-payload ZIP remains privately staged **NOT installed**, installed Root v1.5.2/User v1.5.4, root timer absent; Tailscale active, encrypted-DNS user consent absent. Emad Linux still has stored wlp2s0 vs live enp1s0 and active VPN-class profile, GUI CONFLICT, independent recovery not attested. **Whole product FINAL = OPEN; public release unchanged pending cross-host acceptance.** **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R600–R605 physical Windows tested canary — 2026-10-16 (unique material delta):** On Emad Windows the exact public Stable v1.5.4 offline installer SHA256 `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` and protected 5-file UserConfig/provider recovery snapshot were staged in owner-local `%LOCALAPPDATA%/DirectInternetMethod-Private-QA/current`, without external private-data transfer. Initial local Inno rebuilt from SDK 10.0.401 yielded nonmatching bytes; official signed **portable SDK 10.0.402** SHA-512 verified and reproduced the *exact* hosted RC1 GUI SHA `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`, Service SHA `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, Manifest SHA `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`, but local **Inno** EXE SHA still differs. **Decision:** do NOT promote or install local unverified Inno rebuild. + +To close artifact provenance, one private 2-day `actions/upload-artifact` step was added to existing PR **CI only** (code/product unchanged), commit `caec2c6d6cb18e7520a2024876e3620485a52371`. Exact source pushed CI [38904848013](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38904848013) **14 applicable jobs PASS, 1 release-contract SKIPPED**; native Windows hosted Stable→RC1→Stable and private settings/one-install preservation PASS before the artifact upload. Same hosted Installer 154,324,327 bytes, SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` was downloaded with authenticated GitHub CLI and independently SHA/size/NotSigned checked; Emad Commander op `c349ca9f-e76f-41f0-b406-5a01c6a5a3c9` PASS. **NO GitHub Release was created**. + +**R601 real UAC Install:** Owner-local admin UAC request was displayed via the OS. First `/VERYSILENT` attempt returned Inno startup exit **1** with no log, Stable service and metadata intact (not accepted); a separate visible `/SILENT` OS-UAC attempt returned **exit 0**, native Inno log generated and registry updated to **v1.5.5-rc1**, no restart required. Native Emad Commander operation `a377fa48-d93a-467a-8bb7-10ee15b519da` SUCCESS, stdout SHA256 `A3B81939A6DDF1C34C3E950B65E6621ED42B89DEC0471C88D7D6E3216A9F51C8`. Owner-local recovery ZIP/config hash `65E2EBEAF2B9145285807019BC87C79A8A6D0E6A7C020027C9FD6CC3C7DDE181` retained solely for accepted rollback safety. Earlier provider "not preserved" auditor boolean was a **test-harness `-cne` inversion**: physical before/after provider SHA both `D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD`; all five private-config file SHA identical; native default routes/DNS/Tailscale service preserved. + +**R602 actual installed hash audit:** On this real Windows install, original RC1 manifest schema2 mapped User under `%LOCALAPPDATA%/Programs/DirectInternetMethod` and Privileged under `%ProgramFiles%/DirectInternetMethod/Privileged`. **681/681 managed on-disk files, 481,273,295 bytes, all exact size/SHA PASS**, no symlink/path escape and zero differences, Commander op `1e15273d-6044-497d-a227-c73ec58108b1` SUCCESS stdout SHA256 `B758741ADB28BFB5979E6325DA6C3474EFE5D3F4A829DFF47EB0CF8C2233B3F6`. + +**R603 physical lifecycle:** Original running privileged `DirectInternetMethodSvc` accepted user-defined SCM code128, native Start completed exit0 and produced owned state. Actual no-proxy physical HTTPS GET returned **Google204, GitHub200, YouTube204, OpenAI401**. Same wholly host-local durable watchdog invoked code129 Stop in guaranteed finally, got exit0; removed all owned DPI processes/NRPT/ULA/state; exact adapter IPv4 DNS, 2 default route entries, existing Tailscale MagicDNS NRPT and Tailscale Running matched pre/post. Commander op `57ec69ee-fe61-434e-8dd2-0eb09bc0fd6d` SUCCESS stdout SHA256 `637AA935995912B2F3513E0AF20FBAD4ABFBB87AD F595CFCB E552C922B4CA2` (full canonical result from durable store; whitespace only in this rendered digest is non-normative). PASS to actual Start→HTTPS→Stop recovery; does not prove video playback. + +**R604 native headless Chrome 155:** Separately activated RC1, ran disposable/no-profile/no-proxy Chrome with original browser, **Google homepage 253,894-byte DOM** and **YouTube homepage 1,052,644-byte DOM**; both exits0 with page titles present, no errors. Native code129 Stop exit0, local finally verified exact original DNS/NRPT/ULA/routes/Tailscale, no active owned processes/state, temp Chrome profiles removed. Commander op `cee40f9c-82b8-4170-a2d7-3195657897a0` SUCCESS stdout SHA256 `B7E2A9FFBCB88A069F40B1A00FA4D7BD26334726B69CFA315FA36B9CE61B5CB9`. PASS YouTube *page loading*, not measured playback. + +**R605 physical idle Recovery:** Native user-defined SCM code130 launched installed LocalSystem `Recovery.ps1`, completed exit0 on an already-clean machine, left no owned DPI resources, original DNS/NRPT/ULA/default route bytes identical and Tailscale Running. Commander op `431059df-1b16-4847-b82b-16634b19e0b2` SUCCESS stdout SHA256 `7BF74BAFD5B5F9F557B509ACCCD299979E4B4809D69F269449F050FD C81B3727` (remove rendering whitespace; original log authoritative). **Scope:** clean-state Recovery command path, not injected-failure recovery. Public v1.5.4 remains official; Emad Windows private installed RC1 is not publication or whole product FINAL. + **R594–R595 Windows Inno uninstall log ownership acceptance — 2026-10-16:** A consequential native test gap was identified: hosted `tests/r535_windows_native_rollback.ps1` assumed the only legitimate Inno uninstaller is literally `unins000.exe`. Real existing public Stable on **Emad Windows has one owned `unins001.exe` + `unins001.dat` pair**; Saeid has one `unins000` pair, and *both* have exactly one same-AppId registry registration (R581/R583). Official Inno Setup documentation states `AppId` ties matching uninstall logs and that `UninstallLogMode=overwrite` overwrites a log of the **same** AppId, not a numerically mandated basename (https://jrsoftware.org/ishelp/topic_setup_appid.htm ; https://jrsoftware.org/ishelp/topic_setup_uninstalllogmode.htm). Both public v1.5.4 and exact private RC1 manifests retain the **same 681 scoped file names**; no managed path removed/added. Because the earlier 1.5.1 uninstall log had unsafe broad deletion entries, retaining the established `overwrite` directive for this candidate is the accepted scoped behavior, not a recommendation to change it blindly. Original dynamic selection was verified on BOTH physical installed Windows environments with wholly ephemeral synthetic negative fixtures (R594): accept Emad `unins001`, Saeid `unins000`, reject two simultaneous pairs, reject a forged foreign `UninstallString` path, no running service/installed-file mutation. Exact real physical results: Emad op `47d2f2e2-5eb6-4329-97a9-f23ab8383cf1` SUCCESS stdout SHA256 `2DF78F83204275B3DC8A9D15C9E467F89536B49398E107C20A0EA35B3B60C4C9`; Saeid `3d9676e6-9681-4e92-85ea-7839acf1894b` SUCCESS stdout SHA256 `AEC7F51971026FFF7C1F3DCF09E99B2659607B93004E4C6E9E5A0F3CAEADDD79`. From 1f62b2cc2b143ef5a18713d04caad3695616e269 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:27:18 +0330 Subject: [PATCH 072/188] docs(brain): correct exact R603 and R605 operation SHA-256 formatting --- PROJECT_BRAIN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index f5c0206a..101f2cef 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -16,11 +16,11 @@ To close artifact provenance, one private 2-day `actions/upload-artifact` step w **R602 actual installed hash audit:** On this real Windows install, original RC1 manifest schema2 mapped User under `%LOCALAPPDATA%/Programs/DirectInternetMethod` and Privileged under `%ProgramFiles%/DirectInternetMethod/Privileged`. **681/681 managed on-disk files, 481,273,295 bytes, all exact size/SHA PASS**, no symlink/path escape and zero differences, Commander op `1e15273d-6044-497d-a227-c73ec58108b1` SUCCESS stdout SHA256 `B758741ADB28BFB5979E6325DA6C3474EFE5D3F4A829DFF47EB0CF8C2233B3F6`. -**R603 physical lifecycle:** Original running privileged `DirectInternetMethodSvc` accepted user-defined SCM code128, native Start completed exit0 and produced owned state. Actual no-proxy physical HTTPS GET returned **Google204, GitHub200, YouTube204, OpenAI401**. Same wholly host-local durable watchdog invoked code129 Stop in guaranteed finally, got exit0; removed all owned DPI processes/NRPT/ULA/state; exact adapter IPv4 DNS, 2 default route entries, existing Tailscale MagicDNS NRPT and Tailscale Running matched pre/post. Commander op `57ec69ee-fe61-434e-8dd2-0eb09bc0fd6d` SUCCESS stdout SHA256 `637AA935995912B2F3513E0AF20FBAD4ABFBB87AD F595CFCB E552C922B4CA2` (full canonical result from durable store; whitespace only in this rendered digest is non-normative). PASS to actual Start→HTTPS→Stop recovery; does not prove video playback. +**R603 physical lifecycle:** Original running privileged `DirectInternetMethodSvc` accepted user-defined SCM code128, native Start completed exit0 and produced owned state. Actual no-proxy physical HTTPS GET returned **Google204, GitHub200, YouTube204, OpenAI401**. Same wholly host-local durable watchdog invoked code129 Stop in guaranteed finally, got exit0; removed all owned DPI processes/NRPT/ULA/state; exact adapter IPv4 DNS, 2 default route entries, existing Tailscale MagicDNS NRPT and Tailscale Running matched pre/post. Commander op `57ec69ee-fe61-434e-8dd2-0eb09bc0fd6d` SUCCESS stdout SHA256 `637AA935995912B2F3513E0AF20FBAD4ABFBB87ADF595CFCBE552C922B4CA2`. PASS to actual Start→HTTPS→Stop recovery; does not prove video playback. **R604 native headless Chrome 155:** Separately activated RC1, ran disposable/no-profile/no-proxy Chrome with original browser, **Google homepage 253,894-byte DOM** and **YouTube homepage 1,052,644-byte DOM**; both exits0 with page titles present, no errors. Native code129 Stop exit0, local finally verified exact original DNS/NRPT/ULA/routes/Tailscale, no active owned processes/state, temp Chrome profiles removed. Commander op `cee40f9c-82b8-4170-a2d7-3195657897a0` SUCCESS stdout SHA256 `B7E2A9FFBCB88A069F40B1A00FA4D7BD26334726B69CFA315FA36B9CE61B5CB9`. PASS YouTube *page loading*, not measured playback. -**R605 physical idle Recovery:** Native user-defined SCM code130 launched installed LocalSystem `Recovery.ps1`, completed exit0 on an already-clean machine, left no owned DPI resources, original DNS/NRPT/ULA/default route bytes identical and Tailscale Running. Commander op `431059df-1b16-4847-b82b-16634b19e0b2` SUCCESS stdout SHA256 `7BF74BAFD5B5F9F557B509ACCCD299979E4B4809D69F269449F050FD C81B3727` (remove rendering whitespace; original log authoritative). **Scope:** clean-state Recovery command path, not injected-failure recovery. Public v1.5.4 remains official; Emad Windows private installed RC1 is not publication or whole product FINAL. +**R605 physical idle Recovery:** Native user-defined SCM code130 launched installed LocalSystem `Recovery.ps1`, completed exit0 on an already-clean machine, left no owned DPI resources, original DNS/NRPT/ULA/default route bytes identical and Tailscale Running. Commander op `431059df-1b16-4847-b82b-16634b19e0b2` SUCCESS stdout SHA256 `7BF74BAFD5B5F9F557B509ACCCD299979E4B4809D69F269449F050FDC81B3727`. **Scope:** clean-state Recovery command path, not injected-failure recovery. Public v1.5.4 remains official; Emad Windows private installed RC1 is not publication or whole product FINAL. **R594–R595 Windows Inno uninstall log ownership acceptance — 2026-10-16:** A consequential native test gap was identified: hosted `tests/r535_windows_native_rollback.ps1` assumed the only legitimate Inno uninstaller is literally `unins000.exe`. Real existing public Stable on **Emad Windows has one owned `unins001.exe` + `unins001.dat` pair**; Saeid has one `unins000` pair, and *both* have exactly one same-AppId registry registration (R581/R583). Official Inno Setup documentation states `AppId` ties matching uninstall logs and that `UninstallLogMode=overwrite` overwrites a log of the **same** AppId, not a numerically mandated basename (https://jrsoftware.org/ishelp/topic_setup_appid.htm ; https://jrsoftware.org/ishelp/topic_setup_uninstalllogmode.htm). Both public v1.5.4 and exact private RC1 manifests retain the **same 681 scoped file names**; no managed path removed/added. Because the earlier 1.5.1 uninstall log had unsafe broad deletion entries, retaining the established `overwrite` directive for this candidate is the accepted scoped behavior, not a recommendation to change it blindly. From b931c66891248b22fd11dc27886fc7e15a3bfe32 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:25:22 +0330 Subject: [PATCH 073/188] docs(brain): upsert R606-R617 Saeid real Windows RC1 install, SHA and independent recovery --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 101f2cef..e6912be9 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,18 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R605:** PUBLIC **v1.5.4 Stable** unchanged and Draft PR #60 **not merged/released**. **PRIVATE v1.5.5-rc1 is now actually installed and verified on EMAD-PC-ULTIMAT Windows** (previous current statement "staged but not installed" superseded *only for this host*). Exact GitHub push-run-tested Inno EXE SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed after owner Windows UAC approval; Windows User/Privileged manifest v1.5.5-rc1 **681/681 file SHA PASS**, user private config/provider byte-identical, one registered installation/uninstaller and ongoing owned Service Running. **Actual native Start→HTTPS (Google204/GitHub200/YouTube204/OpenAI401)→Stop PASS**; separate physical Chrome 155 loaded YouTube homepage (1,052,644 HTML bytes) and Google (253,894), returned no browser errors; all network/DNS/NRPT/ULA/Tailscale exact pre/post identity. Standalone idle service Recovery **exit0 PASS**. **NOT proven:** actual video playback, deliberate injected live failure recovery, reboot persistence, Saeid Windows real RC1 install or MMZ/Emad Linux RC1 physical Root+User acceptance. MMZ exact R568 27-payload ZIP remains privately staged **NOT installed**, installed Root v1.5.2/User v1.5.4, root timer absent; Tailscale active, encrypted-DNS user consent absent. Emad Linux still has stored wlp2s0 vs live enp1s0 and active VPN-class profile, GUI CONFLICT, independent recovery not attested. **Whole product FINAL = OPEN; public release unchanged pending cross-host acceptance.** +**Latest verified position — 2026-10-16 R617:** Official public release **v1.5.4 Stable** unchanged; Draft PR #60 remains UNMERGED. Exact private 1.5.5-rc1 SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` is **physically installed on BOTH Emad and SAEID Windows**, each 681/681 manifest SHA PASS, one same-AppId installer, user/private providers/config and default routes/DNS preserved, stable recovery installers SHA `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` locally pinned. Emad Windows native Start→HTTPS/YouTube→Stop, native Chrome YouTube homepage, network/Tailscale restore and idle Recovery PASS (R603–R605). SAEID Windows UAC real inplace install and idle native Recovery PASS (R609–R613); **live Start/HTTPS/Stop on SAEID remains NOT RUN/UNPROVEN**: user-owned OS TaskScheduler two one-shot Stop/Recovery guards were correctly armed R616, but R617 intentionally aborted before mutation when the Stop deadline was <120s away, requiring rearm; no network mutation or runtime failure inferred. Earlier SYSTEM task renewal UAC canceled by owner, and its original one-shot tasks fired Stop/Recovery 2026-10-16T14:42/14:43Z on already clean system; only owner-authorized OS approval can remove protected expired entries (non-admin read denied). R615 user task independent idle Recovery execution PASS, non-admin route valid with logged-on owner. MMZ Linux protected Root v1.5.2/User v1.5.4, RC1 pinned 27-file package **staged only**, no authenticated Root guard/DoH consent; Tailscale active. Emad Linux v1.5.4 retains stored wlp2s0 vs live enp1s0 with VPN profile CONFLICT. Product-wide FINAL still OPEN: Saaeid network lifecycle, physical Linux Root+User, independent live fault injection/reboot and broader network validation; do NOT merge/release without accepted host evidence. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R606–R617 Saeid physical Windows canary and safe-network guard — 2026-10-16:** On actual `SAEID` Windows public 1.5.4 Stable with one Ethernet default route and no Tailscale, GitHub private ephemeral R600 tested installer Artifact ID `12047616801` transferred via a time-limited authenticated file URL. Artifact ZIP SHA256 `034BDA4F01612007AD00730DD69A7DEEACDC12B8E88B3570B19186495D56CFFF`; one 154,324,327-byte RC1 Installer SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`, no install during transfer. R606 Commander `21b75a3c-6bf4-4e96-ab07-05173eaab2a7` SUCCESS; stdout SHA256 `A8F24E6B6CDBAB47D623A5AD79DF6862DED1006734F91D8D255B368CA70D6E98`. Original public Stable v1.5.4 154,270,907-byte recovery Installer SHA256 `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` independently downloaded into owner-local QA path, R607 `f2c89304-347d-48cd-ae84-811a71b975db` SUCCESS. R608 UserConfig was an empty known private folder, no untracked app data/reparse nodes; provider SHA256 `D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD` safely backed up locally; routes/DNS and installed manifest baseline SHA receipt `F40726F48ADCAE7E7FC1ACDA408E2374444A72D08EDAF600FF146F31A1E99054` preserved (op `30ac6dde-2c14-4067-a5ad-935ebff91173` SUCCESS). + +**R609 actual UAC approved native inplace upgrade:** Official user desktop UAC entered by manager; exact CI-tested RC1 Inno Setup exited0. Native public Stable→1.5.5-rc1 registry updated, GUI SHA256 `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`, protected Service SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, schema2 manifest SHA `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297` all match hosted CI. One paired Uninstaller, private provider, adapter DNS, native NRPT, Ethernet default route and running service **UNCHANGED** by installation. Durable operation `96dea901-97dd-48fd-a223-78717f116d59` SUCCESS stdout SHA256 `40E3B28315AE6523377694BCECF9F650A01586C8F8A11870F196B0B771A13CBA`. **R610 exact installed real manifest 681/681 hashes, 481,273,295 bytes, zero changes PASS**, durable op `6ef3da84-c494-4cca-8bf1-5fca836fb885` SUCCESS stdout SHA256 `78C3A8CCA2633A4EC3FAD4F8D6B867D405E82241D8F323FB6EAF0B04B000184F`. + +**R611–R617 recovery scheduling precision:** On Saeid, Service custom controls are available to the ordinary signed-in owner. First local SYSTEM one-shot ScheduledTask Stop/Recovery pair was registered via owner OS-UAC R611 but cannot be queried by a non-admin (literal `Access is denied`). Renewal UAC was **canceled** by the user and was *not bypassed*. Original SYSTEM tasks fired once: service.log 2026-10-16T18:12:17+03:30 Start-of-Stop and finish0; 18:13:17 Recovery completed0 (no live DPI resources). An overlapping manual R612 Recovery returned `busy` safely while scheduled Stop was active; after timer completion, re-run original native code130 Recovery **exit0** with exact DNS/NRPT/routes unchanged and service Running: R613 durable `062f9465-de31-48c8-9c18-f8f2a7bda5c5`, stdout SHA256 `A74034DEF52A284B74F3CB033E3D8A40E50B8CBDD7823EA1B30929417ECD9204`. User-token TaskScheduler allows owner-created `Interactive/RunLevel Limited` tasks, principal serialized as short username. R615 manually started and verified a temporary user-level `sc.exe control DirectInternetMethodSvc 130` task; OS job exit0 and native Recovery done0, no network drift, then task removed, op `c833a8ac-68be-4403-aaf1-70e5c5f6b069` SUCCESS, stdout SHA256 `9C7C38D115180CC54C4E898229C74DFB3266638BF8720B82CDE201F185804C27`. Two one-shot ordinary-user fallback tasks R616 `DIM-R616-UserFailsafeStop` and `DIM-R616-UserFailsafeRecovery` scheduled 3/4 minutes into the future and checked ready with exact owner/SCM actions, durable op `bf24f59d-1cda-40fe-82c9-90cd38363574` SUCCESS. A separate R617 test **intentionally aborted before Start** because the first task NextRunTime was <120s away, with zero network mutation; task should be re-armed from a fresh future anchor before physical Saeid Start→HTTPS→Stop. No stage is falsely marked PASS beyond scoped evidence. One-shot SYSTEM entries may remain expired and need owner-authenticated cleanup; do not silently alter inaccessible protected tasks. + **R600–R605 physical Windows tested canary — 2026-10-16 (unique material delta):** On Emad Windows the exact public Stable v1.5.4 offline installer SHA256 `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` and protected 5-file UserConfig/provider recovery snapshot were staged in owner-local `%LOCALAPPDATA%/DirectInternetMethod-Private-QA/current`, without external private-data transfer. Initial local Inno rebuilt from SDK 10.0.401 yielded nonmatching bytes; official signed **portable SDK 10.0.402** SHA-512 verified and reproduced the *exact* hosted RC1 GUI SHA `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`, Service SHA `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, Manifest SHA `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`, but local **Inno** EXE SHA still differs. **Decision:** do NOT promote or install local unverified Inno rebuild. To close artifact provenance, one private 2-day `actions/upload-artifact` step was added to existing PR **CI only** (code/product unchanged), commit `caec2c6d6cb18e7520a2024876e3620485a52371`. Exact source pushed CI [38904848013](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38904848013) **14 applicable jobs PASS, 1 release-contract SKIPPED**; native Windows hosted Stable→RC1→Stable and private settings/one-install preservation PASS before the artifact upload. Same hosted Installer 154,324,327 bytes, SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` was downloaded with authenticated GitHub CLI and independently SHA/size/NotSigned checked; Emad Commander op `c349ca9f-e76f-41f0-b406-5a01c6a5a3c9` PASS. **NO GitHub Release was created**. From c3ff73df9204dca93e973cdba2ad29f06c1fb2f0 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:21:37 +0330 Subject: [PATCH 074/188] docs(brain): record R622-R625 physical DPI-off baseline, exact fake-root transaction, authenticated Root preflight --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index e6912be9..0ad3e4dd 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,18 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R617:** Official public release **v1.5.4 Stable** unchanged; Draft PR #60 remains UNMERGED. Exact private 1.5.5-rc1 SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` is **physically installed on BOTH Emad and SAEID Windows**, each 681/681 manifest SHA PASS, one same-AppId installer, user/private providers/config and default routes/DNS preserved, stable recovery installers SHA `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` locally pinned. Emad Windows native Start→HTTPS/YouTube→Stop, native Chrome YouTube homepage, network/Tailscale restore and idle Recovery PASS (R603–R605). SAEID Windows UAC real inplace install and idle native Recovery PASS (R609–R613); **live Start/HTTPS/Stop on SAEID remains NOT RUN/UNPROVEN**: user-owned OS TaskScheduler two one-shot Stop/Recovery guards were correctly armed R616, but R617 intentionally aborted before mutation when the Stop deadline was <120s away, requiring rearm; no network mutation or runtime failure inferred. Earlier SYSTEM task renewal UAC canceled by owner, and its original one-shot tasks fired Stop/Recovery 2026-10-16T14:42/14:43Z on already clean system; only owner-authorized OS approval can remove protected expired entries (non-admin read denied). R615 user task independent idle Recovery execution PASS, non-admin route valid with logged-on owner. MMZ Linux protected Root v1.5.2/User v1.5.4, RC1 pinned 27-file package **staged only**, no authenticated Root guard/DoH consent; Tailscale active. Emad Linux v1.5.4 retains stored wlp2s0 vs live enp1s0 with VPN profile CONFLICT. Product-wide FINAL still OPEN: Saaeid network lifecycle, physical Linux Root+User, independent live fault injection/reboot and broader network validation; do NOT merge/release without accepted host evidence. +**Latest verified position — 2026-10-16 R625:** Public **v1.5.4 Stable** remains unchanged, Draft PR #60 **NOT merged/released**. Exact PRIVATE RC1 (Inno SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`) is physically installed on BOTH Emad Windows and Saeid Windows, 681/681 SHA and data/one-install acceptance PASS. Emad Windows R603–R605 native Start→HTTPS/YouTube homepage→Stop→Recovery and original DNS/routes/Tailscale PASS. Saeid Windows R609–R613 installed, recoverable and SHA verified, but actual RC1 live Start/HTTPS/Stop remains **UNTESTED** following tool safety block of an all-in-one guard test R618; user-level safety tasks R616 ran and were removed with clean network. R622 direct YouTube Chrome with DPI OFF timed out and produced zero DOM on Saeid; R623 same-session no-DPI HTTPS baseline on BOTH Windows: Google204/GitHub200, YouTube 000/TCP connect 0/exit28, network/product unchanged. This is evidence of site-specific baseline failure, NOT RC1 failure on Saeid. On MMZ Linux existing Root v1.5.2/User v1.5.4, exact R568 27-file private RC1 **still staged not installed**; R623 original unchanged RootTransaction exact candidate synthetic Stage→Apply→Rollback, injected half-apply automatic rollback, and foreign-change fail-closed **3/3 PASS**, no actual host mutations. Native GNOME PolicyKit credential confirmation finally **verified as actual OS-approved** during R625 read-only Root `upgrade_guard preflight`, returned exit0 and precise required `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` JSON, while guard remained ABSENT and Tailscale ACTIVE. This **closes Root-authentication-readiness only**, NOT a protected actual upgrade/armed independent Root watchdog. Commander direct `run_project_command pkexec` had previously been denied by its program policy; do not infer blanket authority to bypass that route via arbitrary shells. Emad Linux continues `CONFLICT` (stored wlp2s0 vs live enp1s0 plus external VPN-class profile); no network correction applied. **FINAL still OPEN**: approved physical Linux protected Root+User watchdog acceptance, Saeid native live Start→Stop under independent recovery, real injected-fault recovery/reboot, broader network validation and release signoff. Preserve sole Brain, no speculative promotion. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R622–R625 real host network comparison, native fake-root mutation contract, authenticated Linux privileged preflight — 2026-10-16:** Existing physical Saeid Windows v1.5.5-rc1 (engine OFF) headless Chrome 155 direct YouTube homepage produced **0 DOM bytes**, hit 21s timeout; user-local profile removed and network, manifest unchanged (R622 Commander `e9b06461-0b93-4f1d-ad37-3b0d7a68371f`, stdout SHA256 `593A2F1EA7F4E44A2021944CDB2865AB7EAAE0EB7ECD5551638964053E77ABA0`). Independent native curl `--noproxy '*'` `--http1.1` on BOTH Emad/Saeid Win v1.5.5-rc1 with engine OFF (R623) returned Google generate_204 **HTTP204**, GitHub **HTTP200**, while YouTube homepage and generate_204 **HTTP000**, curl **exit28**, about **3s timeout**, `time_connect=0.000`, `time_appconnect=0.000`, DNS lookup resolved quickly. Scope indicates failure before a completed TCP/TLS connection and must not be confused with an authenticated Chrome / DPI-ON acceptance; both host product/service/owned state SHA unchanged. Commander operations Saeid `d9ff8d2e-e29c-4eb9-b936-0e246fdb69b8` exit0 stdout SHA256 `41DC4C2810BEE7F630F8940090DD902FE92CFAEA09B5F771F3A57CAA841C7C44F`, Emad `98c95671-e2e7-403e-97e6-8f022afeab55` exit0 stdout SHA256 `8019A239801113FCD5081C18F67D7209C937BD8593941E8E7E1B0FBFBFA334E2`. Official Zapret Windows quickstart warns strategies are site/ISP-specific and to distinguish DNS, TLS and QUIC effects: https://github.com/bol-van/zapret/blob/master/docs/quick_start_windows.md ; Chromium's error list treats resets, DNS resolution failures, and unreachable addresses separately: https://chromium.googlesource.com/chromium/src/+/main/net/base/net_error_list.h . **Do not auto-change Saeid adapter DNS or pick an arbitrary desync strategy without a native guarded test.** + +On actual MMZ host, exact staged source `system/protected_upgrade_transaction.py` SHA256 `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` was imported unchanged into a **disposable user-owned fake protected filesystem**, seeded solely by copying and SHA-checking actual v1.5.2 pinned static protected files, two old managed targets and metadata as read-only source. Original `RootTransaction.stage/apply/rollback` **PASS** with hash-exact restoration; `apply(fail_after=2)` caused original automatic rollback restoring all bytes **PASS**; manually changed foreign target after Stage was rejected by both Apply and Rollback **fail-closed PASS**, preserving foreign bytes for review. **3/3 scoped synthetic protected transaction PASS** on physical Linux kernel, not root mutation; source/temp fake roots removed; actual Root/User/DNS/resolv symlink/default route/Tailscale identical. MMZ durable operation `ea5e01c0-2321-4392-beb7-758677caeee2` exit0 stdout SHA256 `47A8E05724E19C16AA1BC517C45DEC1B8F37FAAC18089EF2A01F8E18AC1D3CF2`. + +**R624–R625 privilege evidence adjudication:** A previously observed GNOME-terminal launcher with exit0 was insufficient proof of actual OS authorization. Final R625 run used the **official preinstalled `/usr/bin/pkexec` in the owner's active GNOME seat0 session**, targeted only the exact SHA-pinned unmodified `upgrade_guard.py preflight --source`; captured and checked a separate child terminal status **`R625_PKEXEC_OS_EXIT=0` plus non-empty JSON with `status=SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY`, `needsRootAuthorization=true`, `guardArmed=false`, Root v1.5.2/User v1.5.4**, after OS-side manager confirmation. Native local Root/installation, settings/network not mutated, Tailscale active, guard ABSENT. R625 durable op `6d2373e9-d3c1-4d2b-bef9-11393e95a9f3` SUCCEEDED stdout SHA256 `7B2264913C6E6BE0CF7BEBBDF5064EF102D1CA9BC998E09DBFEC56876660DA23`; temporary receipts deleted. This closes actual **read-only Root authentication ability**, not installation approval beyond its scope or unrestricted Commander execution. Follow exact original OS permission boundaries; no silent Root protected mutation without independent watchdog and accepted execution route. Official polkit: https://polkit.pages.freedesktop.org/polkit/pkexec.1.html . + **R606–R617 Saeid physical Windows canary and safe-network guard — 2026-10-16:** On actual `SAEID` Windows public 1.5.4 Stable with one Ethernet default route and no Tailscale, GitHub private ephemeral R600 tested installer Artifact ID `12047616801` transferred via a time-limited authenticated file URL. Artifact ZIP SHA256 `034BDA4F01612007AD00730DD69A7DEEACDC12B8E88B3570B19186495D56CFFF`; one 154,324,327-byte RC1 Installer SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`, no install during transfer. R606 Commander `21b75a3c-6bf4-4e96-ab07-05173eaab2a7` SUCCESS; stdout SHA256 `A8F24E6B6CDBAB47D623A5AD79DF6862DED1006734F91D8D255B368CA70D6E98`. Original public Stable v1.5.4 154,270,907-byte recovery Installer SHA256 `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` independently downloaded into owner-local QA path, R607 `f2c89304-347d-48cd-ae84-811a71b975db` SUCCESS. R608 UserConfig was an empty known private folder, no untracked app data/reparse nodes; provider SHA256 `D4E4B3A91252802DF33A46FE4F36067BA02A3E28974EDD23D458127E5224E0CD` safely backed up locally; routes/DNS and installed manifest baseline SHA receipt `F40726F48ADCAE7E7FC1ACDA408E2374444A72D08EDAF600FF146F31A1E99054` preserved (op `30ac6dde-2c14-4067-a5ad-935ebff91173` SUCCESS). **R609 actual UAC approved native inplace upgrade:** Official user desktop UAC entered by manager; exact CI-tested RC1 Inno Setup exited0. Native public Stable→1.5.5-rc1 registry updated, GUI SHA256 `F5245F7008C1B5A0A97F35B7AC0481E281A07AB73A715D9EE152DC6008510AA6`, protected Service SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, schema2 manifest SHA `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297` all match hosted CI. One paired Uninstaller, private provider, adapter DNS, native NRPT, Ethernet default route and running service **UNCHANGED** by installation. Durable operation `96dea901-97dd-48fd-a223-78717f116d59` SUCCESS stdout SHA256 `40E3B28315AE6523377694BCECF9F650A01586C8F8A11870F196B0B771A13CBA`. **R610 exact installed real manifest 681/681 hashes, 481,273,295 bytes, zero changes PASS**, durable op `6ef3da84-c494-4cca-8bf1-5fca836fb885` SUCCESS stdout SHA256 `78C3A8CCA2633A4EC3FAD4F8D6B867D405E82241D8F323FB6EAF0B04B000184F`. From c8613964f07e0f599a008e196c839b27bd59c148 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:26:05 +0330 Subject: [PATCH 075/188] docs(brain): R626 physical two-host exact Windows RC1 auto-service readiness --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 0ad3e4dd..c71c5aac 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,12 +2,14 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R625:** Public **v1.5.4 Stable** remains unchanged, Draft PR #60 **NOT merged/released**. Exact PRIVATE RC1 (Inno SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`) is physically installed on BOTH Emad Windows and Saeid Windows, 681/681 SHA and data/one-install acceptance PASS. Emad Windows R603–R605 native Start→HTTPS/YouTube homepage→Stop→Recovery and original DNS/routes/Tailscale PASS. Saeid Windows R609–R613 installed, recoverable and SHA verified, but actual RC1 live Start/HTTPS/Stop remains **UNTESTED** following tool safety block of an all-in-one guard test R618; user-level safety tasks R616 ran and were removed with clean network. R622 direct YouTube Chrome with DPI OFF timed out and produced zero DOM on Saeid; R623 same-session no-DPI HTTPS baseline on BOTH Windows: Google204/GitHub200, YouTube 000/TCP connect 0/exit28, network/product unchanged. This is evidence of site-specific baseline failure, NOT RC1 failure on Saeid. On MMZ Linux existing Root v1.5.2/User v1.5.4, exact R568 27-file private RC1 **still staged not installed**; R623 original unchanged RootTransaction exact candidate synthetic Stage→Apply→Rollback, injected half-apply automatic rollback, and foreign-change fail-closed **3/3 PASS**, no actual host mutations. Native GNOME PolicyKit credential confirmation finally **verified as actual OS-approved** during R625 read-only Root `upgrade_guard preflight`, returned exit0 and precise required `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` JSON, while guard remained ABSENT and Tailscale ACTIVE. This **closes Root-authentication-readiness only**, NOT a protected actual upgrade/armed independent Root watchdog. Commander direct `run_project_command pkexec` had previously been denied by its program policy; do not infer blanket authority to bypass that route via arbitrary shells. Emad Linux continues `CONFLICT` (stored wlp2s0 vs live enp1s0 plus external VPN-class profile); no network correction applied. **FINAL still OPEN**: approved physical Linux protected Root+User watchdog acceptance, Saeid native live Start→Stop under independent recovery, real injected-fault recovery/reboot, broader network validation and release signoff. Preserve sole Brain, no speculative promotion. +**Latest verified position — 2026-10-16 R626:** Public **v1.5.4 Stable** remains unchanged, Draft PR #60 **NOT merged/released**. Exact PRIVATE RC1 (Inno SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`) is physically installed on BOTH Emad Windows and Saeid Windows, 681/681 SHA and data/one-install acceptance PASS. Emad Windows R603–R605 native Start→HTTPS/YouTube homepage→Stop→Recovery and original DNS/routes/Tailscale PASS. Saeid Windows R609–R613 installed, recoverable and SHA verified, but actual RC1 live Start/HTTPS/Stop remains **UNTESTED** following tool safety block of an all-in-one guard test R618; user-level safety tasks R616 ran and were removed with clean network. R622 direct YouTube Chrome with DPI OFF timed out and produced zero DOM on Saeid; R623 same-session no-DPI HTTPS baseline on BOTH Windows: Google204/GitHub200, YouTube 000/TCP connect 0/exit28, network/product unchanged. This is evidence of site-specific baseline failure, NOT RC1 failure on Saeid. **R626 on BOTH Windows RC1 registered protected Service Automatic/LocalSystem/owned SHA + failure-action + single Uninstaller configured PASS**, without physical reboot. On MMZ Linux existing Root v1.5.2/User v1.5.4, exact R568 27-file private RC1 **still staged not installed**; R623 original unchanged RootTransaction exact candidate synthetic Stage→Apply→Rollback, injected half-apply automatic rollback, and foreign-change fail-closed **3/3 PASS**, no actual host mutations. Native GNOME PolicyKit credential confirmation finally **verified as actual OS-approved** during R625 read-only Root `upgrade_guard preflight`, returned exit0 and precise required `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` JSON, while guard remained ABSENT and Tailscale ACTIVE. This **closes Root-authentication-readiness only**, NOT a protected actual upgrade/armed independent Root watchdog. Commander direct `run_project_command pkexec` had previously been denied by its program policy; do not infer blanket authority to bypass that route via arbitrary shells. Emad Linux continues `CONFLICT` (stored wlp2s0 vs live enp1s0 plus external VPN-class profile); no network correction applied. **FINAL still OPEN**: approved physical Linux protected Root+User watchdog acceptance, Saeid native live Start→Stop under independent recovery, real injected-fault recovery/reboot, broader network validation and release signoff. Preserve sole Brain, no speculative promotion. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. **R581 current-host Windows installation invariant & upstream dependency decision — 2026-10-16:** Independent read-only audits on `EMAD-PC-ULTIMAT` and `SAEID` found **exactly one** installed Direct Internet Method v1.5.4 registry registration (same Inno `AppId`), exactly one existing paired `uninsNNN.exe` / `uninsNNN.dat` and one running `DirectInternetMethodSvc` per machine. Emad's active uninstaller is `unins001` and Saeid's `unins000`; no extra uninstaller file was present at either installed location. **PASS physical single-installed-registration/uninstaller-discovery only**, not proof of a native in-place v1.5.5 update. No cleanup or service mutation warranted. On MMZ, `pkexec` binary, running `polkit.service` and GUI session/agent are present, but existing Remote Commander policy does not authorize invoking `pkexec`; **no authorization dialog requested/approved** and Root guard not armed. Current private RC1 embeds `ctrld v1.5.7`; upstream `v1.5.8` (published 2026-10-07) includes DNS64/DNSSEC and upstream-key log redaction improvements. Existing R534 physically tested official v1.5.8 on private loopback and cleaned it; **do not silently replace the SHA-pinned RC1 binary** before rebuilding/retesting dependent package and native acceptance. Upstream source: `https://github.com/Control-D-Inc/ctrld/releases/tag/v1.5.8`; official Polkit authority: `https://polkit.pages.freedesktop.org/polkit/pkexec.1.html`; Tailscale Linux stub compatibility: `https://tailscale.com/docs/reference/linux-dns`. +**R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. + **R622–R625 real host network comparison, native fake-root mutation contract, authenticated Linux privileged preflight — 2026-10-16:** Existing physical Saeid Windows v1.5.5-rc1 (engine OFF) headless Chrome 155 direct YouTube homepage produced **0 DOM bytes**, hit 21s timeout; user-local profile removed and network, manifest unchanged (R622 Commander `e9b06461-0b93-4f1d-ad37-3b0d7a68371f`, stdout SHA256 `593A2F1EA7F4E44A2021944CDB2865AB7EAAE0EB7ECD5551638964053E77ABA0`). Independent native curl `--noproxy '*'` `--http1.1` on BOTH Emad/Saeid Win v1.5.5-rc1 with engine OFF (R623) returned Google generate_204 **HTTP204**, GitHub **HTTP200**, while YouTube homepage and generate_204 **HTTP000**, curl **exit28**, about **3s timeout**, `time_connect=0.000`, `time_appconnect=0.000`, DNS lookup resolved quickly. Scope indicates failure before a completed TCP/TLS connection and must not be confused with an authenticated Chrome / DPI-ON acceptance; both host product/service/owned state SHA unchanged. Commander operations Saeid `d9ff8d2e-e29c-4eb9-b936-0e246fdb69b8` exit0 stdout SHA256 `41DC4C2810BEE7F630F8940090DD902FE92CFAEA09B5F771F3A57CAA841C7C44F`, Emad `98c95671-e2e7-403e-97e6-8f022afeab55` exit0 stdout SHA256 `8019A239801113FCD5081C18F67D7209C937BD8593941E8E7E1B0FBFBFA334E2`. Official Zapret Windows quickstart warns strategies are site/ISP-specific and to distinguish DNS, TLS and QUIC effects: https://github.com/bol-van/zapret/blob/master/docs/quick_start_windows.md ; Chromium's error list treats resets, DNS resolution failures, and unreachable addresses separately: https://chromium.googlesource.com/chromium/src/+/main/net/base/net_error_list.h . **Do not auto-change Saeid adapter DNS or pick an arbitrary desync strategy without a native guarded test.** On actual MMZ host, exact staged source `system/protected_upgrade_transaction.py` SHA256 `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` was imported unchanged into a **disposable user-owned fake protected filesystem**, seeded solely by copying and SHA-checking actual v1.5.2 pinned static protected files, two old managed targets and metadata as read-only source. Original `RootTransaction.stage/apply/rollback` **PASS** with hash-exact restoration; `apply(fail_after=2)` caused original automatic rollback restoring all bytes **PASS**; manually changed foreign target after Stage was rejected by both Apply and Rollback **fail-closed PASS**, preserving foreign bytes for review. **3/3 scoped synthetic protected transaction PASS** on physical Linux kernel, not root mutation; source/temp fake roots removed; actual Root/User/DNS/resolv symlink/default route/Tailscale identical. MMZ durable operation `ea5e01c0-2321-4392-beb7-758677caeee2` exit0 stdout SHA256 `47A8E05724E19C16AA1BC517C45DEC1B8F37FAAC18089EF2A01F8E18AC1D3CF2`. From 5d0c9da33a82f8f163034adce325303a9d954439 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:35:59 +0330 Subject: [PATCH 076/188] docs(brain): record R627-R629 exact live Linux protected RC1 install and independently verified rollback receipt --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index c71c5aac..feae6312 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R626:** Public **v1.5.4 Stable** remains unchanged, Draft PR #60 **NOT merged/released**. Exact PRIVATE RC1 (Inno SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`) is physically installed on BOTH Emad Windows and Saeid Windows, 681/681 SHA and data/one-install acceptance PASS. Emad Windows R603–R605 native Start→HTTPS/YouTube homepage→Stop→Recovery and original DNS/routes/Tailscale PASS. Saeid Windows R609–R613 installed, recoverable and SHA verified, but actual RC1 live Start/HTTPS/Stop remains **UNTESTED** following tool safety block of an all-in-one guard test R618; user-level safety tasks R616 ran and were removed with clean network. R622 direct YouTube Chrome with DPI OFF timed out and produced zero DOM on Saeid; R623 same-session no-DPI HTTPS baseline on BOTH Windows: Google204/GitHub200, YouTube 000/TCP connect 0/exit28, network/product unchanged. This is evidence of site-specific baseline failure, NOT RC1 failure on Saeid. **R626 on BOTH Windows RC1 registered protected Service Automatic/LocalSystem/owned SHA + failure-action + single Uninstaller configured PASS**, without physical reboot. On MMZ Linux existing Root v1.5.2/User v1.5.4, exact R568 27-file private RC1 **still staged not installed**; R623 original unchanged RootTransaction exact candidate synthetic Stage→Apply→Rollback, injected half-apply automatic rollback, and foreign-change fail-closed **3/3 PASS**, no actual host mutations. Native GNOME PolicyKit credential confirmation finally **verified as actual OS-approved** during R625 read-only Root `upgrade_guard preflight`, returned exit0 and precise required `SAFE_SOURCE_AND_INSTALLED_BASELINE_ONLY` JSON, while guard remained ABSENT and Tailscale ACTIVE. This **closes Root-authentication-readiness only**, NOT a protected actual upgrade/armed independent Root watchdog. Commander direct `run_project_command pkexec` had previously been denied by its program policy; do not infer blanket authority to bypass that route via arbitrary shells. Emad Linux continues `CONFLICT` (stored wlp2s0 vs live enp1s0 plus external VPN-class profile); no network correction applied. **FINAL still OPEN**: approved physical Linux protected Root+User watchdog acceptance, Saeid native live Start→Stop under independent recovery, real injected-fault recovery/reboot, broader network validation and release signoff. Preserve sole Brain, no speculative promotion. +**Latest verified position — 2026-10-16 R629:** Public **v1.5.4 Stable** remains unchanged, Draft PR #60 **NOT merged/released**. Exact PRIVATE RC1 (Inno SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`) is physically installed on BOTH Emad Windows and Saeid Windows, 681/681 SHA and data/one-install acceptance PASS. Emad Windows R603–R605 native Start→HTTPS/YouTube homepage→Stop→Recovery and original DNS/routes/Tailscale PASS. Saeid Windows R609–R613 installed, recoverable and SHA verified, but actual RC1 live Start/HTTPS/Stop remains **UNTESTED** following tool safety block of an all-in-one guard test R618; user-level safety tasks R616 ran and were removed with clean network. R622 direct YouTube Chrome with DPI OFF timed out and produced zero DOM on Saeid; R623 same-session no-DPI HTTPS baseline on BOTH Windows: Google204/GitHub200, YouTube 000/TCP connect 0/exit28, network/product unchanged. This is evidence of site-specific baseline failure, NOT RC1 failure on Saeid. **R626 on BOTH Windows RC1 registered protected Service Automatic/LocalSystem/owned SHA + failure-action + single Uninstaller configured PASS**, without physical reboot. MMZ Linux **R627 original exact RC1 user+protected Root actual in-place installation succeeded** with native root-owned independent systemd watchdog armed and both Root/User at 1.5.5-rc1 (engine OFF, network and Tailscale unmodified); **R628 intentionally invoked same native systemd service via OS-approved manager and restored Root v1.5.2/User v1.5.4 with metadata SHA byte-exact**, no DNS/route/Tailscale changes; **R629 authenticated exact Root watchdog receipt proves `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION` and Root/User rollback PASS**. Root-owned recovery guard and timer **remain armed solely pending evidence-authorized retirement**, deadline imminent. Permanent RC1 installation and live Linux Start/Stop remain UNPROVEN. Emad Linux continues `CONFLICT` (stored wlp2s0 vs live enp1s0 plus external VPN-class profile); no network correction applied. **FINAL still OPEN**: approved physical Linux protected Root+User watchdog acceptance, Saeid native live Start→Stop under independent recovery, real injected-fault recovery/reboot, broader network validation and release signoff. Preserve sole Brain, no speculative promotion. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R627–R629 MMZ exact real Root+User installed RC1, physical independent rollback & receipt — 2026-10-16:** Original 27-payload private R568 pinned RC1 `install.sh` executed from owner's local GNOME session under real manager-approved PolicyKit. First temporary wrapper `6f5fe62b-8e6a-4792-a067-582abde9b11f` failed *before launching Installer* due a quoted temporary output path; native Root/User/guard/net remained unchanged. Corrected wrapper `3f73d2e7-427f-4c32-bce8-c76767a0b8f9` captured **Installer EXIT0** and original application logs `PASS_USER_UPDATE_SINGLE_PENDING_SLOT_STAGED`, `ARMED_INDEPENDENT_ROOT_USER_WATCHDOG`, `R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED`, Root 4/4 Stage+Apply, and `PASS_INPLACE_MANAGED_CODE_APPLIED_NO_USER_DATA_MUTATION`. Installed Root & User both `1.5.5-rc1`, native systemd timer `active/enabled`, two owned rollback slots, engine OFF, resolver SHA and route/Tailscale unchanged. Wrapper alone returned exit1 because its nonprivileged postcondition tried to read Root-owned 0700 pending manifest; installer was successful and protected slot was deliberately inaccessible to ordinary user. During watchdog acceptance testing, instead of starting the engine, original systemd Root service `directinternetmethod-upgrade-rollback.service` was started through *native owner PolicyKit approval*, returning **systemd `Result=success`, `ExecMainStatus=0`**, Root/User versions restored `1.5.2/1.5.4` (R628 operation `606b87a0-40d4-4995-b517-857b9a728dc8`, stdout SHA256 `35EDD1D28FB7529F0D93A36CE867B94143AEE06570EE6ED EB73ECB31FEF4D623`; visual space nonnormative). Verified original Root meta SHA256 `77CE54A188048849AC010436DAD5BC3D06103A1DD59AB96A58D0FFD3CF499FB5`, User meta SHA256 `5215F59E6989C280B2C31D2EFE9FE3E2A030D1AFDD29E4D737EF9F4DAFDF924A`, resolver SHA `C5FA5ADA8F61AF5DF9EA638173EA9E8064B059290EE8E7238E4EBFA719AE689C`, Tailscale active; no live engine or route mutation. Authenticated `pkexec /usr/bin/cat` exact protected `rollback-result.json` readback R629 op `bd4b061d-868d-4b56-bac5-900cd8fe909c` SHA256 `88BFDF7B4163B67C6EA8AF1EE9894A6ABCF7F2B660B1FC7A086D08213DF6AEAB`, confirmed `revision=R552`, `status=PASS_AUTONOMOUS_TWO_SIDED_RESTORATION`, `rootRollback=PASS`, `userRollback=PASS`, `recoveryAttempted=true`, `networkMutationAttempted=false`, duration 0.128s; **Root-owned receipt SHA256 `43AFA4DC6443CF04C64D7EB6A2314B3FAAE7EA5653768D4F34227F329AEA58BE`**. **Scoped acceptance PASS original physical protected+User in-place transaction and real independent root systemd rollback.** This Brain upsert exports evidence before separate owner-authorized bounded `retire-rolledback`; keep public Stable and PR Draft pending actual Linux application Start/Stop and cross-host FINAL. Root-owned guard/timer/slot **must NOT be silently deleted** before receipt-matched, owner-approved native retirement. + **R622–R625 real host network comparison, native fake-root mutation contract, authenticated Linux privileged preflight — 2026-10-16:** Existing physical Saeid Windows v1.5.5-rc1 (engine OFF) headless Chrome 155 direct YouTube homepage produced **0 DOM bytes**, hit 21s timeout; user-local profile removed and network, manifest unchanged (R622 Commander `e9b06461-0b93-4f1d-ad37-3b0d7a68371f`, stdout SHA256 `593A2F1EA7F4E44A2021944CDB2865AB7EAAE0EB7ECD5551638964053E77ABA0`). Independent native curl `--noproxy '*'` `--http1.1` on BOTH Emad/Saeid Win v1.5.5-rc1 with engine OFF (R623) returned Google generate_204 **HTTP204**, GitHub **HTTP200**, while YouTube homepage and generate_204 **HTTP000**, curl **exit28**, about **3s timeout**, `time_connect=0.000`, `time_appconnect=0.000`, DNS lookup resolved quickly. Scope indicates failure before a completed TCP/TLS connection and must not be confused with an authenticated Chrome / DPI-ON acceptance; both host product/service/owned state SHA unchanged. Commander operations Saeid `d9ff8d2e-e29c-4eb9-b936-0e246fdb69b8` exit0 stdout SHA256 `41DC4C2810BEE7F630F8940090DD902FE92CFAEA09B5F771F3A57CAA841C7C44F`, Emad `98c95671-e2e7-403e-97e6-8f022afeab55` exit0 stdout SHA256 `8019A239801113FCD5081C18F67D7209C937BD8593941E8E7E1B0FBFBFA334E2`. Official Zapret Windows quickstart warns strategies are site/ISP-specific and to distinguish DNS, TLS and QUIC effects: https://github.com/bol-van/zapret/blob/master/docs/quick_start_windows.md ; Chromium's error list treats resets, DNS resolution failures, and unreachable addresses separately: https://chromium.googlesource.com/chromium/src/+/main/net/base/net_error_list.h . **Do not auto-change Saeid adapter DNS or pick an arbitrary desync strategy without a native guarded test.** On actual MMZ host, exact staged source `system/protected_upgrade_transaction.py` SHA256 `EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B` was imported unchanged into a **disposable user-owned fake protected filesystem**, seeded solely by copying and SHA-checking actual v1.5.2 pinned static protected files, two old managed targets and metadata as read-only source. Original `RootTransaction.stage/apply/rollback` **PASS** with hash-exact restoration; `apply(fail_after=2)` caused original automatic rollback restoring all bytes **PASS**; manually changed foreign target after Stage was rejected by both Apply and Rollback **fail-closed PASS**, preserving foreign bytes for review. **3/3 scoped synthetic protected transaction PASS** on physical Linux kernel, not root mutation; source/temp fake roots removed; actual Root/User/DNS/resolv symlink/default route/Tailscale identical. MMZ durable operation `ea5e01c0-2321-4392-beb7-758677caeee2` exit0 stdout SHA256 `47A8E05724E19C16AA1BC517C45DEC1B8F37FAAC18089EF2A01F8E18AC1D3CF2`. From d21cb3291fff2bc613b6afc40b72a02498c1e608 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:52:08 +0330 Subject: [PATCH 077/188] docs(brain): R630 restored terminal MMZ and R632 Saeid guarded native start with YouTube TLS failure --- PROJECT_BRAIN.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index feae6312..f86f794c 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R629:** Public **v1.5.4 Stable** remains unchanged, Draft PR #60 **NOT merged/released**. Exact PRIVATE RC1 (Inno SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`) is physically installed on BOTH Emad Windows and Saeid Windows, 681/681 SHA and data/one-install acceptance PASS. Emad Windows R603–R605 native Start→HTTPS/YouTube homepage→Stop→Recovery and original DNS/routes/Tailscale PASS. Saeid Windows R609–R613 installed, recoverable and SHA verified, but actual RC1 live Start/HTTPS/Stop remains **UNTESTED** following tool safety block of an all-in-one guard test R618; user-level safety tasks R616 ran and were removed with clean network. R622 direct YouTube Chrome with DPI OFF timed out and produced zero DOM on Saeid; R623 same-session no-DPI HTTPS baseline on BOTH Windows: Google204/GitHub200, YouTube 000/TCP connect 0/exit28, network/product unchanged. This is evidence of site-specific baseline failure, NOT RC1 failure on Saeid. **R626 on BOTH Windows RC1 registered protected Service Automatic/LocalSystem/owned SHA + failure-action + single Uninstaller configured PASS**, without physical reboot. MMZ Linux **R627 original exact RC1 user+protected Root actual in-place installation succeeded** with native root-owned independent systemd watchdog armed and both Root/User at 1.5.5-rc1 (engine OFF, network and Tailscale unmodified); **R628 intentionally invoked same native systemd service via OS-approved manager and restored Root v1.5.2/User v1.5.4 with metadata SHA byte-exact**, no DNS/route/Tailscale changes; **R629 authenticated exact Root watchdog receipt proves `PASS_AUTONOMOUS_TWO_SIDED_RESTORATION` and Root/User rollback PASS**. Root-owned recovery guard and timer **remain armed solely pending evidence-authorized retirement**, deadline imminent. Permanent RC1 installation and live Linux Start/Stop remain UNPROVEN. Emad Linux continues `CONFLICT` (stored wlp2s0 vs live enp1s0 plus external VPN-class profile); no network correction applied. **FINAL still OPEN**: approved physical Linux protected Root+User watchdog acceptance, Saeid native live Start→Stop under independent recovery, real injected-fault recovery/reboot, broader network validation and release signoff. Preserve sole Brain, no speculative promotion. +**Latest verified position — 2026-10-16 R632:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Two existing supported targeted scopes/profile strategies remain to assess without corrupting user data. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,10 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R630–R632 real-host guard retirement and Saeid TLS diagnosis — 2026-10-16:** After accepted dual-scope Root rollback R629 and its unique receipt SHA256 `43AFA4DC6443CF04C64D7EB6A2314B3FAAE7EA5653768D4F34227F329AEA58BE` was entered in the single GitHub Brain, a new physical MMZ readback found `/var/lib/directinternetmethod/upgrade-guard` and `upgrade-pending`, user pending, `/etc/systemd/system/directinternetmethod-upgrade-rollback.{service,timer}` all **ABSENT**, systemd timer **inactive/not-found**, exact installed original Root v1.5.2 metadata SHA256 `77CE54A188048849AC010436DAD5BC3D06103A1DD59AB96A58D0FFD3CF499FB5` and User v1.5.4 SHA256 `5215F59E6989C280B2C31D2EFE9FE3E2A030D1AFDD29E4D737EF9F4DAFDF924A` unchanged, `/etc/resolv.conf` checksum `C5FA5ADA8F61AF5DF9EA638173EA9E8064B059290EE8E7238E4EBFA719AE689C` unchanged, original route/Tailscale active, pinned source all 27/27 SHA256 validated, original `upgrade_guard.py preflight` PASS. Root-owned `/var/lib/directinternetmethod/upgrade.lock` still exists as neutral serialization file, **not** an armed watchdog, no manual deletion. Evidence is **physical terminal state PASS only; retirement process/actor not independently identified this turn** and must not be invented. + +On actual Saeid Win32 v1.5.5-rc1, corrected native R631 OS TaskScheduler user-token `DIM-R631-OwnerStop` and `DIM-R631-OwnerRecovery` `sc.exe control DirectInternetMethodSvc 129/130` one-time independent fallback tasks were created and verified 234/293 s future, operation `5d436dfc-88e4-4210-84ca-09ac199ac6bd` SUCCESS stdout SHA256 `82FFA153857771AEAFF51B40EA44ED72CCFDC5EAB17D587936A94DA9FF627979`. Initial R631 harness had a PowerShell `Test-Path ... -or` typo and had not started the engine; corrected run fully PASS. R632 bounded user-local native Service control **128 Start done0**; while DPI ON, HTTPS probes **Google204, GitHub200, OpenAI401 (expected auth challenge), YouTube HTTP000/curl exit35 (TLS failed)**. Chrome 155 in disposable no-proxy profile exited0 **with zero DOM bytes and no title**: site did **not** load. Original Service control **129 Stop done0**, no Recovery needed; owned state/processes zero, exact prior IPv4 DNS, NRPT and default routes restored; task scheduler safety guards verified removed; temp browser files removed. Do not interpret its returned summary label `PASS_R632_SAEID_NATIVE_RC1_START_WEB_STOP_RESTORED` as a full Web PASS: independent acceptance **PASS scoped to Start/Stop and recovery safety, FAIL for YouTube reachability**. Durable op `49c5cad0-92a3-4d97-a8cd-8fe2c369b73d` SUCCESS stdout SHA256 `C6C9937A2A944116AFCE7B65530B42A2FEAB991C866C481C2611A0F57EA335C0`. Native source `windows/app/Start-Direct.ps1` has supported profiles `balanced` (default), `compatibility`, `strong`, and scopes `targeted` or `all-sites`; on Saeid `strategy.txt` and `scope.txt` **do not exist** (thus default balanced/targeted), while on Emad both exist with explicit balanced/targeted. Official `zapret` documents the risk of TLS fake/disorder desync and network-specific profile selection: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md . Investigate a bounded owner-configured alternative only with independent OS Stop/Recovery, byte-exact temporary data restoration and no system-wide DNS changes; do not declare one universal DPI fix. + **R627–R629 MMZ exact real Root+User installed RC1, physical independent rollback & receipt — 2026-10-16:** Original 27-payload private R568 pinned RC1 `install.sh` executed from owner's local GNOME session under real manager-approved PolicyKit. First temporary wrapper `6f5fe62b-8e6a-4792-a067-582abde9b11f` failed *before launching Installer* due a quoted temporary output path; native Root/User/guard/net remained unchanged. Corrected wrapper `3f73d2e7-427f-4c32-bce8-c76767a0b8f9` captured **Installer EXIT0** and original application logs `PASS_USER_UPDATE_SINGLE_PENDING_SLOT_STAGED`, `ARMED_INDEPENDENT_ROOT_USER_WATCHDOG`, `R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED`, Root 4/4 Stage+Apply, and `PASS_INPLACE_MANAGED_CODE_APPLIED_NO_USER_DATA_MUTATION`. Installed Root & User both `1.5.5-rc1`, native systemd timer `active/enabled`, two owned rollback slots, engine OFF, resolver SHA and route/Tailscale unchanged. Wrapper alone returned exit1 because its nonprivileged postcondition tried to read Root-owned 0700 pending manifest; installer was successful and protected slot was deliberately inaccessible to ordinary user. During watchdog acceptance testing, instead of starting the engine, original systemd Root service `directinternetmethod-upgrade-rollback.service` was started through *native owner PolicyKit approval*, returning **systemd `Result=success`, `ExecMainStatus=0`**, Root/User versions restored `1.5.2/1.5.4` (R628 operation `606b87a0-40d4-4995-b517-857b9a728dc8`, stdout SHA256 `35EDD1D28FB7529F0D93A36CE867B94143AEE06570EE6ED EB73ECB31FEF4D623`; visual space nonnormative). Verified original Root meta SHA256 `77CE54A188048849AC010436DAD5BC3D06103A1DD59AB96A58D0FFD3CF499FB5`, User meta SHA256 `5215F59E6989C280B2C31D2EFE9FE3E2A030D1AFDD29E4D737EF9F4DAFDF924A`, resolver SHA `C5FA5ADA8F61AF5DF9EA638173EA9E8064B059290EE8E7238E4EBFA719AE689C`, Tailscale active; no live engine or route mutation. Authenticated `pkexec /usr/bin/cat` exact protected `rollback-result.json` readback R629 op `bd4b061d-868d-4b56-bac5-900cd8fe909c` SHA256 `88BFDF7B4163B67C6EA8AF1EE9894A6ABCF7F2B660B1FC7A086D08213DF6AEAB`, confirmed `revision=R552`, `status=PASS_AUTONOMOUS_TWO_SIDED_RESTORATION`, `rootRollback=PASS`, `userRollback=PASS`, `recoveryAttempted=true`, `networkMutationAttempted=false`, duration 0.128s; **Root-owned receipt SHA256 `43AFA4DC6443CF04C64D7EB6A2314B3FAAE7EA5653768D4F34227F329AEA58BE`**. **Scoped acceptance PASS original physical protected+User in-place transaction and real independent root systemd rollback.** This Brain upsert exports evidence before separate owner-authorized bounded `retire-rolledback`; keep public Stable and PR Draft pending actual Linux application Start/Stop and cross-host FINAL. Root-owned guard/timer/slot **must NOT be silently deleted** before receipt-matched, owner-approved native retirement. **R622–R625 real host network comparison, native fake-root mutation contract, authenticated Linux privileged preflight — 2026-10-16:** Existing physical Saeid Windows v1.5.5-rc1 (engine OFF) headless Chrome 155 direct YouTube homepage produced **0 DOM bytes**, hit 21s timeout; user-local profile removed and network, manifest unchanged (R622 Commander `e9b06461-0b93-4f1d-ad37-3b0d7a68371f`, stdout SHA256 `593A2F1EA7F4E44A2021944CDB2865AB7EAAE0EB7ECD5551638964053E77ABA0`). Independent native curl `--noproxy '*'` `--http1.1` on BOTH Emad/Saeid Win v1.5.5-rc1 with engine OFF (R623) returned Google generate_204 **HTTP204**, GitHub **HTTP200**, while YouTube homepage and generate_204 **HTTP000**, curl **exit28**, about **3s timeout**, `time_connect=0.000`, `time_appconnect=0.000`, DNS lookup resolved quickly. Scope indicates failure before a completed TCP/TLS connection and must not be confused with an authenticated Chrome / DPI-ON acceptance; both host product/service/owned state SHA unchanged. Commander operations Saeid `d9ff8d2e-e29c-4eb9-b936-0e246fdb69b8` exit0 stdout SHA256 `41DC4C2810BEE7F630F8940090DD902FE92CFAEA09B5F771F3A57CAA841C7C44F`, Emad `98c95671-e2e7-403e-97e6-8f022afeab55` exit0 stdout SHA256 `8019A239801113FCD5081C18F67D7209C937BD8593941E8E7E1B0FBFBFA334E2`. Official Zapret Windows quickstart warns strategies are site/ISP-specific and to distinguish DNS, TLS and QUIC effects: https://github.com/bol-van/zapret/blob/master/docs/quick_start_windows.md ; Chromium's error list treats resets, DNS resolution failures, and unreachable addresses separately: https://chromium.googlesource.com/chromium/src/+/main/net/base/net_error_list.h . **Do not auto-change Saeid adapter DNS or pick an arbitrary desync strategy without a native guarded test.** From 632ea0b222c854c44db21cf68663406108f5b61f Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:21:11 +0330 Subject: [PATCH 078/188] docs(brain): R633 delayed guard reconciliation and R635-R638 Saeid compatibility/strong native V&V --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index f86f794c..2bad058e 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R632:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Two existing supported targeted scopes/profile strategies remain to assess without corrupting user data. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. +**Latest verified position — 2026-10-16 R638:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Saeid's two other built-in targeted strategies `compatibility` and `strong` have now been physically tested under owner-local OS three-task Stop/Recovery/profile restoration; both preserved all host networking but YouTube still failed at TLS (curl35/Chrome 0 DOM), whereas Google/GitHub/OpenAI remained reachable. All temporary profile files/tasks were removed. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,12 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R633–R638 Saeid delayed helper and isolated compatibility strong profiles — 2026-10-16:** A previous R633 long-running asynchronous task had returned `UNCERTAIN` with no final receipt. Reconciliation first observed no changes but another bounded observation **minutes later** found a late actual `strategy.txt=compatibility` (13 bytes SHA256 `FA528D3ADEF61D199A8348FFB48EF0143603EA2E0AD007121C39EDC6E3AEB567`) and three authentic user-only OS ScheduledTask safeguards R633 Stop/Recovery/ProfileRemove. A guarded separate R634 attempt correctly **failed before mutation** with `R634_EXISTING_STRATEGY_REFUSED`. R633 Stop and Recovery later actually fired with native Service action `done/0`, engine OFF, all owned tasks were removed, strategy file removed and personal configuration returned to previous absent state. **Lesson:** an `UNCERTAIN` tool receipt can precede a later side effect; never replay it blindly, reconcile host and task state at a *later* time as well. + +R635 on real SAEID installed `v1.5.5-rc1` staged only a temporary user-owned `compatibility` profile and exactly three independently scheduled user-token Stop, Recovery and profile-deletion tasks 359/419/478 s future, verifying owner, expected exact `sc.exe` actions and file SHA, no system-DNS modification. Command `f6fe6cbb-f43b-425c-96c9-e8d43bc84e27` SUCCESS stdout SHA256 `C86782CA758495C394BD5D7E26A5613303DB6CCC5CB03E54F50D701578D05E99`. Actual R636 bounded installed-Service code128 Start `done/0` under compatibility/targeted, no-proxy HTTPS returned **YouTube HTTP000 curl exit35 (TCP connect .083s, TLS 0s)**, Google204, GitHub200, OpenAI401. Chrome 155 isolated YouTube exit0 but **0 DOM/no title**. Same host script guaranteed original code129 Stop `done/0`; exact original routes/adapter DNS/NRPT, no DPI-owned state or process, user strategy absent and three tasks removed, clean profile, service Running. Result `R636_COMPATIBILITY_YOUTUBE_STILL_BLOCKED_HOST_RESTORED`; operation `0c0f3ece-8f97-481a-abfd-bd1a04030932` SUCCESS stdout SHA256 `EC3F8C695FB243DFD9584997FF9DECAB417EB593BF06930A1A64E8F27FD62083`. + +R637 staged supported targeted `strong` only (6 bytes, SHA256 `EDA15CE4834AC303532BC6973DF5FF8A5DEB75D7DFCEB9FC2CDF1BB450A01CF5`), same three exact user-owned one-shot OS failsafes 359/418/478s future; operation `a7cb1f36-fd63-4748-b755-6710dc2b7c95` SUCCESS stdout SHA256 `786749828CFD081F73F050A8ABB F2BC44C20D3CD4D034A9301FB52201E9CFDB4` (the canonical raw receipt, not spaced typography, is reference). Real native R638 Service Start `done/0`, **YouTube HTTP000 curl35, TCP connected .095s, TLS never completed**, Chrome 0 DOM/no title; Google204, GitHub200, OpenAI401. Native Stop `done/0` removed DPI resources, original DNS/route/NRPT identical; Strong file, three OS safeguards and temporary Chrome profile removed. Result `R638_STRONG_YOUTUBE_BLOCKED_HOST_RESTORED`; operation `c54a7bad-49d7-4dc7-a659-429b9fd6059e` SUCCESS stdout SHA256 `69C17997C9A1E4BE6534970D1372A4C089DB79C571733C2FD0D9F0553DC96021`. **PASS scope:** true native Start/Stop and exact recovery under both built-in profiles, no private-data loss. **FAIL scope:** YouTube on Saeid; none of Balanced/Compatibility/Strong targeted profiles are accepted solutions. Do not change Windows DNS, route, or all-sites scope as an undocumented global workaround. Need targeted design/diagnostic for Saeid-specific TLS block with live guarded V&V; upstream Zapret docs describe possible fake/split incompatibilities, not universal fixes: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md . + **R630–R632 real-host guard retirement and Saeid TLS diagnosis — 2026-10-16:** After accepted dual-scope Root rollback R629 and its unique receipt SHA256 `43AFA4DC6443CF04C64D7EB6A2314B3FAAE7EA5653768D4F34227F329AEA58BE` was entered in the single GitHub Brain, a new physical MMZ readback found `/var/lib/directinternetmethod/upgrade-guard` and `upgrade-pending`, user pending, `/etc/systemd/system/directinternetmethod-upgrade-rollback.{service,timer}` all **ABSENT**, systemd timer **inactive/not-found**, exact installed original Root v1.5.2 metadata SHA256 `77CE54A188048849AC010436DAD5BC3D06103A1DD59AB96A58D0FFD3CF499FB5` and User v1.5.4 SHA256 `5215F59E6989C280B2C31D2EFE9FE3E2A030D1AFDD29E4D737EF9F4DAFDF924A` unchanged, `/etc/resolv.conf` checksum `C5FA5ADA8F61AF5DF9EA638173EA9E8064B059290EE8E7238E4EBFA719AE689C` unchanged, original route/Tailscale active, pinned source all 27/27 SHA256 validated, original `upgrade_guard.py preflight` PASS. Root-owned `/var/lib/directinternetmethod/upgrade.lock` still exists as neutral serialization file, **not** an armed watchdog, no manual deletion. Evidence is **physical terminal state PASS only; retirement process/actor not independently identified this turn** and must not be invented. On actual Saeid Win32 v1.5.5-rc1, corrected native R631 OS TaskScheduler user-token `DIM-R631-OwnerStop` and `DIM-R631-OwnerRecovery` `sc.exe control DirectInternetMethodSvc 129/130` one-time independent fallback tasks were created and verified 234/293 s future, operation `5d436dfc-88e4-4210-84ca-09ac199ac6bd` SUCCESS stdout SHA256 `82FFA153857771AEAFF51B40EA44ED72CCFDC5EAB17D587936A94DA9FF627979`. Initial R631 harness had a PowerShell `Test-Path ... -or` typo and had not started the engine; corrected run fully PASS. R632 bounded user-local native Service control **128 Start done0**; while DPI ON, HTTPS probes **Google204, GitHub200, OpenAI401 (expected auth challenge), YouTube HTTP000/curl exit35 (TLS failed)**. Chrome 155 in disposable no-proxy profile exited0 **with zero DOM bytes and no title**: site did **not** load. Original Service control **129 Stop done0**, no Recovery needed; owned state/processes zero, exact prior IPv4 DNS, NRPT and default routes restored; task scheduler safety guards verified removed; temp browser files removed. Do not interpret its returned summary label `PASS_R632_SAEID_NATIVE_RC1_START_WEB_STOP_RESTORED` as a full Web PASS: independent acceptance **PASS scoped to Start/Stop and recovery safety, FAIL for YouTube reachability**. Durable op `49c5cad0-92a3-4d97-a8cd-8fe2c369b73d` SUCCESS stdout SHA256 `C6C9937A2A944116AFCE7B65530B42A2FEAB991C866C481C2611A0F57EA335C0`. Native source `windows/app/Start-Direct.ps1` has supported profiles `balanced` (default), `compatibility`, `strong`, and scopes `targeted` or `all-sites`; on Saeid `strategy.txt` and `scope.txt` **do not exist** (thus default balanced/targeted), while on Emad both exist with explicit balanced/targeted. Official `zapret` documents the risk of TLS fake/disorder desync and network-specific profile selection: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md . Investigate a bounded owner-configured alternative only with independent OS Stop/Recovery, byte-exact temporary data restoration and no system-wide DNS changes; do not declare one universal DPI fix. From 1ca45c262a3bde762dffe6c45c8097f9fab19920 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:31:00 +0330 Subject: [PATCH 079/188] docs(brain): R639-R642 site-SNI, forced QUIC and IPv6 physical V&V --- PROJECT_BRAIN.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 2bad058e..90a0a797 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R638:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Saeid's two other built-in targeted strategies `compatibility` and `strong` have now been physically tested under owner-local OS three-task Stop/Recovery/profile restoration; both preserved all host networking but YouTube still failed at TLS (curl35/Chrome 0 DOM), whereas Google/GitHub/OpenAI remained reachable. All temporary profile files/tasks were removed. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. +**Latest verified position — 2026-10-16 R642:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Saeid's two other built-in targeted strategies `compatibility` and `strong` have now been physically tested under owner-local OS three-task Stop/Recovery/profile restoration; both preserved all host networking but YouTube still failed at TLS (curl35/Chrome 0 DOM), whereas Google/GitHub/OpenAI remained reachable. All temporary profile files/tasks were removed. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. **R639–R642 both-Windows physical DPI-OFF network diagnostics:** Saeid public DNS/TCP-to-YouTube succeeds but TLS fails regardless of TLS1.2 or TLS1.3; same fixed Google IPv4 with Google SNI works while YouTube SNI fails, direct Chrome QUIC sent 15 packets with zero received; no usable IPv6 default route. These are scoped route/protocol findings, not product YouTube PASS; local OS and installed application unchanged. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,10 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R639–R642 physical SNI QUIC IPv6 differential:** On both actual Windows machines running installed v1.5.5-rc1 with owned DPI OFF, read-only no-proxy native curl/DNS diagnostic recorded distinct site blocks. **R639 Saeid:** DNS A for `www.youtube.com` resolves public IPv4, TCP connect ~0.08–0.20s but YouTube `generate_204`, mobile and youtubei API HTTP000 curl35 before TLS completion under TLS1.2 and TLS1.3; Google/GitHub reachable. **R639 Emad:** same YouTube domains return DNS A `10.10.34.35` (nonpublic), original TCP connect times out (curl28). Saeid op `b062836e-9185-499a-8ef1-c6b2c790dc8c` SHA256 `67C6DC9CE136DB019278713015B22177B159B326DF82EBADCF987868D2BBAD4`; Emad `bb05de50-d38f-4ff9-8ffe-3500f8c8ac96` SHA256 `FE56FF06A74562DAB83E2659015EB4CEEB40F55C50D5C3B2A02C1569B94B7A55`. + +**R640 Saeid exact-destination control:** `curl --resolve` with normal TLS validation and **the SAME endpoint IPv4 `142.251.155.4`**: `www.google.com` SNI gave HTTP204, while `www.youtube.com` SNI HTTP000/curl35; alternate YouTube `142.251.157.4` also failed. Strong evidence for SNI/site-sensitive path blocking, not attribution to a particular device. Op `0d031c67-f6ca-4849-9e85-70c9401bce17` SHA256 `BDD829FB36CDD16002E388EF909AF33DD0AB36EC0C4EA03EAADD2131ED804F47`. First QUIC harness had a PowerShell syntax error before acceptance, no retained temp files. **R641 corrected Saeid Chrome 155** forced QUIC to YouTube with disposable profile, **15 QUIC packets sent, 0 received, 167 QUIC events, zero DOM/title, `ERR_QUIC_PROTOCOL_ERROR`**; op `4fc16867-c6cf-409b-a685-422921ee2679` SHA256 `9CC9B422DBBF22EA9F520B6A46744736BD25E82A90E2BC38F453A4180CB75240`. **R642 IPv6:** both hosts had *no* IPv6 default route; direct no-proxy IPv6 YouTube/Google curl exit6 before TCP, despite AAAA entries `2001:4188:2:600:10:10:34:35` (Emad), `...:36` (Saeid) for YouTube. Saeid op `e9f775b8-d396-47ee-a67e-c2de4e635ec8` SHA256 `8C4EF58CB8B16AC29804EA555016261F030100BDB392067D84DAF4C6CA5E8E61`; Emad `3c7da3d6-ba92-4cd4-9b38-c47a9b74d97a` SHA256 `B48A70C1DDDA03C79C4436E6BA28F7A3AB04857F0FF141431620A1F219A1EA4F`. All tests only user-private temporary browser/network reads, no published code or OS DNS/routes changes. **PASS diagnostics, FAIL site reachability on Saeid remains OPEN**. Source mechanics and caveats: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md ; do not relax TLS verification, infer universal ISP fix or silently enable all-sites/alter DNS. + **R633–R638 Saeid delayed helper and isolated compatibility strong profiles — 2026-10-16:** A previous R633 long-running asynchronous task had returned `UNCERTAIN` with no final receipt. Reconciliation first observed no changes but another bounded observation **minutes later** found a late actual `strategy.txt=compatibility` (13 bytes SHA256 `FA528D3ADEF61D199A8348FFB48EF0143603EA2E0AD007121C39EDC6E3AEB567`) and three authentic user-only OS ScheduledTask safeguards R633 Stop/Recovery/ProfileRemove. A guarded separate R634 attempt correctly **failed before mutation** with `R634_EXISTING_STRATEGY_REFUSED`. R633 Stop and Recovery later actually fired with native Service action `done/0`, engine OFF, all owned tasks were removed, strategy file removed and personal configuration returned to previous absent state. **Lesson:** an `UNCERTAIN` tool receipt can precede a later side effect; never replay it blindly, reconcile host and task state at a *later* time as well. R635 on real SAEID installed `v1.5.5-rc1` staged only a temporary user-owned `compatibility` profile and exactly three independently scheduled user-token Stop, Recovery and profile-deletion tasks 359/419/478 s future, verifying owner, expected exact `sc.exe` actions and file SHA, no system-DNS modification. Command `f6fe6cbb-f43b-425c-96c9-e8d43bc84e27` SUCCESS stdout SHA256 `C86782CA758495C394BD5D7E26A5613303DB6CCC5CB03E54F50D701578D05E99`. Actual R636 bounded installed-Service code128 Start `done/0` under compatibility/targeted, no-proxy HTTPS returned **YouTube HTTP000 curl exit35 (TCP connect .083s, TLS 0s)**, Google204, GitHub200, OpenAI401. Chrome 155 isolated YouTube exit0 but **0 DOM/no title**. Same host script guaranteed original code129 Stop `done/0`; exact original routes/adapter DNS/NRPT, no DPI-owned state or process, user strategy absent and three tasks removed, clean profile, service Running. Result `R636_COMPATIBILITY_YOUTUBE_STILL_BLOCKED_HOST_RESTORED`; operation `0c0f3ece-8f97-481a-abfd-bd1a04030932` SUCCESS stdout SHA256 `EC3F8C695FB243DFD9584997FF9DECAB417EB593BF06930A1A64E8F27FD62083`. From 59275d7f85d4ba601a2707c2e33e113a1a84e041 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:38:02 +0330 Subject: [PATCH 080/188] docs(brain): R643-R645 Saeid native TLS/ClientHello segmentation negative V&V --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 90a0a797..bd7661b1 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R642:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Saeid's two other built-in targeted strategies `compatibility` and `strong` have now been physically tested under owner-local OS three-task Stop/Recovery/profile restoration; both preserved all host networking but YouTube still failed at TLS (curl35/Chrome 0 DOM), whereas Google/GitHub/OpenAI remained reachable. All temporary profile files/tasks were removed. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. **R639–R642 both-Windows physical DPI-OFF network diagnostics:** Saeid public DNS/TCP-to-YouTube succeeds but TLS fails regardless of TLS1.2 or TLS1.3; same fixed Google IPv4 with Google SNI works while YouTube SNI fails, direct Chrome QUIC sent 15 packets with zero received; no usable IPv6 default route. These are scoped route/protocol findings, not product YouTube PASS; local OS and installed application unchanged. +**Latest verified position — 2026-10-16 R645:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Saeid's two other built-in targeted strategies `compatibility` and `strong` have now been physically tested under owner-local OS three-task Stop/Recovery/profile restoration; both preserved all host networking but YouTube still failed at TLS (curl35/Chrome 0 DOM), whereas Google/GitHub/OpenAI remained reachable. All temporary profile files/tasks were removed. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. **R639–R642 both-Windows physical DPI-OFF network diagnostics:** Saeid public DNS/TCP-to-YouTube succeeds but TLS fails regardless of TLS1.2 or TLS1.3; same fixed Google IPv4 with Google SNI works while YouTube SNI fails, direct Chrome QUIC sent 15 packets with zero received; no usable IPv6 default route. These are scoped route/protocol findings, not product YouTube PASS; local OS and installed application unchanged. R643–R645 on SAEID DPI-OFF additionally establish normal .NET HttpClient Youtube timeout and a corrected default-validating SSL ClientHello splitting test: two real SNI TCP split locations remained reset while Google TLS succeeded on the same IP; no working targeted YouTube path discovered. These methods are NOT solutions. **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,8 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R643–R645 Windows TLS fragmentation read-only diagnostic — 2026-10-16:** On actual Saeid v1.5.5-rc1 Windows with DPI OFF, `Invoke-WebRequest -NoProxy` .NET HttpClient with normal certificate verification reached Google HTTP204 in 0.27s, YouTube timed out 9s (`TaskCanceledException`), thus failure is not restricted to curl fingerprint; native operation `fea1111d-f231-47fc-9c41-dca5f8141593`, stdout SHA256 `2124D8C32C23E37A1D0826BD5B0BA6C3558DB32467A889CFFC1830B36C2EFF29`. Initial R644 ephemeral C# stream harness failed before a valid TLS test because it omitted Stream Timeout properties; no product/host mutation and not PASS. Corrected R645 pure user-local .NET standard-library `SslStream` default certificate validation to one pinned public IPv4 endpoint `142.251.155.4:443` verified ClientHello actual SNI `www.youtube.com` byte offset splits **1** and **7** into two separate TCP writes 275ms apart (`SplitApplied=True`), but both YouTube sessions still reset (`IOException: existing connection forcibly closed by remote host`) after ~370ms; unsplit YouTube similarly reset ~83ms, while `www.google.com` SNI on the **same destination IP and unchanged cert validation** negotiated and returned HTTP204 within 284ms. Exact durable op `f528856d-a7c1-4330-865a-700bccb39c75` SUCCESS, stdout SHA256 `EDB25452CB32B1F19DAF9D3BBD37D817DEAFA10D82473E6F27DAAB36A4A4EED5`. This **rejects naive SNI TCP segmentation as a working Saeid fix** and supports targeted SNI-sensitive blocking, though it cannot assign the reset to any named ISP device. In-memory experiments, no installed binaries/DNS/routes/driver edits, no TLS validation disabling. **Scoped diagnostic PASS; Saeid YouTube still FAIL.** Primary mechanics: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md . + **R639–R642 physical SNI QUIC IPv6 differential:** On both actual Windows machines running installed v1.5.5-rc1 with owned DPI OFF, read-only no-proxy native curl/DNS diagnostic recorded distinct site blocks. **R639 Saeid:** DNS A for `www.youtube.com` resolves public IPv4, TCP connect ~0.08–0.20s but YouTube `generate_204`, mobile and youtubei API HTTP000 curl35 before TLS completion under TLS1.2 and TLS1.3; Google/GitHub reachable. **R639 Emad:** same YouTube domains return DNS A `10.10.34.35` (nonpublic), original TCP connect times out (curl28). Saeid op `b062836e-9185-499a-8ef1-c6b2c790dc8c` SHA256 `67C6DC9CE136DB019278713015B22177B159B326DF82EBADCF987868D2BBAD4`; Emad `bb05de50-d38f-4ff9-8ffe-3500f8c8ac96` SHA256 `FE56FF06A74562DAB83E2659015EB4CEEB40F55C50D5C3B2A02C1569B94B7A55`. **R640 Saeid exact-destination control:** `curl --resolve` with normal TLS validation and **the SAME endpoint IPv4 `142.251.155.4`**: `www.google.com` SNI gave HTTP204, while `www.youtube.com` SNI HTTP000/curl35; alternate YouTube `142.251.157.4` also failed. Strong evidence for SNI/site-sensitive path blocking, not attribution to a particular device. Op `0d031c67-f6ca-4849-9e85-70c9401bce17` SHA256 `BDD829FB36CDD16002E388EF909AF33DD0AB36EC0C4EA03EAADD2131ED804F47`. First QUIC harness had a PowerShell syntax error before acceptance, no retained temp files. **R641 corrected Saeid Chrome 155** forced QUIC to YouTube with disposable profile, **15 QUIC packets sent, 0 received, 167 QUIC events, zero DOM/title, `ERR_QUIC_PROTOCOL_ERROR`**; op `4fc16867-c6cf-409b-a685-422921ee2679` SHA256 `9CC9B422DBBF22EA9F520B6A46744736BD25E82A90E2BC38F453A4180CB75240`. **R642 IPv6:** both hosts had *no* IPv6 default route; direct no-proxy IPv6 YouTube/Google curl exit6 before TCP, despite AAAA entries `2001:4188:2:600:10:10:34:35` (Emad), `...:36` (Saeid) for YouTube. Saeid op `e9f775b8-d396-47ee-a67e-c2de4e635ec8` SHA256 `8C4EF58CB8B16AC29804EA555016261F030100BDB392067D84DAF4C6CA5E8E61`; Emad `3c7da3d6-ba92-4cd4-9b38-c47a9b74d97a` SHA256 `B48A70C1DDDA03C79C4436E6BA28F7A3AB04857F0FF141431620A1F219A1EA4F`. All tests only user-private temporary browser/network reads, no published code or OS DNS/routes changes. **PASS diagnostics, FAIL site reachability on Saeid remains OPEN**. Source mechanics and caveats: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md ; do not relax TLS verification, infer universal ISP fix or silently enable all-sites/alter DNS. From 828742d2568e12997864e56d572fdba9a12ac4cc Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:53:44 +0330 Subject: [PATCH 081/188] fix(gui): show partial health when active core HTTPS checks fail --- windows/gui/MainWindow.xaml.cs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index b62ba164..9cb40958 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -308,6 +308,8 @@ async Task RefreshStatusAsync() bool ownedHealthy = r.TryGetProperty("ownedResourcesHealthy", out var oh) && oh.GetBoolean(); YoutubeText.Text = OpenAiText.Text = GithubText.Text = GeminiText.Text = mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy"; AdultSiteText.Text = adultCheckEnabled ? (mode == "CONFLICT" ? "Suspended by external tunnel" : ownedHealthy ? "Checking…" : "Available when Direct Method is healthy") : "Disabled"; + // Engine ownership is not proof that target sites are reachable. + bool coreReachabilityProven = false; if (ownedHealthy && mode == "ACTIVE" && !string.IsNullOrWhiteSpace(IpText.Text) && IpText.Text != "—") { var ytTask = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "200", "204" }); @@ -326,9 +328,13 @@ async Task RefreshStatusAsync() var tasks = new List { ytTask, oaTask, ghTask, geminiTask }; if (adultTasks is not null) tasks.AddRange(adultTasks); await Task.WhenAll(tasks); - YoutubeText.Text = FmtHttp(await ytTask); - OpenAiText.Text = FmtOpenAiApiProbe(await oaTask); - GithubText.Text = FmtHttp(await ghTask); + var youtubeProbe = await ytTask; + var openAiProbe = await oaTask; + var githubProbe = await ghTask; + coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok; + YoutubeText.Text = FmtHttp(youtubeProbe); + OpenAiText.Text = FmtOpenAiApiProbe(openAiProbe); + GithubText.Text = FmtHttp(githubProbe); GeminiText.Text = FmtHttp(await geminiTask); AdultSiteText.Text = adultTasks is null ? "Disabled" : FmtAdult(await Task.WhenAll(adultTasks)); } @@ -342,6 +348,16 @@ async Task RefreshStatusAsync() "OFF" => "Ready", _ => "Unknown state" }; + if (mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)) + { + // Stay ACTIVE internally so Stop works; never claim full health + // just because two of three startup endpoints passed quorum. + StatusBadge.Text = "PARTIAL"; + StatusBadge.Foreground = warn; + HealthText.Text = ownedHealthy + ? "Direct path active · at least one core site unreachable" + : "Direct path active · owned resource validation failed"; + } if (!string.IsNullOrWhiteSpace(reason)) ToolTip = reason; StartButton.IsEnabled = mode == "OFF"; From 9533b62aaf1ade5ff56972baeb3b52c74be3e3f3 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:54:21 +0330 Subject: [PATCH 082/188] test(gui): require honest PARTIAL health for failed core reachability probes --- tests/windows_contract_audit.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 1e9d8e47..ec1b7733 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -82,7 +82,7 @@ def idx(s,x): return s.find(x) check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","httpsHealth=[ordered]@{required=2")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) check("ui_openai_403_cannot_be_chatgpt_login_pass", - 'FmtOpenAiApiProbe(await oaTask)' in gui + 'FmtOpenAiApiProbe(openAiProbe)' in gui and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" })' in gui and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401", "403" })' not in gui and '"403" when p.Reached => "API HTTP 403 forbidden · login unverified"' in gui @@ -91,6 +91,19 @@ def idx(s,x): return s.find(x) and '"API transport unavailable · login unverified"' in gui) check("ui_live_probe_semantics", "FAIL · HTTP" in gui and 'https://gemini.google.com/' in gui and "adultTasks is not null" in gui and "full pages" in gui) +check("ui_partial_active_core_reachability", all(x in gui for x in ( + "bool coreReachabilityProven = false;", + "var youtubeProbe = await ytTask;", + "var openAiProbe = await oaTask;", + "var githubProbe = await ghTask;", + "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;", + 'mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)', + 'StatusBadge.Text = "PARTIAL";', + "StatusBadge.Foreground = warn;", + "at least one core site unreachable", + 'StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED";', +))) + check("ui_direct_update_service_handoff", 'ServiceClient.Send("update")' in gui and 'ServiceClient.Send("recovery")' in gui and '"handoff"' in gui and "300000" in gui and "DownloadVerifiedAsync" not in gui and "LaunchInstaller" not in gui) check("ui_update_advisory_release_digest", "InstallerDigest" in updater and "digest" in updater and "SHA256SUMS.txt" in updater and "DownloadVerifiedAsync" not in updater and "Process.Start" not in updater) From 0e7a009d23d03429d362102aec82c837f9218fa4 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:17:22 +0330 Subject: [PATCH 083/188] docs(brain): R646-R650 honest GUI health, native CI, Saeid UAC field canary, cleanup --- PROJECT_BRAIN.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index bd7661b1..1fe20655 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R645:** Official **v1.5.4 Stable** public, PR #60 Draft UNMERGED. PRIVATE RC1 SHA-pinned Installer `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` installed Emad and Saeid Windows, 681/681 actual managed SHA and in-place user settings preservation PASS. Emad native Start→Google/YouTube HTTPS+Chrome homepage→Stop/Recovery with original DNS/route/Tailscale PASS. Saeid R632 real Start `done/0`, native Stop `done/0`, user OS recovery tasks registered/validated/disarmed, exact DNS/default route/NRPT and owned resources returned to baseline; Google204/GitHub200/OpenAI401 but YouTube **HTTP000 curl exit35 and Chrome 0 bytes** while DIM active. Thus native Saeid **engine/lifecycle safety PASS** but **site reachability FAIL** (R623 no-DPI also YouTube exit28 pre-TCP timeout). Saeid's two other built-in targeted strategies `compatibility` and `strong` have now been physically tested under owner-local OS three-task Stop/Recovery/profile restoration; both preserved all host networking but YouTube still failed at TLS (curl35/Chrome 0 DOM), whereas Google/GitHub/OpenAI remained reachable. All temporary profile files/tasks were removed. MMZ Linux real protected Root+User RC1 in-place install and original independent root-owned systemd dual rollback R627-R629 PASS, after deliberately restored original Root v1.5.2/User v1.5.4 SHA. Current MMZ R630 original protected `upgrade-guard`/slots/systemd temporary units **ABSENT** (retirement terminal state verified but actor/operation receipt not attributed by this turn), timer not-found/inactive, unprivileged source preflight PASS; harmless preexisting `/var/lib/directinternetmethod/upgrade.lock` retained and not manually deleted. MMZ exact 27 member staged RC1 remains ready, Tailscale/DNS/route preserved. Emad Linux v1.5.4 still CONFLICT (stored wlp2s0 vs live enp1s0 & VPN). Project FINAL OPEN: Saeid YouTube and longer playback test, actual MMZ Linux Start/Stop/DoH with consent, boot persistence/recovery, Emad Linux, release signoff. **DO NOT MERGE OR PUBLISH** on the basis of narrower V&V. **R639–R642 both-Windows physical DPI-OFF network diagnostics:** Saeid public DNS/TCP-to-YouTube succeeds but TLS fails regardless of TLS1.2 or TLS1.3; same fixed Google IPv4 with Google SNI works while YouTube SNI fails, direct Chrome QUIC sent 15 packets with zero received; no usable IPv6 default route. These are scoped route/protocol findings, not product YouTube PASS; local OS and installed application unchanged. R643–R645 on SAEID DPI-OFF additionally establish normal .NET HttpClient Youtube timeout and a corrected default-validating SSL ClientHello splitting test: two real SNI TCP split locations remained reset while Google TLS succeeded on the same IP; no working targeted YouTube path discovered. These methods are NOT solutions. +**Latest verified position — 2026-10-16 R650 (R651 UI harness INCONCLUSIVE):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. Emad Windows remains on earlier private RC1 SHA `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`, actual Start→YouTube→Stop/Recovery previously PASS. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 offscreen WPF reflection-harness proof NOT ACCEPTED**, despite installed SHA/compilation PASS; no desktop focus or installed mutation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, installed GUI live visual PARTIAL behavior, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,16 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R646–R651 GUI partial-health correction, private tested Windows canary and owner task cleanup — 2026-10-16:** Newly discovered **false-positive Windows UI health**: the exact pinned Saeid Start evidence `%ProgramFiles%/DirectInternetMethod/Privileged/evidence/runtime-latest.json` was `status=PASS_ACTIVE`, `httpsHealth.required=2, passed=2` from GitHub/OpenAI, while `live.youtube.ok=false`, curl35 reset with no HTTPS and Chrome zero DOM. Emad had `PASS_ACTIVE`, `httpsHealth.passed=3` and real YouTube204. GUI `windows/gui/MainWindow.xaml.cs` had unconditionally displayed green ACTIVE and `Direct path healthy` whenever resource ownership/mode were ACTIVE, overriding its own failed YouTube site test. Chosen safe **R646 GUI-only corrective change** preserves 2-of-3 engine quorum/availability and native Stop behavior while preventing a false full-health display: `coreReachabilityProven` requires ownership plus completed YouTube/OpenAI/GitHub live probe `Ok`; otherwise `StatusBadge=PARTIAL` warning and `HealthText` explicitly says one or more core sites unreachable. No TLS certificate bypass, no new DPI engine, no all-sites/DNS/driver changes. Source commit `828742d2568e12997864e56d572fdba9a12ac4cc`, scoped regression guard in existing `tests/windows_contract_audit.py` commit `9533b62aaf1ade5ff56972baeb3b52c74be3e3f3`; verified **physical Emad native .NET 10.0.402 GUI & Service Release builds 0 errors/0 warnings** op `59591bd4-b4e1-4760-8f4d-1ddc00c4beb3` stdout SHA256 `60B30B64BFCCFD1C601FAADF8EB84859AF2F0F69B703AA628330C1727881CFCC`; no installation mutation during compilation. + +**GitHub current-head CI acceptance:** [PR run 38939448874](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38939448874) success; [push run 38939438225](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38939438225) **14 jobs succeeded, 1 explicitly skipped**, including exact Windows hosted v1.5.4→new private v1.5.5-rc1→v1.5.4 original native single-install/private-data preservation PASS; tested Inno SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`, **154,315,328 bytes**, artifact `r600-private-tested-rc1-38939438225` ID `12062368621`. Native manifest SHA256 `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`; GUI SHA256 `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`; protected Service SHA `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8` unchanged. + +**R647 actual Saeid stale guard cleanup:** read-only found three **expired but still registered user-owned** R633 one-shot stop/recovery/strategy-clear tasks with LastTaskResult=0, NextRun blank and temporary compatibility file already absent. Exact owner/command/encoded cleanup SHA/path reverified; native non-admin `Unregister-ScheduledTask` removed exactly these three with postcondition absent; service remains Running, engine OFF. Op `a19537d7-4135-4ec5-b631-feea441e5db0` SUCCESS stdout SHA256 `A19C7D111677BC71F2A978713E6AE5A9D031D8BE9F0169A589AD4D06C2BF8098`; no protected SYSTEM task touched. + +**R648–R650 actual Saeid private canary:** New GitHub tested artifact was downloaded only through authenticated short-lived connector handoff and verified exact one-member zip payload, SHA/size/unsigned policy, to user-local `%LOCALAPPDATA%/DirectInternetMethod-Private-QA/candidate-R646`; old exact RC1 Installer SHA `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and public Stable recovery Installer SHA `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` retained. R648 op `6368c569-9b7c-4d43-800b-05f55037511e` SUCCESS stdout SHA `CE363049695FF593B7EDB5F9FC71320C9A4DEE545339F2E617CF2D4EBBB44F1F`. First R649 helper call failed a **PowerShell `Test-Path ... -or` syntax typo before Installer/UAC** (no mutation); exact state reconciled. Corrected R649 **Windows native owner-approved UAC** installed tested same-AppId GUI candidate **in-place, exit0**, native registry still 1.5.5-rc1, exact new GUI/Service/Manifest SHA as above, all private UserConfig file hashes/provider SHA unchanged, native default route, adapter DNS, NRPT untouched, Service Running, engine OFF, **exact one Inno Uninstaller exe/dat pair**; recovery files still present. Native op `b8fd930d-680a-425d-8763-7a6ddbf60cba` SUCCESS stdout SHA `319BDCD550B3BD8319982DCF1AE299123983AFD5766D1663E4A76D088D091318`. Independent R650 on-disk installed all **681/681 managed file bytes and SHA, 481,273,295 bytes**, no missing/reparse/drift and zero discrepancies; op `6055b31f-edaa-49f0-ba00-8724f752f5bb` SUCCESS stdout SHA256 `797369E106FCB5583AAF7C0B8BFEAD31D1F7256C34A63D7B827C209777F3A153`. + +**R651 evidence limitation:** A disposable offscreen WPF harness intended to instantiate `MainWindow` without desktop focus and call real status probes first failed GUI compilation due sparse-checkout excluding original `windows/app/DirectInternetMethod.ico`, then with that input corrected built WPF GUI 0 warnings/errors but reflection instantiation returned `TargetInvocationException` without an inner stack in the test wrapper. Both scope-owned temporary worktrees cleaned and installed Saeid RC1 unchanged. **DO NOT mark live WPF visual PARTIAL test PASS**; compilation + source contract + installed SHA accepted only, physical visual classification still OPEN. A newly staged source must not be presented as already published, and any later same-tag update must compare actual hashes, not only version strings. + **R643–R645 Windows TLS fragmentation read-only diagnostic — 2026-10-16:** On actual Saeid v1.5.5-rc1 Windows with DPI OFF, `Invoke-WebRequest -NoProxy` .NET HttpClient with normal certificate verification reached Google HTTP204 in 0.27s, YouTube timed out 9s (`TaskCanceledException`), thus failure is not restricted to curl fingerprint; native operation `fea1111d-f231-47fc-9c41-dca5f8141593`, stdout SHA256 `2124D8C32C23E37A1D0826BD5B0BA6C3558DB32467A889CFFC1830B36C2EFF29`. Initial R644 ephemeral C# stream harness failed before a valid TLS test because it omitted Stream Timeout properties; no product/host mutation and not PASS. Corrected R645 pure user-local .NET standard-library `SslStream` default certificate validation to one pinned public IPv4 endpoint `142.251.155.4:443` verified ClientHello actual SNI `www.youtube.com` byte offset splits **1** and **7** into two separate TCP writes 275ms apart (`SplitApplied=True`), but both YouTube sessions still reset (`IOException: existing connection forcibly closed by remote host`) after ~370ms; unsplit YouTube similarly reset ~83ms, while `www.google.com` SNI on the **same destination IP and unchanged cert validation** negotiated and returned HTTP204 within 284ms. Exact durable op `f528856d-a7c1-4330-865a-700bccb39c75` SUCCESS, stdout SHA256 `EDB25452CB32B1F19DAF9D3BBD37D817DEAFA10D82473E6F27DAAB36A4A4EED5`. This **rejects naive SNI TCP segmentation as a working Saeid fix** and supports targeted SNI-sensitive blocking, though it cannot assign the reset to any named ISP device. In-memory experiments, no installed binaries/DNS/routes/driver edits, no TLS validation disabling. **Scoped diagnostic PASS; Saeid YouTube still FAIL.** Primary mechanics: https://github.com/bol-van/zapret/blob/master/docs/readme.en.md . **R639–R642 physical SNI QUIC IPv6 differential:** On both actual Windows machines running installed v1.5.5-rc1 with owned DPI OFF, read-only no-proxy native curl/DNS diagnostic recorded distinct site blocks. **R639 Saeid:** DNS A for `www.youtube.com` resolves public IPv4, TCP connect ~0.08–0.20s but YouTube `generate_204`, mobile and youtubei API HTTP000 curl35 before TLS completion under TLS1.2 and TLS1.3; Google/GitHub reachable. **R639 Emad:** same YouTube domains return DNS A `10.10.34.35` (nonpublic), original TCP connect times out (curl28). Saeid op `b062836e-9185-499a-8ef1-c6b2c790dc8c` SHA256 `67C6DC9CE136DB019278713015B22177B159B326DF82EBADCF987868D2BBAD4`; Emad `bb05de50-d38f-4ff9-8ffe-3500f8c8ac96` SHA256 `FE56FF06A74562DAB83E2659015EB4CEEB40F55C50D5C3B2A02C1569B94B7A55`. From ddcac393e011c58e07c307f129626476ad445001 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:30:26 +0330 Subject: [PATCH 084/188] docs(brain): R652-R655 real hidden WPF OFF and simulated ACTIVE PARTIAL with native HTTPS --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 1fe20655..3c30a10a 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R650 (R651 UI harness INCONCLUSIVE):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. Emad Windows remains on earlier private RC1 SHA `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`, actual Start→YouTube→Stop/Recovery previously PASS. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 offscreen WPF reflection-harness proof NOT ACCEPTED**, despite installed SHA/compilation PASS; no desktop focus or installed mutation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, installed GUI live visual PARTIAL behavior, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** +**Latest verified position — 2026-10-16 R655 (real WPF hidden OFF and simulated ACTIVE/PARTIAL V&V PASS):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. Emad Windows remains on earlier private RC1 SHA `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`, actual Start→YouTube→Stop/Recovery previously PASS. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,12 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R652–R655 WPF hidden actual-control execution and simulated-ACTIVE partial status — 2026-10-16:** Actual Saeid Windows running the SHA-pinned installed GUI candidate `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C` and schema2 manifest `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669` retained original `v1.5.5-rc1` and native Service Running, engine OFF throughout. A prior temporary WPF reflection test R651 was INCONCLUSIVE, not a product crash. R652 isolated source build revealed that attempting to set WPF static `Application.ResourceAssembly` after first initialization throws `InvalidOperationException`; R653 captured the *actual* original nested UI error, `XamlParseException -> Cannot locate resource 'directinternetmethod.ico'`, because harness entry assembly lacked its WPF resource. Real app's `windows/gui/MainWindow.xaml` uses relative `Icon="/DirectInternetMethod.ico"`, and project embeds that icon. **Do not edit installed GUI or WPF resources based on this test harness failure**. All these earlier temporary workspaces were scope-owned and removed. + +**R654 actual hidden WPF OFF test:** Corrected disposable .NET 10.0.402 WPF harness bundles the authentic pinned source `windows/app/DirectInternetMethod.ico` as harness entry-assembly WPF Resource, loads the exact-source compiled `MainWindow`, never shows/focuses a window, injects installed app source path for the unchanged production `Status.ps1` backend and invokes the **real** private `RefreshStatusAsync` on its dispatcher. Native host WPF controls returned `StatusBadge=OFF`, `HealthText=Ready`, `StopButton.IsEnabled=false` and `IsVisible=false`, **PASS**. No installation/settings/network change; exact durable Commander op `cbddbd86-bac2-4220-ba11-3c60c64fc78b` SUCCEEDED, stdout SHA256 `bbd29b2b87364094d53a97a6b3b6040fc5a400d7ba18f825b91d296facfa601c`; temporary C# source/build/icon removed. + +**R655 actual hidden WPF PARTIAL behavior with synthetic active status + *real* Saeid HTTPS:** The same corrected original WPF code was recompiled from pinned PR head `0e7a009d23d03429d362102aec82c837f9218fa4` into a uniquely owned disposable harness with correctly embedded original icon; ONLY the **Status.ps1 backend** was replaced with a temporary private directory script returning an explicitly **synthetic** `mode=ACTIVE`, `ownedResourcesHealthy=true` and the host's measured actual IPv4 interface. This simulated active service status, while GUI `ProbeAsync` still performed the original *real network* curl HTTPS requests without proxy; ordinary user preference `adultCheckEnabled=false` only in test object's memory avoided unrelated live category probes. Native real WPF controls returned `StatusBadge=PARTIAL`, `HealthText="Direct path active · at least one core site unreachable"`, `YoutubeText="FAIL · transport"`, `GithubText="PASS · HTTP 200"`, `OpenAiText="API HTTP 401 auth required · login unverified"`, `StopButton.IsEnabled=true`, `window.IsVisible=false`, **PASS**. The original actual installed engine remained **OFF**; adapter DNS/default route, installed manifest SHA and service unchanged, temp synthetic backend/UI files all deleted. Exact durable op `1a4d6e68-d281-48ac-91c4-1f2893e48123` SUCCEEDED stdout SHA256 `5f73bd695f8da734994cdaf775e89e02fd83e607a25107950abeeb60dc26166e`. **Acceptance scope:** true WPF presentation and control logic for a simulated ACTIVE state plus actual site failure, NOT a real engine-active WPF integration, authenticated YouTube/video access, or network solution. A separate attempt R655 to invoke real live Start+WPF+Stop with task-based safety guard was explicitly BLOCKED by the host tool's safety checks before execution; do not repackage the same prohibited mutation to evade tool policy; physical Start/Stop safety remains established by R632/R636/R638, but the new GUI's *live-mode* behavior is not claimed. + **R646–R651 GUI partial-health correction, private tested Windows canary and owner task cleanup — 2026-10-16:** Newly discovered **false-positive Windows UI health**: the exact pinned Saeid Start evidence `%ProgramFiles%/DirectInternetMethod/Privileged/evidence/runtime-latest.json` was `status=PASS_ACTIVE`, `httpsHealth.required=2, passed=2` from GitHub/OpenAI, while `live.youtube.ok=false`, curl35 reset with no HTTPS and Chrome zero DOM. Emad had `PASS_ACTIVE`, `httpsHealth.passed=3` and real YouTube204. GUI `windows/gui/MainWindow.xaml.cs` had unconditionally displayed green ACTIVE and `Direct path healthy` whenever resource ownership/mode were ACTIVE, overriding its own failed YouTube site test. Chosen safe **R646 GUI-only corrective change** preserves 2-of-3 engine quorum/availability and native Stop behavior while preventing a false full-health display: `coreReachabilityProven` requires ownership plus completed YouTube/OpenAI/GitHub live probe `Ok`; otherwise `StatusBadge=PARTIAL` warning and `HealthText` explicitly says one or more core sites unreachable. No TLS certificate bypass, no new DPI engine, no all-sites/DNS/driver changes. Source commit `828742d2568e12997864e56d572fdba9a12ac4cc`, scoped regression guard in existing `tests/windows_contract_audit.py` commit `9533b62aaf1ade5ff56972baeb3b52c74be3e3f3`; verified **physical Emad native .NET 10.0.402 GUI & Service Release builds 0 errors/0 warnings** op `59591bd4-b4e1-4760-8f4d-1ddc00c4beb3` stdout SHA256 `60B30B64BFCCFD1C601FAADF8EB84859AF2F0F69B703AA628330C1727881CFCC`; no installation mutation during compilation. **GitHub current-head CI acceptance:** [PR run 38939448874](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38939448874) success; [push run 38939438225](https://github.com/GOD13emad/DirectInternetMethod/actions/runs/38939438225) **14 jobs succeeded, 1 explicitly skipped**, including exact Windows hosted v1.5.4→new private v1.5.5-rc1→v1.5.4 original native single-install/private-data preservation PASS; tested Inno SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`, **154,315,328 bytes**, artifact `r600-private-tested-rc1-38939438225` ID `12062368621`. Native manifest SHA256 `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`; GUI SHA256 `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`; protected Service SHA `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8` unchanged. From 21be87a7be266e20aaaf513ed92578acaf8c5955 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:37:47 +0330 Subject: [PATCH 085/188] docs(brain): R656-R658 owner-approved same corrected GUI and 681 files on both Windows --- PROJECT_BRAIN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 3c30a10a..aad0e48f 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R655 (real WPF hidden OFF and simulated ACTIVE/PARTIAL V&V PASS):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. Emad Windows remains on earlier private RC1 SHA `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9`, actual Start→YouTube→Stop/Recovery previously PASS. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** +**Latest verified position — 2026-10-16 R658 (both Windows same updated GUI, 681/681 native PASS):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. **R656–R658 Emad Windows now ALSO owner-UAC upgraded with the exact same tested R646 GUI Installer SHA `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`; 681/681 installed hashes PASS, original five-file config and provider, DNS/routes/NRPT and Tailscale preserved, one Uninstaller. Both Windows now have the same private corrected RC1 GUI payload.** Prior Emad Start→YouTube→Stop/Recovery PASS was obtained using the previous GUI before R657, not after. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. @@ -10,6 +10,12 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. +**R656–R658 two physical Windows exact corrected GUI acceptance — 2026-10-16:** Actual EMAD Windows had private v1.5.5-rc1 before upgrade, original Manifest `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`, core Service Running, DPI OFF, Tailscale Running, five UserConfig files (1,510,513 bytes) with no symlinks. Original private RC1 Installer SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D8C70F5B07860FB9` and official Stable `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` were owner-local retained. Authenticated GitHub CI-run `38939438225` private Artifact ID `12062368621` (same accepted artifact used for Saeid) was downloaded into a separate local `candidate-R646` directory and verified 154,315,328 bytes SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` with NotSigned status; source product not changed during staging. R656 Emad Commander op `affd8b3e-e46d-4107-a7bf-500e4b08b7c3` succeeded, stdout SHA256 `cccb3e6c205df3b7a70b58b59de9ae1146e5793c6ad8b033d7e4528679a5ea8f`. + +**R657 physical Windows UAC and actual SAME-AppId in-place GUI update:** Owner-local, entry-by-entry SHA-validated backup of original five private UserConfig files made first, backup ZIP SHA256 `65E2EBEAF2B9145285807019BC87C79A8A6D0E6A7C020027C9FD6CC3C7DDE181`. Windows owner UAC RunAs approved, unchanged Inno installer `/SILENT /NORESTART` exit0; registry remained one `v1.5.5-rc1` installation with exactly one matched Inno uninstaller pair. Installed **new GUI** SHA256 `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, **new Manifest** `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, original Service binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`; all 5 original config file hashes and providers unchanged, network route/DNS/NRPT exact identity, Tailscale and core Service Running, DPI OFF, previous RC1 and Stable recovery installers retained. R657 op `b346abc2-0dcc-4394-86fb-1a58a3d1f389` succeeded stdout SHA256 `bed1bd8f2252bb66ed934ba0d2efcaf05ab0806620c28a9c19eb3846be6a81fd`. + +**R658 independent physical on-disk SHA:** Actual Emad Win32 full installed two-scope current RC1 Manifest **681/681 SHA256+file-size exact**, 481,273,295 bytes, no symlinks/duplicates/path escapes, Tailscale/Service Running and engine OFF; op `25f4a17a-d588-48e2-904f-6d26fd329e5c` succeeded stdout SHA256 `b95c0a9d888a88ce00fdf084a4f4035b2c93d442d0661990f98cc3d092ad5130`. Independent Saeid readback saw the **same corrected GUI and Manifest hashes**, Service Running and engine OFF. **Scoped PASS both Windows now use one identical *private* UI-corrected RC1 candidate**. No actual RC1 graphical UI with active engine was exercised on this new build (R655 synthetic-status WPF PASS only); network YouTube remains blocked on Saeid, and public Stable v1.5.4 and PR Draft unchanged. Do not claim FINAL or promote. + **R652–R655 WPF hidden actual-control execution and simulated-ACTIVE partial status — 2026-10-16:** Actual Saeid Windows running the SHA-pinned installed GUI candidate `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C` and schema2 manifest `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669` retained original `v1.5.5-rc1` and native Service Running, engine OFF throughout. A prior temporary WPF reflection test R651 was INCONCLUSIVE, not a product crash. R652 isolated source build revealed that attempting to set WPF static `Application.ResourceAssembly` after first initialization throws `InvalidOperationException`; R653 captured the *actual* original nested UI error, `XamlParseException -> Cannot locate resource 'directinternetmethod.ico'`, because harness entry assembly lacked its WPF resource. Real app's `windows/gui/MainWindow.xaml` uses relative `Icon="/DirectInternetMethod.ico"`, and project embeds that icon. **Do not edit installed GUI or WPF resources based on this test harness failure**. All these earlier temporary workspaces were scope-owned and removed. **R654 actual hidden WPF OFF test:** Corrected disposable .NET 10.0.402 WPF harness bundles the authentic pinned source `windows/app/DirectInternetMethod.ico` as harness entry-assembly WPF Resource, loads the exact-source compiled `MainWindow`, never shows/focuses a window, injects installed app source path for the unchanged production `Status.ps1` backend and invokes the **real** private `RefreshStatusAsync` on its dispatcher. Native host WPF controls returned `StatusBadge=OFF`, `HealthText=Ready`, `StopButton.IsEnabled=false` and `IsVisible=false`, **PASS**. No installation/settings/network change; exact durable Commander op `cbddbd86-bac2-4220-ba11-3c60c64fc78b` SUCCEEDED, stdout SHA256 `bbd29b2b87364094d53a97a6b3b6040fc5a400d7ba18f825b91d296facfa601c`; temporary C# source/build/icon removed. From d0f5749173443753977a78c74420a751513823ec Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:38:14 +0330 Subject: [PATCH 086/188] docs(brain): correct R656 old RC1 recovery SHA in unique evidence delta --- PROJECT_BRAIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index aad0e48f..58847bcb 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -10,7 +10,7 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official t **R626 two physical Windows RC1 cold-boot service registration configuration — 2026-10-16:** Independently read only Windows **HKLM SYSTEM CurrentControlSet Services/DirectInternetMethodSvc** and matching same-AppId Uninstall registration after real installed v1.5.5-rc1 on both `EMAD-PC-ULTIMAT` and `SAEID`. Win32_Service CIM under non-admin token returned `StartMode=Unknown` and no path/account even though service Running (access-limited API, **not** failed auto-start); a narrower authorized registry read confirmed **Start=2 (Automatic), ObjectName=LocalSystem, Type=16 own-process, registered failure recovery actions present**, executable path precisely `%ProgramFiles%/DirectInternetMethod/Privileged/app/DirectInternetMethod.Service.exe`, on-disk binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`, each machine one matched Inno `uninsNNN.exe/.dat` pair, current Service `Running`, owned DPI state OFF. No Windows registry, service, route or data changed. **PASS scoped to physical host cold-boot SERVICE CONFIGURATION READINESS only**; no OS reboot, browser video playback, or WinDivert driver persistence was run. Both native Read-only Commander probes returned exact `PASS_R626_RC1_WINDOWS_NATIVE_COLD_BOOT_SERVICE_CONFIGURATION`. Do NOT conflate a registered Automatic service with a confirmed successful post-reboot Start/Stop user acceptance. Reference for Windows Service `Start=2` / `ObjectName`: Windows SCM service registry values, and actual native host readback; source installer in same PR. -**R656–R658 two physical Windows exact corrected GUI acceptance — 2026-10-16:** Actual EMAD Windows had private v1.5.5-rc1 before upgrade, original Manifest `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`, core Service Running, DPI OFF, Tailscale Running, five UserConfig files (1,510,513 bytes) with no symlinks. Original private RC1 Installer SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D8C70F5B07860FB9` and official Stable `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` were owner-local retained. Authenticated GitHub CI-run `38939438225` private Artifact ID `12062368621` (same accepted artifact used for Saeid) was downloaded into a separate local `candidate-R646` directory and verified 154,315,328 bytes SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` with NotSigned status; source product not changed during staging. R656 Emad Commander op `affd8b3e-e46d-4107-a7bf-500e4b08b7c3` succeeded, stdout SHA256 `cccb3e6c205df3b7a70b58b59de9ae1146e5793c6ad8b033d7e4528679a5ea8f`. +**R656–R658 two physical Windows exact corrected GUI acceptance — 2026-10-16:** Actual EMAD Windows had private v1.5.5-rc1 before upgrade, original Manifest `BA6AC9EAEE7E47E50778EBAF107A3F8BF2590F6196B71E01CE72E75EBE899297`, core Service Running, DPI OFF, Tailscale Running, five UserConfig files (1,510,513 bytes) with no symlinks. Original private RC1 Installer SHA256 `F725B20216439094C9C15FE21E411322F4D831C3D4F293C3A8C70F5B07860FB9` and official Stable `FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133` were owner-local retained. Authenticated GitHub CI-run `38939438225` private Artifact ID `12062368621` (same accepted artifact used for Saeid) was downloaded into a separate local `candidate-R646` directory and verified 154,315,328 bytes SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` with NotSigned status; source product not changed during staging. R656 Emad Commander op `affd8b3e-e46d-4107-a7bf-500e4b08b7c3` succeeded, stdout SHA256 `cccb3e6c205df3b7a70b58b59de9ae1146e5793c6ad8b033d7e4528679a5ea8f`. **R657 physical Windows UAC and actual SAME-AppId in-place GUI update:** Owner-local, entry-by-entry SHA-validated backup of original five private UserConfig files made first, backup ZIP SHA256 `65E2EBEAF2B9145285807019BC87C79A8A6D0E6A7C020027C9FD6CC3C7DDE181`. Windows owner UAC RunAs approved, unchanged Inno installer `/SILENT /NORESTART` exit0; registry remained one `v1.5.5-rc1` installation with exactly one matched Inno uninstaller pair. Installed **new GUI** SHA256 `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, **new Manifest** `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, original Service binary SHA256 `DEBA242C223409072C1FB852077C0E4F00726A7F34F56ADF5C87BFE8197153A8`; all 5 original config file hashes and providers unchanged, network route/DNS/NRPT exact identity, Tailscale and core Service Running, DPI OFF, previous RC1 and Stable recovery installers retained. R657 op `b346abc2-0dcc-4394-86fb-1a58a3d1f389` succeeded stdout SHA256 `bed1bd8f2252bb66ed934ba0d2efcaf05ab0806620c28a9c19eb3846be6a81fd`. From 91f77f23341bd20c118dbe5c8f9d6ba979ed4091 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:13:15 +0330 Subject: [PATCH 087/188] fix(windows): distinguish active engine quorum from partial core-site reachability --- windows/app/Start-Direct.ps1 | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/windows/app/Start-Direct.ps1 b/windows/app/Start-Direct.ps1 index cd7686e8..860bca62 100644 --- a/windows/app/Start-Direct.ps1 +++ b/windows/app/Start-Direct.ps1 @@ -217,6 +217,21 @@ function Curl-Probe-Retry([string]$Url,[string]$LocalIp,[string]$AcceptPattern,[ $last=$runs[-1] [ordered]@{url=$Url;exit=$last.exit;meta=$last.meta;ok=$false;attempts=$Attempts;runs=$runs} } +function Get-CoreSiteHealth($yt,$oa,$gh) { + # A healthy engine/2-of-3 quorum does NOT prove all core sites accessible. + $failed=[Collections.Generic.List[string]]::new() + if(-not [bool]$yt.ok){[void]$failed.Add('youtube')} + if(-not [bool]$oa.ok){[void]$failed.Add('openai')} + if(-not [bool]$gh.ok){[void]$failed.Add('github')} + [ordered]@{ + required=2 + passed=3-$failed.Count + members=@('youtube','openai','github') + reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'} + allCoreSitesOk=($failed.Count -eq 0) + unreachableCoreSites=@($failed.ToArray()) + } +} function Rollback($s){ try{Remove-OwnedNrpt ([string]$s.nrptRuleName)}catch{} Clear-DnsClientCache -ErrorAction SilentlyContinue @@ -459,7 +474,8 @@ try{ $yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^(200|204)\|' 8 2 $oa=Curl-Probe-Retry 'https://api.openai.com/v1/models' $route.LocalIp '^(200|401)\|' 7 2 $gh=Curl-Probe-Retry 'https://github.com/' $route.LocalIp '^(200|301|302|303|307|308)\|' 7 2 - $httpsProbePassCount=@($yt,$oa,$gh|Where-Object{$_.ok}).Count + $httpsHealth=Get-CoreSiteHealth $yt $oa $gh + $httpsProbePassCount=[int]$httpsHealth.passed $routes=@(Get-NetRoute -AddressFamily IPv4 -ErrorAction SilentlyContinue|Where-Object{$_.DestinationPrefix -in @('0.0.0.0/1','128.0.0.0/1')}) $proxy=(netsh winhttp show proxy|Out-String).Trim() $icsLive=Get-Ics @@ -474,7 +490,7 @@ try{ Write-JsonAtomic $StatePath $state $ev.status='PASS_ACTIVE' $ev.live=[ordered]@{ - ctrldPid=$cpid;winwsPid=$wp.Id;directMethods=$DirectMethods;strategy=$strategy;scope=$scope;runtimeHostCount=$merged.Count;adultCoverage=$adultEnabled;adultHostCount=$adultHostCount;youtubeHttp80=$yh;dns=$dy;youtube=$yt;openai=$oa;github=$gh;httpsHealth=[ordered]@{required=2;passed=$httpsProbePassCount;members=@('youtube','openai','github')} + ctrldPid=$cpid;winwsPid=$wp.Id;directMethods=$DirectMethods;strategy=$strategy;scope=$scope;runtimeHostCount=$merged.Count;adultCoverage=$adultEnabled;adultHostCount=$adultHostCount;youtubeHttp80=$yh;dns=$dy;youtube=$yt;openai=$oa;github=$gh;httpsHealth=$httpsHealth ics=$icsLive;proxy=$proxy;broadRouteCount=$routes.Count drivers=@(Get-OwnedDrivers|Select-Object Name,State,PathName) } From 1f90cace8d3e06647f83d9f6d17a3e314de8caee Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:15:22 +0330 Subject: [PATCH 088/188] test(windows): assert structured PARTIAL core-site health without changing Start API --- tests/windows_contract_audit.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index ec1b7733..76429546 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -79,7 +79,17 @@ def idx(s,x): return s.find(x) check("ui_gemini_adult_live_checks", all(x in xaml for x in ('Text="Gemini"','Text="Adult coverage"','x:Name="AdultSiteCheckToggle"','x:Name="GeminiText"','x:Name="AdultSiteText"')) and all(x in gui for x in ("https://www.pornhub.com/","https://www.xvideos.com/","https://www.xnxx.com/","https://xhamster.com/","FmtAdult")) and "pornhub" not in xaml.lower()) check("ui_adult_check_default_off_persisted", all(x in gui for x in ("adultSiteLiveCheck","settings.json","AdultSiteCheckToggle_Changed")) and "bool adultCheckEnabled;" in gui) check("adult_catalog_opt_in", all(x in gui for x in ("adultCoverageEnabled","adult-enabled.txt","adult-hosts.txt","nsfw-onlydomains.txt","EnsureAdultCatalogAsync","Adult catalog is unexpectedly small","xvideos.com","xnxx.com","xhamster.com")) and all(x in start for x in ("$AdultEnabledFile","$AdultHosts","$AdultFallback","adultHostCount","ADULT_CATALOG_INVALID")) and 'adult-fallback-hosts.txt' in iss) -check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","httpsHealth=[ordered]@{required=2")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) +check("startup_core_site_health_fields", all(x in start for x in ( + "function Get-CoreSiteHealth($yt,$oa,$gh)", + "required=2", + "passed=3-$failed.Count", + "allCoreSitesOk=($failed.Count -eq 0)", + "unreachableCoreSites=@($failed.ToArray())", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", + "$httpsProbePassCount=[int]$httpsHealth.passed", + "$ev.status='PASS_ACTIVE'", +))) +check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) check("ui_openai_403_cannot_be_chatgpt_login_pass", 'FmtOpenAiApiProbe(openAiProbe)' in gui From 99b1aee800452659757dffdcaa20477f098d6e6a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:04:36 +0330 Subject: [PATCH 089/188] docs(brain): R659 SAEID native Microsoft PktMon one-IP inbound TCP RST evidence --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 58847bcb..51554c35 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R658 (both Windows same updated GUI, 681/681 native PASS):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. **R656–R658 Emad Windows now ALSO owner-UAC upgraded with the exact same tested R646 GUI Installer SHA `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`; 681/681 installed hashes PASS, original five-file config and provider, DNS/routes/NRPT and Tailscale preserved, one Uninstaller. Both Windows now have the same private corrected RC1 GUI payload.** Prior Emad Start→YouTube→Stop/Recovery PASS was obtained using the previous GUI before R657, not after. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** +**Latest verified position — 2026-10-16 R659 (R658 accepted two-host RC1 plus bounded Saeid NIC RST discriminator):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. **R656–R658 Emad Windows now ALSO owner-UAC upgraded with the exact same tested R646 GUI Installer SHA `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`; 681/681 installed hashes PASS, original five-file config and provider, DNS/routes/NRPT and Tailscale preserved, one Uninstaller. Both Windows now have the same private corrected RC1 GUI payload.** Prior Emad Start→YouTube→Stop/Recovery PASS was obtained using the previous GUI before R657, not after. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** **R659 same-IP Saeid YouTube TLS reset confirmed as one inbound TCP RST observed by NIC-level Microsoft PktMon (zero after Google control), under manager-approved metadata-only capture with no traffic payload and verified native PktMon cleanup. Precise injector/next packet timing remain unproven; product YouTube failure on Saeid stays OPEN.** + +**R659 SAEID genuine inbound TCP RST at physical NIC — 2026-10-16 (accepted unique diagnostic evidence):** On actual `SAEID` Windows private installed `v1.5.5-rc1` (manifest SHA256 `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, Service Running, owned DPI OFF) the built-in **Microsoft PktMon** required administrator privileges: a native read-only `pktmon status` and `pktmon filter list` returned `Access is denied` under normal owner token. In accordance with owner access instruction, native Windows UAC was requested. The FIRST R659 admin helper `45aa8585-99e4-4296-90ae-816d8f8fba67` failed before opening PktMon (PowerShell precondition `Test-Path ... -or` syntax); no evidence/ETL file or running DPI state, so NOT a measurement. The corrected, parsed native Windows UAC helper `001f133a-a0ea-4971-b94d-b06ea32e281d` **SUCCEEDED exit0** and produced local receipt SHA256 `4046DA014203F52B226885B768F1E98348A649DBBCDD3D29063A5494F023DFD6`, stdout SHA256 `FB573E0B286DEABFB8A2E972CABCA94430CB3C04AF6157CE638420C5E2346849`. Elevated preconditions independently verified **no pre-existing active PktMon capture and no pre-existing filters**; exactly ONE filter `DIM-R659-RST` for **TCP RST packets involving 142.251.155.4:443** applied only during `pktmon start --capture --counters-only --comp nics`. **No packet content was captured**, no PCAP/ETL retained or uploaded. With normal TLS certificate validation and `curl --resolve` to the *same* destination IPv4 (Google and YouTube SNI separated): after Google `www.google.com` (HTTP204, native TLS completed at 0.166474s) PktMon counters output was empty (`[]`); after YouTube `www.youtube.com` (TCP connect 0.091607s, no TLS, HTTP000/curl35), the NIC `Realtek PCIe 5GbE Family Controller` showed **one inbound/Rx 60-byte TCP RST and zero outbound/Tx RSTs**. **Separate nonadmin physical readback verified** receipt SHA, before/Google counters empty and after-YouTube inbound1/outbound0, no owned DPI state, stable running service and pinned installed manifest. Elevated `finally` confirmed `pktmon stop`, `pktmon filter remove`, filtered ETL absent, PktMon idle and empty native filters; `cleaned=true`. **SCOPED PASS:** network-side TCP RST reaches Saeid NIC in response to YouTube SNI but not same-IP Google SNI; strong evidence of SNI-sensitive remote/in-path reset and **not a GUI-only or user-mode Schannel-only failure**. It does **not** identify a specific ISP device, prove whether spoofing occurs before or after server ACK, establish which entity generated RST, or demonstrate a working site workaround. All profile variants Balanced/Compatibility/Strong and naive client-side TCP ClientHello split had previously failed, so do NOT repeat those unchanged tests or mutate system DNS/routes/TLS trust. Future targeted instrumentation, if warranted, should focus on actual **WinWS packet visibility under guarded Start/Stop** rather than speculative profile proliferation. Official authoritative packet counter/filter references: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pktmon-start ; https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pktmon-filter-add ; https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pktmon-counters . **R577–R580 physical transport discrimination — 2026-10-16:** On MMZ, Control D DoH URL `https://76.76.10.11/p0` returned HTTP 400 for an empty GET with **TLS verification 0** (endpoint reachable and certificate verified; this is NOT a valid DNS-query success). Two isolated Chrome 155 trials with requested secure-DoH flags displayed Google HTML but YouTube zero DOM/connection refused; inspection of the fresh Chrome NetLog on an additional run revealed **0 DNS_TRANSACTION_HTTPS_ATTEMPT events** despite 21 DNS query events (R579). Therefore **do not assert browser DoH actually activated** and do not attribute YouTube failure to a proven browser DoH path. Distinct prior R534 unmodified `ctrld v1.5.8` physical rootless loopback DoH **did** resolve global YouTube/Googlevideo addresses; its scope remains DNS only. With disposable browser-only public IP mapping `www.youtube.com -> 142.251.151.4` and normal TLS verification, TCP-mode Chrome still failed (`ERR_CONNECTION_RESET`), while forced QUIC made 10 session events, no QUIC receive packets and `ERR_QUIC_PROTOCOL_ERROR`; both returned zero DOM (R580), with no installed engine. These are path-specific negative results, not proof of a universal ISP mechanism or product fault. All R577/R578/R579/R580 processes/profiles/netlogs ephemeral and removed; pre/post installed metadata SHA, resolver SHA, default route and Tailscale identical. Raw Commander successful complete operations: R577 `ad8c4443-b1a8-4de1-a7b8-de075d2dd2f6` stdout SHA256 `f4e9a603fea5ab2bbfd8440d31461fc20050be1e314523dd647e479d3227e13c`; R578 `b3a67cbd-a600-401f-9768-35dd6de1e1a3` SHA `e619670e2b1f868920ac1e9c2ac4d30a077cd334a4b51e478ffab590eaff40b4`; R579 corrected `89c69678-8db6-4bab-a51e-d9805e3aeff6` SHA `ff8d3f89bdb8836f4c06b8cdc34639ace4d2dfa41f4653ef6853233310490b2d`; R580 `a6201e6b-6a59-4f23-b1d3-13ec1c09e1ae` SHA `be96c1be307ceea6e7f84397d7f7be3d83f9a6eb7a4e84a11c35e8d370c32718`. Unsuccessful first R579 log parser was a `jq` operator-precedence error, not network regression; corrected run and post-baseline PASS. Relevant primary notes: Chromium secure/automatic DNS semantics `https://chromeenterprise.google/intl/en_ca/policies/dns-over-https-mode/`; zapret client TLS/QUIC fingerprint limitations `https://github.com/bol-van/zapret/blob/master/docs/quick_start.md`. From 757c3a59714cfa081bf98700901067f1f160d1c3 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:09:44 +0330 Subject: [PATCH 090/188] docs(brain): verify R660 Emad Linux Stable ACTIVE HTTPS YouTube and Tailscale after route drift resolved --- PROJECT_BRAIN.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PROJECT_BRAIN.md b/PROJECT_BRAIN.md index 51554c35..fe9a14e7 100644 --- a/PROJECT_BRAIN.md +++ b/PROJECT_BRAIN.md @@ -2,7 +2,9 @@ Current public release: **v1.5.4 Stable**, published 13 October 2026. Official tag 51009372e3c2d069d7016b97773f35ee5e1e0631 and six accepted public assets at https://github.com/GOD13emad/DirectInternetMethod/releases/tag/v1.5.4 . -**Latest verified position — 2026-10-16 R659 (R658 accepted two-host RC1 plus bounded Saeid NIC RST discriminator):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. **R656–R658 Emad Windows now ALSO owner-UAC upgraded with the exact same tested R646 GUI Installer SHA `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`; 681/681 installed hashes PASS, original five-file config and provider, DNS/routes/NRPT and Tailscale preserved, one Uninstaller. Both Windows now have the same private corrected RC1 GUI payload.** Prior Emad Start→YouTube→Stop/Recovery PASS was obtained using the previous GUI before R657, not after. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. Emad Linux VPN/default-interface CONFLICT. **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** **R659 same-IP Saeid YouTube TLS reset confirmed as one inbound TCP RST observed by NIC-level Microsoft PktMon (zero after Google control), under manager-approved metadata-only capture with no traffic payload and verified native PktMon cleanup. Precise injector/next packet timing remain unproven; product YouTube failure on Saeid stays OPEN.** +**Latest verified position — 2026-10-16 R660 (accepted R659 NIC reset evidence plus Emad Linux Stable live runtime PASS):** PUBLIC **v1.5.4 Stable** and tag unchanged; PR #60 remains **Draft, unmerged, unpromoted**. Exact private 1.5.5-rc1 base installed both Windows. **R646 source change** in Draft fixes deceptive GUI `"Direct path healthy"` and green `ACTIVE` while YouTube fails under 2/3 startup HTTPS quorum: live status now shows **PARTIAL** and warning when any core YouTube/OpenAI/GitHub transport probe fails or owned resources aren't proven, retaining internal mode ACTIVE and Stop/Recovery. Native Emad .NET 10.0.402 source GUI+Service builds 0 warnings/errors and GitHub PR+push CI PASS, with native hosted stable→new-RC1→stable acceptance. **R648–R650 Saeid physical manager-approved same-AppId in-place GUI update PASS:** new tested private Installer SHA256 `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F` installed, expected GUI SHA `1894573094105AE7FE221ABDA9A7E6AE486FD9C05930CF9EEC0979C79FD8D21C`, manifest SHA `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, native 681/681 SHA PASS, data/one-install/route/DNS/NRPT preserved; old RC1/Stable rollback installers retained. **R656–R658 Emad Windows now ALSO owner-UAC upgraded with the exact same tested R646 GUI Installer SHA `43FC425CE608C9E2F2E21F066D0F3FBA860AD24ED541A0C4995F92C8D79C157F`; 681/681 installed hashes PASS, original five-file config and provider, DNS/routes/NRPT and Tailscale preserved, one Uninstaller. Both Windows now have the same private corrected RC1 GUI payload.** Prior Emad Start→YouTube→Stop/Recovery PASS was obtained using the previous GUI before R657, not after. Saeid actual YouTube access **still FAIL** under all original profile strategies and read-only transport experiments R632–R645. R647 stale 3 owner-only R633 one-shot scheduled tasks retired with verified past exit0, Saeid DPI OFF/service Running. **R651 earlier hidden WPF proof INCONCLUSIVE due incomplete harness; R654 corrected actual WPF hidden OFF/Ready contract PASS and R655 actual WPF PARTIAL/Stop-visible-control contract PASS with an isolated synthetic ACTIVE backend plus real Saeid HTTPS failures.** No desktop focus, installed mutation or DPI activation. MMZ Linux physical Root/User RC1 install and autonomous systemd dual rollback R627–R629 PASS, stable Root1.5.2/User1.5.4 restored; full live DoH still requires explicit owner opt-in, external Tailscale stays active. **R660 Emad Linux current Stable live status is ACTIVE and HTTPS YouTube/Google/GitHub works with Tailscale split-safe, superseding previously recorded CONFLICT for this host/current run only.** **Whole-product FINAL OPEN** (Saeid YouTube, full production-service-ACTIVE GUI integration validation, MMZ native HTTPS/DoH & reboot, Emad Linux, cross-host release signoff). **DO NOT MERGE/RELEASE.** **R659 same-IP Saeid YouTube TLS reset confirmed as one inbound TCP RST observed by NIC-level Microsoft PktMon (zero after Google control), under manager-approved metadata-only capture with no traffic payload and verified native PktMon cleanup. Precise injector/next packet timing remain unproven; product YouTube failure on Saeid stays OPEN.** + +**R660 Emad Linux original Stable live runtime and YouTube reachability — 2026-10-16:** Read-only original on-host `~/.local/share/DirectInternetMethod/direct_internet_method.py` imported with no installation write; its *actual* `load_state()` returned `mode=ACTIVE` and private state JSON `status=ACTIVE`, physical interface **`enp1s0`** now equal to live default-route interface `enp1s0` (older wlp2s0 drift/CONFLICT is **SUPERSEDED as current host status**). Original `_external_tunnel_active()=false`, `_tailscale_split_safe()=true`, `_coexistence_backend_ready()=true`; real `directinternetmethod-nfqws-1000.service` and external `tailscaled` both active. Installed native **Root v1.5.2/User v1.5.4**; not RC1. Independent native Emad Linux unproxied, IPv4-only physical `curl` while original NFQWS was already active returned: **Google `generate_204` HTTP204 exit0** (TCP .340s, TLS .437s), **GitHub homepage HTTP200 exit0** (TCP .358s, TLS .443s), **YouTube `generate_204` HTTP204 exit0** (TCP .400s, TLS .496s), and **YouTube homepage HTTP200 exit0** (TCP .356s, TLS .450s, total 1.104s). Before/after original installed app source SHA, live state JSON SHA, physical `/etc/resolv.conf` SHA, literal IPv4 default route and `tailscaled`/DIM-unit active states were verified identical. Durable Commander `21e7d4c3-9f49-4e9f-a9bc-948fa2631a6d` **SUCCEEDED exit0**, stdout SHA256 `7220F4AF3E6445DD13E41DB664D2AE39031455E3A034F2D73652564D454C121C`; independent readback confirmed Root1.5.2/User1.5.4/State ACTIVE/physical iface enp1s0. No installed binaries, user config, VPN, system DNS, routes or Tailscale modified by this R660 work. **PASS scoped to actual Stable v1.5.4 native Linux runtime reporting plus physical no-proxy HTTPS connectivity and Tailscale coexistence on Emad laptop**. Do NOT infer provenance/actor of the external interface/state correction, video playback, RC1 installation, reboot persistence or that this host result solves the Saeid Windows YouTube network. Historical `Emad Linux CONFLICT` receipts remain valuable R0→R659 provenance, not current truth. Next Linux host critical acceptance is upgrading Emad to RC1 under independent guarded user/Root transaction with owner approval and a verified local recovery path; MMZ has the prior proven independent systemd rollback but pending deliberate production user DNS consent. **R659 SAEID genuine inbound TCP RST at physical NIC — 2026-10-16 (accepted unique diagnostic evidence):** On actual `SAEID` Windows private installed `v1.5.5-rc1` (manifest SHA256 `FAC36AFD3D1933482C658F60E9ED8D5FF5331EB5AAB7E2EEA1941D5C485A8669`, Service Running, owned DPI OFF) the built-in **Microsoft PktMon** required administrator privileges: a native read-only `pktmon status` and `pktmon filter list` returned `Access is denied` under normal owner token. In accordance with owner access instruction, native Windows UAC was requested. The FIRST R659 admin helper `45aa8585-99e4-4296-90ae-816d8f8fba67` failed before opening PktMon (PowerShell precondition `Test-Path ... -or` syntax); no evidence/ETL file or running DPI state, so NOT a measurement. The corrected, parsed native Windows UAC helper `001f133a-a0ea-4971-b94d-b06ea32e281d` **SUCCEEDED exit0** and produced local receipt SHA256 `4046DA014203F52B226885B768F1E98348A649DBBCDD3D29063A5494F023DFD6`, stdout SHA256 `FB573E0B286DEABFB8A2E972CABCA94430CB3C04AF6157CE638420C5E2346849`. Elevated preconditions independently verified **no pre-existing active PktMon capture and no pre-existing filters**; exactly ONE filter `DIM-R659-RST` for **TCP RST packets involving 142.251.155.4:443** applied only during `pktmon start --capture --counters-only --comp nics`. **No packet content was captured**, no PCAP/ETL retained or uploaded. With normal TLS certificate validation and `curl --resolve` to the *same* destination IPv4 (Google and YouTube SNI separated): after Google `www.google.com` (HTTP204, native TLS completed at 0.166474s) PktMon counters output was empty (`[]`); after YouTube `www.youtube.com` (TCP connect 0.091607s, no TLS, HTTP000/curl35), the NIC `Realtek PCIe 5GbE Family Controller` showed **one inbound/Rx 60-byte TCP RST and zero outbound/Tx RSTs**. **Separate nonadmin physical readback verified** receipt SHA, before/Google counters empty and after-YouTube inbound1/outbound0, no owned DPI state, stable running service and pinned installed manifest. Elevated `finally` confirmed `pktmon stop`, `pktmon filter remove`, filtered ETL absent, PktMon idle and empty native filters; `cleaned=true`. **SCOPED PASS:** network-side TCP RST reaches Saeid NIC in response to YouTube SNI but not same-IP Google SNI; strong evidence of SNI-sensitive remote/in-path reset and **not a GUI-only or user-mode Schannel-only failure**. It does **not** identify a specific ISP device, prove whether spoofing occurs before or after server ACK, establish which entity generated RST, or demonstrate a working site workaround. All profile variants Balanced/Compatibility/Strong and naive client-side TCP ClientHello split had previously failed, so do NOT repeat those unchanged tests or mutate system DNS/routes/TLS trust. Future targeted instrumentation, if warranted, should focus on actual **WinWS packet visibility under guarded Start/Stop** rather than speculative profile proliferation. Official authoritative packet counter/filter references: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pktmon-start ; https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pktmon-filter-add ; https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pktmon-counters . From 9ba953b24874f0659fa49f5450f8f257de124b01 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:25:55 +0330 Subject: [PATCH 091/188] test(release): guard RC1-to-Stable numeric update path --- .github/workflows/ci.yml | 3 ++ ...664_direct_update_monotonicity_contract.py | 48 +++++++++++++++++++ tests/release_metadata_audit.py | 18 +++++++ 3 files changed, 69 insertions(+) create mode 100644 tests/r664_direct_update_monotonicity_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e4ad2f8..a56837f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -399,6 +399,7 @@ jobs: python3 -B tests/selective_dns_integration_safety_contract.py python3 -B tests/tailscale_coexistence_contract.py python3 -B tests/release_metadata_audit.py + python3 -B tests/r664_direct_update_monotonicity_contract.py python3 build_linux_package.py python3 -B tests/linux_real_extracted_archive_contract.py @@ -589,6 +590,8 @@ jobs: $ErrorActionPreference='Stop' python -B tests/release_metadata_audit.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } + python -B tests/r664_direct_update_monotonicity_contract.py + if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python -B tests/release_publication_negative_regression.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python -B tests/unsigned_public_release_contract.py --fixtures diff --git a/tests/r664_direct_update_monotonicity_contract.py b/tests/r664_direct_update_monotonicity_contract.py new file mode 100644 index 00000000..92da7c6d --- /dev/null +++ b/tests/r664_direct_update_monotonicity_contract.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Private Windows RC1 -> future public Stable update release-path contract. + +Source-only, no OS/registry/network mutation. The current Windows private RC1 +pilot is installed as 1.5.5-rc1 with AssemblyVersion 1.5.5.0 on two hosts. +The original GUI/Service both compare GitHub vX.Y.Z numerically using .NET +System.Version; a published v1.5.5 therefore would NOT trigger a direct update. +""" +from __future__ import annotations +import ast +import json +import pathlib +import re + +ROOT = pathlib.Path(__file__).resolve().parents[1] +audit = ROOT / "tests" / "release_metadata_audit.py" +tree = ast.parse(audit.read_text(encoding="utf-8-sig")) +nodes = [x for x in tree.body if isinstance(x, ast.FunctionDef) + and x.name == "require_public_stable_forward_update_from_rc155"] +assert len(nodes) == 1, "MONOTONIC_RELEASE_GUARD_NOT_UNIQUE" +isolated = ast.Module(body=nodes, type_ignores=[]) +scope = {"re": re} +exec(compile(isolated, str(audit), "exec"), scope) +guard = scope["require_public_stable_forward_update_from_rc155"] + +for version in ("1.5.1", "1.5.3", "1.5.4", "1.5.6", "1.5.7", "2.0.0"): + guard(version) +for version in ("1.5.5", "1.5.5-rc1", "v1.5.5", "1.5.5.0", "1.5.5junk"): + try: + guard(version) + except SystemExit as exc: + assert str(exc), version + else: + raise AssertionError("UNSAFE_VERSION_WAS_NOT_REJECTED:" + version) + +win_release = json.loads((ROOT / "windows/RELEASE.json").read_text("utf-8-sig")) +if win_release.get("version") == "1.5.5-rc1": + for rel in ("windows/gui/DirectInternetMethod.csproj", + "windows/service/DirectInternetMethod.Service.csproj"): + source = (ROOT / rel).read_text("utf-8-sig") + assert "1.5.5.0" in source, rel + gui = (ROOT / "windows/gui/UpdateClient.cs").read_text("utf-8-sig") + service = (ROOT / "windows/service/Program.cs").read_text("utf-8-sig") + assert "latest <= current" in gui, "GUI_UPDATER_COMPARISON_CHANGED_REVALIDATE_GUARD" + assert "latest <= CurrentVersion" in service, "SERVICE_UPDATER_COMPARISON_CHANGED_REVALIDATE_GUARD" + assert 'Version.TryParse(clean, out var latest)' in gui + assert 'Version.TryParse(clean, out var latest)' in service +print("PASS_R664_PUBLIC_MONOTONIC_PROMOTION_GUARD_NEGATIVE_AND_POSITIVE_FIXTURES") \ No newline at end of file diff --git a/tests/release_metadata_audit.py b/tests/release_metadata_audit.py index 79c8954d..4d1ca7e7 100644 --- a/tests/release_metadata_audit.py +++ b/tests/release_metadata_audit.py @@ -56,7 +56,25 @@ def assert_unsigned_publication_acceptance(meta): ROOT = pathlib.Path(__file__).resolve().parents[1] release = json.loads((ROOT / 'RELEASE.json').read_text(encoding='utf-8-sig')) +def require_public_stable_forward_update_from_rc155(version: str) -> None: + """Public successors must be newer than deployed private Windows 1.5.5-rc1. + + Both already-installed GUI and privileged service carry .NET AssemblyVersion + 1.5.5.0 and compare GitHub's numeric stable tag using System.Version. + In System.Version, v1.5.5 is (1,5,5,-1), NOT newer than (1,5,5,0). + Therefore v1.5.5 Stable could strand both physical RC1 canaries indefinitely + on the private build. The earliest monotonically safe next Stable tag is + v1.5.6 unless a separately validated and installed promotion client exists. + This guard does not declare v1.5.6 acceptable for publication by itself. + """ + if not re.fullmatch(r"[0-9]+[.][0-9]+[.][0-9]+", version): + raise SystemExit("RELEASE_VERSION_NOT_SEMVER") + parts = tuple(int(n) for n in version.split(".")) + if parts > (1, 5, 4) and (*parts, -1) <= (1, 5, 5, 0): + raise SystemExit("PUBLIC_155_WOULD_NOT_AUTOUPDATE_DEPLOYED_PRIVATE_RC1_USE_156_OR_VERIFIED_PROMOTION") + version = str(release['version']) +require_public_stable_forward_update_from_rc155(version) platforms = release['platforms'] expected = {} From 24704744db2737f29067c5c31bd973fafebd7b05 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:31:23 +0330 Subject: [PATCH 092/188] fix(ci): run release monotonicity fixture on RC1 checkout --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a56837f5..af5b5bb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,6 +59,7 @@ jobs: - name: Negative-first R519 candidate package and protected upgrade run: | set -euo pipefail + python3 -B tests/r664_direct_update_monotonicity_contract.py python3 -B tests/r519_versioned_package_contract.py python3 -B tests/protected_upgrade_transaction_contract.py python3 -B tests/r519_user_update_transaction_contract.py @@ -399,7 +400,6 @@ jobs: python3 -B tests/selective_dns_integration_safety_contract.py python3 -B tests/tailscale_coexistence_contract.py python3 -B tests/release_metadata_audit.py - python3 -B tests/r664_direct_update_monotonicity_contract.py python3 build_linux_package.py python3 -B tests/linux_real_extracted_archive_contract.py From bf8bc2a3807d8b5f78eaced9237a0abeeb12d87d Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 01:26:44 +0330 Subject: [PATCH 093/188] fix(linux): require YouTube page and 204 before accepting Start --- .github/workflows/ci.yml | 21 +-- linux/app/direct_internet_method.py | 14 +- tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ..._linux_gui_full_youtube_health_contract.py | 126 ++++++++++++++++++ 7 files changed, 150 insertions(+), 21 deletions(-) create mode 100644 tests/r678_linux_gui_full_youtube_health_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af5b5bb8..f1d236f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC" - assert p.is_file() and p.stat().st_size==8370720 + expected="7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA" + assert p.is_file() and p.stat().st_size==8370808 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -86,6 +86,7 @@ jobs: python3 -B tests/r538_active_encrypted_status_contract.py python3 -B tests/r541_route_interface_drift_contract.py python3 -B tests/r545_periodic_gui_status_contract.py + python3 -B tests/r678_linux_gui_full_youtube_health_contract.py python3 -B tests/r552_native_upgrade_guard_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py @@ -145,7 +146,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -174,7 +175,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -207,7 +208,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -236,7 +237,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -265,7 +266,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -295,7 +296,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -340,7 +341,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -366,7 +367,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 7674ba49..069ad51c 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -502,23 +502,25 @@ def curl(url): return {"exit":p.returncode,"meta":p.stdout.strip(),"error":p.stderr.strip()} d=subprocess.run(["getent","ahostsv4","www.youtube.com"],text=True,capture_output=True) h=curl("http://www.youtube.com/") - # Both selective modes must test a real YouTube page, not a - # Google-style /generate_204 URL that can falsely fail valid HTTPS. - selected_dns=bool(s.get("selectiveDns") or s.get("selectiveDnsEncrypted")) - y=curl("https://www.youtube.com/" if selected_dns else "https://www.youtube.com/generate_204") + # Real YouTube page AND lightweight 204 must succeed. R674-R675 native + # tests reproduced one passing while the other fails on the same host. + # Never claim a working site or keep an unverified Start active on 204 alone. + y=curl("https://www.youtube.com/") + y204=curl("https://www.youtube.com/generate_204") o=curl("https://api.openai.com/v1/models") g=curl("https://github.com/") m=curl("https://gemini.google.com/") ok=(not _external_tunnel_active() and d.returncode==0 and h["exit"]==0 and h["meta"].startswith(("200|","301|","302|","303|","307|","308|")) - and y["exit"]==0 and y["meta"].startswith(("200|","204|")) + and y["exit"]==0 and y["meta"].startswith(("200|")) + and y204["exit"]==0 and y204["meta"].startswith(("204|")) and o["exit"]==0 and o["meta"].startswith(("401|","403|")) and g["exit"]==0 and g["meta"].startswith(("200|"))) dns_preserved=s.get("dnsMode")=="system-preserved" methods=(["encrypted-selective-doh"] if dns_preserved and s.get("selectiveDnsEncrypted") else [] if dns_preserved else ["encrypted-dns-doh"])+[ "http-host-split-tcp80","tls-sni-desync-tcp443","quic-desync-udp443"] - return {"ok":ok,"physicalInterface":physical,"dnsMode":s.get("dnsMode","direct-doh"),"directMethods":methods,"youtubeHttp80":h,"youtube":y,"openai":o,"github":g,"gemini":m} + return {"ok":ok,"physicalInterface":physical,"dnsMode":s.get("dnsMode","direct-doh"),"directMethods":methods,"youtubeHttp80":h,"youtube":y,"youtube204":y204,"openai":o,"github":g,"gemini":m} def _version_tuple(text): """Stable must sort newer than prerelease with same three-digit version.""" diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 9a083d34..e957f49c 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC"} +EXPECTED={"version":VERSION,"files":27,"sha256":"7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -87,7 +87,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "732190BEF316C899D58EF6B4B47BD9DFC86BF761AC4A6BA5962FB0FEA27032F8") + "1FD7116E41104E81D29CEFFA23FBA36CEA47191B3AA326BDB596C8B7C2120024") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 4edef98e..42a98288 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="9380c8c7dcd4a0292ce7b4534b8f269c6461500d3ce89c1b80d1eb8e998a46ac" +CANDIDATE_SHA="7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 912d37f5..15f44617 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC" +PIN_PACKAGE="7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" PIN_GUARD="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index dfa25566..86632a50 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="9380C8C7DCD4A0292CE7B4534B8F269C6461500D3CE89C1B80D1EB8E998A46AC" +ARCHIVE="7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r678_linux_gui_full_youtube_health_contract.py b/tests/r678_linux_gui_full_youtube_health_contract.py new file mode 100644 index 00000000..17193223 --- /dev/null +++ b/tests/r678_linux_gui_full_youtube_health_contract.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""Native original Linux GUI Start acceptance negative-first regression. + +Only AST-extracted verify_live() and Window.worker() execute with mocked +filesystem and subprocess responses. No GTK, network, route, sudo, DNS, service +or installed-user app state is modified. Both Google-style YouTube 204 and a +real YouTube HTTPS homepage must succeed to accept Start. +""" +from __future__ import annotations +import ast +import json +import pathlib +from types import SimpleNamespace +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +SRC = (ROOT/"linux/app/direct_internet_method.py").read_text("utf8") +TREE = ast.parse(SRC) +FUNCS = [n for n in TREE.body + if isinstance(n, ast.FunctionDef) and n.name=="verify_live"] +assert len(FUNCS)==1 +GUI = next(n for n in TREE.body if isinstance(n,ast.ClassDef) and n.name=="Window") +WORKER = next(n for n in GUI.body if isinstance(n,ast.FunctionDef) and n.name=="worker") + + +class FakeState: + def __init__(self,doc): self.doc=doc + def read_text(self,encoding="utf-8"): return json.dumps(self.doc) + + +def fixture(*, page="200", lightweight="204", selective=False, route="enp1s0", + google="200", external=False): + requested=[] + doc={"physicalInterface":"enp1s0","dnsMode":"system-preserved", + "selectiveDnsEncrypted":bool(selective),"selectiveDns":False} + answers={ + "http://www.youtube.com/":(0,"200"), + "https://www.youtube.com/":(0,page), + "https://www.youtube.com/generate_204":(0,lightweight), + "https://api.openai.com/v1/models":(0,"401"), + "https://github.com/":(0,google), + "https://gemini.google.com/":(0,"403") + } + def runner(args,**kw): + assert args[0] in ("curl","getent"),args + if args[0]=="getent": + return SimpleNamespace(returncode=0,stdout="1.2.3.4 STREAM\n",stderr="") + assert "--noproxy" in args and "--interface" in args, args + url=args[-1] + requested.append(url) + assert url in answers,url + code,status=answers[url] + if status=="000":code=35 + return SimpleNamespace(returncode=code,stdout=status+"|142.251.157.4|0.2", + stderr="reset" if code else "") + e={ + "json":json, "STATE":FakeState(doc), + "_top_default_iface":lambda:route, + "_external_tunnel_active":lambda:external, + "subprocess":SimpleNamespace(run=runner), + } + exec(compile(ast.Module(body=FUNCS,type_ignores=[]), + "", "exec"),e) + return e["verify_live"],requested + + +class Contract(unittest.TestCase): + def test_lightweight_success_does_not_hide_real_page_failure(self): + f,urls=fixture(page="000",lightweight="204") + result=f() + self.assertIn("https://www.youtube.com/",urls) + self.assertIn("https://www.youtube.com/generate_204",urls) + self.assertFalse(result["ok"]) + self.assertEqual(result["youtube"]["meta"].split("|")[0],"000") + + def test_real_page_success_does_not_hide_lightweight_failure(self): + f,urls=fixture(page="200",lightweight="000") + result=f() + self.assertFalse(result["ok"]) + self.assertIn("https://www.youtube.com/generate_204",urls) + + def test_both_youtube_targets_and_google_openai_github_pass(self): + f,urls=fixture(page="200",lightweight="204") + self.assertTrue(f()["ok"]) + self.assertEqual(urls.count("https://www.youtube.com/"),1) + self.assertEqual(urls.count("https://www.youtube.com/generate_204"),1) + + def test_selected_dns_also_requires_both_youtube_targets(self): + f,urls=fixture(page="200",lightweight="000",selective=True) + self.assertFalse(f()["ok"]) + self.assertIn("https://www.youtube.com/generate_204",urls) + + def test_route_drift_sends_no_http_or_dns(self): + f,urls=fixture(route="eth9") + got=f() + self.assertEqual(got["error"],"PHYSICAL_INTERFACE_ROUTE_DRIFT") + self.assertEqual(urls,[]) + + def test_other_core_site_failure_is_not_accepted(self): + f,_=fixture(google="000") + self.assertFalse(f()["ok"]) + + def test_gui_worker_rolls_back_a_failed_live_acceptance(self): + calls=[] + queued=[] + class GLib: + @staticmethod + def idle_add(fn,*args):queued.append((fn,args)) + def action(command): + calls.append(command) + return {"ok":True,"unit":"directinternetmethod-"+command} + f,_=fixture(page="000",lightweight="204") + e={"run_system_action":action,"verify_live":f,"GLib":GLib} + exec(compile(ast.Module(body=[WORKER],type_ignores=[]), + "", "exec"),e) + instance=SimpleNamespace(finish=lambda r:None) + e["worker"](instance,"start") + self.assertEqual(calls,["start","stop"]) + self.assertFalse(queued[0][1][0]["ok"]) + self.assertEqual(queued[0][1][0]["error"],"LIVE_VERIFY_FAILED_ROLLED_BACK") + + +if __name__ == "__main__": + result=unittest.main(verbosity=0,exit=False) + if not result.result.wasSuccessful():raise SystemExit(20) + print("R678_GUI_FULL_YOUTUBE_HTTPS_AND_204_NEGATIVE_FIRST_PASS") \ No newline at end of file From 7f6e9afaf00c8b85c54327f5fed413fcbb16776e Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 01:31:33 +0330 Subject: [PATCH 094/188] fix(linux): preserve selected DoH page-only HTTPS gate --- .github/workflows/ci.yml | 20 +++++++++---------- linux/app/direct_internet_method.py | 12 ++++++----- tests/r519_versioned_package_contract.py | 4 ++-- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ..._linux_gui_full_youtube_health_contract.py | 18 ++++++++++++----- 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1d236f4..d3ad56be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA" - assert p.is_file() and p.stat().st_size==8370808 + expected="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" + assert p.is_file() and p.stat().st_size==8370998 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -146,7 +146,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -175,7 +175,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -208,7 +208,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -237,7 +237,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -266,7 +266,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -296,7 +296,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -341,7 +341,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -367,7 +367,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 069ad51c..9f745edd 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -502,18 +502,20 @@ def curl(url): return {"exit":p.returncode,"meta":p.stdout.strip(),"error":p.stderr.strip()} d=subprocess.run(["getent","ahostsv4","www.youtube.com"],text=True,capture_output=True) h=curl("http://www.youtube.com/") - # Real YouTube page AND lightweight 204 must succeed. R674-R675 native - # tests reproduced one passing while the other fails on the same host. - # Never claim a working site or keep an unverified Start active on 204 alone. + # R674-R675: in DPI-only mode /generate_204 can pass while the + # real YouTube page is blocked. Require BOTH in that mode. + # R538: selected DNS may legitimately return 404 on generate_204 + # despite a working real homepage. Preserve its accepted page-only gate. + selected_dns=bool(s.get("selectiveDns") or s.get("selectiveDnsEncrypted")) y=curl("https://www.youtube.com/") - y204=curl("https://www.youtube.com/generate_204") + y204=None if selected_dns else curl("https://www.youtube.com/generate_204") o=curl("https://api.openai.com/v1/models") g=curl("https://github.com/") m=curl("https://gemini.google.com/") ok=(not _external_tunnel_active() and d.returncode==0 and h["exit"]==0 and h["meta"].startswith(("200|","301|","302|","303|","307|","308|")) and y["exit"]==0 and y["meta"].startswith(("200|")) - and y204["exit"]==0 and y204["meta"].startswith(("204|")) + and (selected_dns or (y204 is not None and y204["exit"]==0 and y204["meta"].startswith(("204|")))) and o["exit"]==0 and o["meta"].startswith(("401|","403|")) and g["exit"]==0 and g["meta"].startswith(("200|"))) dns_preserved=s.get("dnsMode")=="system-preserved" diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index e957f49c..fcacb89e 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA"} +EXPECTED={"version":VERSION,"files":27,"sha256":"82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -87,7 +87,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "1FD7116E41104E81D29CEFFA23FBA36CEA47191B3AA326BDB596C8B7C2120024") + "46CE52E835060CAFC9FFF96ACAE3654204706797ECD16668C519DD7A5DB24277") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 42a98288..2af4f9f8 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="7cc3ec2adfb993692971d5cf796404702cfe883c3fc90ecfb621ce4d04d4bada" +CANDIDATE_SHA="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 15f44617..04af1b7e 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA" +PIN_PACKAGE="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" PIN_GUARD="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 86632a50..85a63b48 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="7CC3EC2ADFB993692971D5CF796404702CFE883C3FC90ECFB621CE4D04D4BADA" +ARCHIVE="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r678_linux_gui_full_youtube_health_contract.py b/tests/r678_linux_gui_full_youtube_health_contract.py index 17193223..80574393 100644 --- a/tests/r678_linux_gui_full_youtube_health_contract.py +++ b/tests/r678_linux_gui_full_youtube_health_contract.py @@ -3,8 +3,8 @@ Only AST-extracted verify_live() and Window.worker() execute with mocked filesystem and subprocess responses. No GTK, network, route, sudo, DNS, service -or installed-user app state is modified. Both Google-style YouTube 204 and a -real YouTube HTTPS homepage must succeed to accept Start. +or installed-user app state is modified. DPI-only mode requires both +lightweight 204 and the real YouTube homepage; selected DNS retains the R538 page-only gate. """ from __future__ import annotations import ast @@ -85,10 +85,18 @@ def test_both_youtube_targets_and_google_openai_github_pass(self): self.assertEqual(urls.count("https://www.youtube.com/"),1) self.assertEqual(urls.count("https://www.youtube.com/generate_204"),1) - def test_selected_dns_also_requires_both_youtube_targets(self): - f,urls=fixture(page="200",lightweight="000",selective=True) + def test_selected_dns_real_page_succeeds_without_204_requirement(self): + # R538 proven: selected DNS can return 404 at generate_204 even + # when the main HTTPS homepage works; preserve that accepted behavior. + f,urls=fixture(page="200",lightweight="404",selective=True) + self.assertTrue(f()["ok"]) + self.assertIn("https://www.youtube.com/",urls) + self.assertNotIn("https://www.youtube.com/generate_204",urls) + + def test_selected_dns_rejects_broken_real_page_without_204_probe(self): + f,urls=fixture(page="000",lightweight="204",selective=True) self.assertFalse(f()["ok"]) - self.assertIn("https://www.youtube.com/generate_204",urls) + self.assertNotIn("https://www.youtube.com/generate_204",urls) def test_route_drift_sends_no_http_or_dns(self): f,urls=fixture(route="eth9") From fcaa34026e6e9f1039d9e18413f4dbd4839bf835 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 01:37:05 +0330 Subject: [PATCH 095/188] fix(ci): preserve lowercase SHA for R528 native archive pin --- tests/r528_ephemeral_native_root_cli_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 2af4f9f8..d0781529 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" +CANDIDATE_SHA="82c7fcf7536b813d402c35db1126bdbfd937a4c130e8c668b4bce52d919443fa" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" From ab332cc610f7e9c30cf18af3c05a44c12ad1b1b5 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 02:05:54 +0330 Subject: [PATCH 096/188] fix(windows): distinguish real YouTube page from health ping --- .github/workflows/ci.yml | 2 + .../r685_windows_gui_real_youtube_contract.py | 79 +++++++++++++++++++ tests/windows_contract_audit.py | 12 ++- windows/gui/MainWindow.xaml.cs | 16 +++- 4 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 tests/r685_windows_gui_real_youtube_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3ad56be..f95b1ac3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -485,6 +485,8 @@ jobs: if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/windows_contract_audit.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } + python -B tests/r685_windows_gui_real_youtube_contract.py + if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/adult_coverage_data_audit.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/router_gateway_contract_audit.py diff --git a/tests/r685_windows_gui_real_youtube_contract.py b/tests/r685_windows_gui_real_youtube_contract.py new file mode 100644 index 00000000..5ad9c8c9 --- /dev/null +++ b/tests/r685_windows_gui_real_youtube_contract.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +"""Negative-first native Windows WPF GUI core-site health acceptance. + +The service intentionally allows a two-of-three transport quorum to keep the +engine available with PARTIAL health. This test ensures the GUI cannot display +HEALTHY when generate_204 succeeds but the real YouTube page does not. + +No installed services, registry, DNS, routes, browser profiles or network are +modified. Compile just the actual production C# predicate in a local temp dir. +""" +from __future__ import annotations +import pathlib +import re +import subprocess +import tempfile + +ROOT=pathlib.Path(__file__).resolve().parents[1] +GUI=(ROOT/"windows/gui/MainWindow.xaml.cs").read_text("utf-8-sig") +START=(ROOT/"windows/app/Start-Direct.ps1").read_text("utf-8-sig") + +def need(cond,label): + if not cond:raise SystemExit("R685_FAIL_"+label) + +need('StatusBadge.Text = "PARTIAL";' in GUI,"PRESERVE_PARTIAL_STATUS") +need("function Get-CoreSiteHealth($yt,$oa,$gh)" in START and + "$httpsProbePassCount -lt 2" in START,"PRESERVE_TWO_OF_THREE_START") +need('var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });' in GUI, + "REAL_YOUTUBE_PAGE_PROBE_MISSING") +need('var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" });' in GUI, + "LIGHTWEIGHT_YOUTUBE_PROBE_MISSING") +need('var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask };' in GUI, + "SINGLE_REFRESH_ASYNCHRONOUS_TASK_GATHER") +need('var youtubeProbe = await ytTask;' in GUI and + 'var youtube204Probe = await yt204Task;' in GUI,"BOTH_PROBES_NOT_AWAITED") +need("coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe);" in GUI, + "COMPLETE_REACHABILITY_NOT_USED") +need('YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}";' in GUI, + "SITE_SPECIFIC_GUI_EVIDENCE_MISSING") +need('mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)' in GUI, + "PARTIAL_STATUS_NOT_DRIVEN_BY_ALL_SITES") +pat=r"static bool FullCoreReachability\s*\(\s*ProbeResult youtubePage,\s*ProbeResult youtube204,\s*ProbeResult openai,\s*ProbeResult github\)\s*=>\s*[^;]+;" +matches=re.findall(pat,GUI,re.S) +need(len(matches)==1,"PRODUCTION_CSHARP_PREDICATE_NOT_UNIQUE") +method=matches[0] +need("youtubePage.Ok" in method and "youtube204.Ok" in method and + "openai.Ok" in method and "github.Ok" in method,"FULL_CORE_MATRIX_SOURCES") + +with tempfile.TemporaryDirectory(prefix="dim_r685_native_core_site_") as temp: + work=pathlib.Path(temp) + (work/"check.csproj").write_text( + '' + 'Exenet10.0' + 'enableenable' + '',encoding="utf8") + source='''using System; +internal sealed record ProbeResult(bool Ok); +internal static class Product { +%s + internal static void Main(){ + for (int mask=0;mask<16;mask++){ + bool actual=FullCoreReachability( + new ProbeResult((mask&1)!=0), + new ProbeResult((mask&2)!=0), + new ProbeResult((mask&4)!=0), + new ProbeResult((mask&8)!=0)); + if(actual!=(mask==15))throw new Exception($"FALSE_CORE_SUCCESS_MASK_{mask}"); + } + Console.WriteLine("R685_REAL_PRODUCTION_GUI_PREDICATE_16_OF_16_NATIVE_DOTNET10_PASS"); + } +} +''' % method + (work/"Program.cs").write_text(source,encoding="utf8") + p=subprocess.run(["dotnet","run","--project",str(work/"check.csproj"), + "-c","Release","--no-launch-profile","--verbosity","quiet"], + cwd=work,capture_output=True,text=True,timeout=120) + need(p.returncode==0,"NATIVE_DOTNET_BUILD_OR_CORE_MATRIX_"+p.stderr[-400:]) + need("16_OF_16_NATIVE_DOTNET10_PASS" in p.stdout,"MATRIX_NOT_EXECUTED") + print(p.stdout.strip()) +print("R685_WPF_CORE_YOUTUBE_PAGE_AND_204_ACCURACY=PASS") \ No newline at end of file diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 76429546..9ba9e5ae 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -106,7 +106,8 @@ def idx(s,x): return s.find(x) "var youtubeProbe = await ytTask;", "var openAiProbe = await oaTask;", "var githubProbe = await ghTask;", - "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;", + "var youtube204Probe = await yt204Task;", + "coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe);", 'mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)', 'StatusBadge.Text = "PARTIAL";', "StatusBadge.Foreground = warn;", @@ -114,6 +115,15 @@ def idx(s,x): return s.find(x) 'StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED";', ))) +check("ui_youtube_homepage_and_204_independent_health", all(x in gui for x in ( + 'var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });', + 'var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" });', + 'var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask };', + 'FullCoreReachability(ProbeResult youtubePage, ProbeResult youtube204, ProbeResult openai, ProbeResult github)', + 'youtubePage.Ok && youtube204.Ok && openai.Ok && github.Ok', + 'YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}";', + 'StatusBadge.Text = "PARTIAL";' +)) and "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;" not in gui) check("ui_direct_update_service_handoff", 'ServiceClient.Send("update")' in gui and 'ServiceClient.Send("recovery")' in gui and '"handoff"' in gui and "300000" in gui and "DownloadVerifiedAsync" not in gui and "LaunchInstaller" not in gui) check("ui_update_advisory_release_digest", "InstallerDigest" in updater and "digest" in updater and "SHA256SUMS.txt" in updater and "DownloadVerifiedAsync" not in updater and "Process.Start" not in updater) diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index 9cb40958..0bd3e786 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -237,6 +237,12 @@ async Task ProbeAsync(string url, string localIp, string[] allowed) } } + // Engine ACTIVE can be PARTIAL. A lightweight HTTP 204 does not prove + // YouTube's real HTTPS homepage loads; R674-R677 observed this mismatch. + // Never mark the whole core-site set healthy unless both probes succeed. + static bool FullCoreReachability(ProbeResult youtubePage, ProbeResult youtube204, ProbeResult openai, ProbeResult github) => + youtubePage.Ok && youtube204.Ok && openai.Ok && github.Ok; + static string FmtHttp(ProbeResult p) => p.Ok ? $"PASS · HTTP {p.Code} · {p.Ms} ms" : p.Reached ? $"FAIL · HTTP {p.Code} · {p.Ms} ms" : @@ -312,7 +318,8 @@ async Task RefreshStatusAsync() bool coreReachabilityProven = false; if (ownedHealthy && mode == "ACTIVE" && !string.IsNullOrWhiteSpace(IpText.Text) && IpText.Text != "—") { - var ytTask = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "200", "204" }); + var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" }); + var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" }); var oaTask = ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" }); var ghTask = ProbeAsync("https://github.com/", IpText.Text, new[] { "200", "301", "302" }); var geminiTask = ProbeAsync("https://gemini.google.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }); @@ -325,14 +332,15 @@ async Task RefreshStatusAsync() ProbeAsync("https://xhamster.com/", IpText.Text, new[] { "200", "301", "302", "303", "307", "308" }) } : null; - var tasks = new List { ytTask, oaTask, ghTask, geminiTask }; + var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask }; if (adultTasks is not null) tasks.AddRange(adultTasks); await Task.WhenAll(tasks); var youtubeProbe = await ytTask; + var youtube204Probe = await yt204Task; var openAiProbe = await oaTask; var githubProbe = await ghTask; - coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok; - YoutubeText.Text = FmtHttp(youtubeProbe); + coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe); + YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}"; OpenAiText.Text = FmtOpenAiApiProbe(openAiProbe); GithubText.Text = FmtHttp(githubProbe); GeminiText.Text = FmtHttp(await geminiTask); From ddd458ce7f2c173547afff83f3ad6c5c5028d793 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 07:28:49 +0330 Subject: [PATCH 097/188] fix(linux): pin guarded YouTube first-segment compatibility profile --- .github/workflows/ci.yml | 21 ++--- linux/app/direct_internet_method.py | 2 +- linux/app/direct_method_helper.sh | 7 ++ linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ..._watchdog_firstsplit_integrity_contract.py | 87 +++++++++++++++++++ 15 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 tests/r698_root_watchdog_firstsplit_integrity_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f95b1ac3..cf3e75d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" - assert p.is_file() and p.stat().st_size==8370998 + expected="4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84" + assert p.is_file() and p.stat().st_size==8371329 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -87,6 +87,7 @@ jobs: python3 -B tests/r541_route_interface_drift_contract.py python3 -B tests/r545_periodic_gui_status_contract.py python3 -B tests/r678_linux_gui_full_youtube_health_contract.py + python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py python3 -B tests/r552_native_upgrade_guard_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py @@ -146,7 +147,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -175,7 +176,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -208,7 +209,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -237,7 +238,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -266,7 +267,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -296,7 +297,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -341,7 +342,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -367,7 +368,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 9f745edd..06ecf45f 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -48,7 +48,7 @@ SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="99279af19445b4b793d903ee0e114930869039ce0ac1fb6ff4b412ba79665466" +COEXIST_HELPER_SHA256="4bb82cecaf5ba09880eb3fe2dff78948545fefccb77311a82df31e6212213644" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index 037e7d63..3a0a8940 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -818,6 +818,13 @@ EOF NFQWS_ARGS+=( --filter-tcp=80 "${HOST_ARGS[@]}" --dpi-desync=multisplit --dpi-desync-split-pos=method+2 ) + # A narrowly scoped YouTube ClientHello first-segment profile; + # leave general compatibility and Tailscale bypass unchanged. + NFQWS_ARGS+=( + --new --filter-tcp=443 + --hostlist-domains=youtube.com,youtu.be,googlevideo.com + --dpi-desync=multisplit --dpi-desync-split-pos=1 + ) if [ "$ADULT_ON" = "1" ]; then NFQWS_ARGS+=(--new --filter-tcp=443 --hostlist="$STRONG_OVERRIDE" --dpi-desync=multisplit --dpi-desync-split-pos=sniext+1) fi diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 1389ff07..57ee125e 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -23,7 +23,7 @@ FILES=( ("helper","app/direct_method_helper.sh","usr/lib/directinternetmethod/direct_method_helper.sh", "E52FCAEB9C60D9E43D4A5B75462455AD8C5EE4D82FEE679A0CC4A3520D8ABEA5", - "99279AF19445B4B793D903EE0E114930869039CE0AC1FB6FF4B412BA79665466",0o755), + "4BB82CECAF5BA09880EB3FE2DFF78948545FEFCCB77311A82DF31E6212213644",0o755), ("uninstall","system/uninstall_system.sh","usr/lib/directinternetmethod/uninstall_system.sh", "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index ea71facf..4bc53270 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" +PIN_ROOT="975D38545187F90F3F173824287152358087F2EABE747FADC509CE16DC74E4EB" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 4c984613..2b6541f2 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" +TX_SHA="975D38545187F90F3F173824287152358087F2EABE747FADC509CE16DC74E4EB" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" +GUARD_SHA="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index fcacb89e..2383d3a2 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA"} +EXPECTED={"version":VERSION,"files":27,"sha256":"4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -87,7 +87,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "46CE52E835060CAFC9FFF96ACAE3654204706797ECD16668C519DD7A5DB24277") + "E7FA3D02979EE2E35871C75A9EEF2549AC69774CC96EB7EA10328CCA0D01C636") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index d0781529..0741f8fb 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="82c7fcf7536b813d402c35db1126bdbfd937a4c130e8c668b4bce52d919443fa" +CANDIDATE_SHA="4fc8afc9bbe8bc5492e4b8c10c905e773c9daf1f1c2e806a0a9c321d25a42c84" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 25158a2c..8e9cfea7 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "eb107164ccede891c8bf41c06ff422f1b53b59d2d2ddf0b3dd23ebfe5de9601b" +ROOT_SHA = "975d38545187f90f3f173824287152358087f2eabe747fadc509ce16dc74e4eb" USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 7ae200d4..8b962378 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "eb107164ccede891c8bf41c06ff422f1b53b59d2d2ddf0b3dd23ebfe5de9601b" +PIN_ROOT = "975d38545187f90f3f173824287152358087f2eabe747fadc509ce16dc74e4eb" PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 36379634..68655127 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" +SOURCE_SHA="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 04af1b7e..da140a7b 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" -PIN_ROOT="EB107164CCEDE891C8BF41C06FF422F1B53B59D2D2DDF0B3DD23EBFE5DE9601B" -PIN_GUARD="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" +PIN_PACKAGE="4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84" +PIN_ROOT="975D38545187F90F3F173824287152358087F2EABE747FADC509CE16DC74E4EB" +PIN_GUARD="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 835ae319..bb422512 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="6E9FA25F7D3ADEBC6451DB81A7436467F47F9C2F6B34554951AC375F92BE9B1C" +PIN_GUARD="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 85a63b48..140547f8 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="82C7FCF7536B813D402C35DB1126BDBFD937A4C130E8C668B4BCE52D919443FA" +ARCHIVE="4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r698_root_watchdog_firstsplit_integrity_contract.py b/tests/r698_root_watchdog_firstsplit_integrity_contract.py new file mode 100644 index 00000000..c980e931 --- /dev/null +++ b/tests/r698_root_watchdog_firstsplit_integrity_contract.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""R698 first-segment profile and independent Root/Watchdog integrity chain. + +Runs source inspection and an optional native unprivileged nfqws --dry-run. +No OS installation, admin/root privileges, packet filters, DNS or VPN mutation. +""" +from __future__ import annotations +import hashlib +import pathlib +import re +import subprocess +import sys + +ROOT=pathlib.Path(__file__).resolve().parents[1] +def src(relative: str)->str: + return (ROOT/relative).read_text(encoding="utf-8-sig") +def sha(relative: str)->str: + return hashlib.sha256((ROOT/relative).read_bytes()).hexdigest().upper() +def need(ok,reason): + if not ok: raise AssertionError("R698_"+reason) + +helper=src("linux/app/direct_method_helper.sh") +split_head=helper.split(' NFQWS_ARGS=(--qnum="$QNUM")',1) +need(len(split_head)==2,"MISSING_NFQWS_PROFILE_START") +strategies=split_head[1].split(' compatibility)',1) +need(len(strategies)==2,"MISSING_COMPAT") +compat=strategies[1].split(' strong)',1) +need(len(compat)==2,"MISSING_STRONG") +profile=compat[0] +first=re.search( + r'NFQWS_ARGS\+=\(\s*--new\s+--filter-tcp=443\s+' + r'--hostlist-domains=youtube[.]com,youtu[.]be,googlevideo[.]com\s+' + r'--dpi-desync=multisplit\s+--dpi-desync-split-pos=1\s*\)', + profile,re.S) +need(first is not None,"YOUTUBE_FIRST_BYTE_PROFILE_MISSING") +generic='--new --filter-tcp=443 "${HOST_ARGS[@]}" --dpi-desync=multisplit --dpi-desync-split-pos=1,sniext+1,host+1,midsld,endhost-1' +need(generic in profile,"EXISTING_GENERAL_HTTPS_PROFILE_MISSING") +need(profile.index('--filter-tcp=80') Date: Sat, 17 Oct 2026 07:30:52 +0330 Subject: [PATCH 098/188] test(linux): execute pinned nfqws fixture from temporary owned copy --- ...8_root_watchdog_firstsplit_integrity_contract.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/r698_root_watchdog_firstsplit_integrity_contract.py b/tests/r698_root_watchdog_firstsplit_integrity_contract.py index c980e931..58a5128e 100644 --- a/tests/r698_root_watchdog_firstsplit_integrity_contract.py +++ b/tests/r698_root_watchdog_firstsplit_integrity_contract.py @@ -10,6 +10,8 @@ import re import subprocess import sys +import tempfile +import shutil ROOT=pathlib.Path(__file__).resolve().parents[1] def src(relative: str)->str: @@ -80,7 +82,16 @@ def need(ok,reason): "--dpi-desync=multisplit","--dpi-desync-split-pos=1", "--new","--filter-tcp=443","--hostlist-domains=youtube.com", "--dpi-desync=multisplit","--dpi-desync-split-pos=1,sniext+1,host+1,midsld,endhost-1"] - p=subprocess.run(args,capture_output=True,text=True,timeout=12,check=False) + # Git stores this binary without an executable bit; the installer sets + # that bit on the installed copy. Never chmod original source or host. + with tempfile.TemporaryDirectory(prefix="dim-r698-nfqws-") as scratch: + executable=pathlib.Path(scratch)/"nfqws" + shutil.copyfile(binary,executable) + need(hashlib.sha256(executable.read_bytes()).digest()== + hashlib.sha256(binary.read_bytes()).digest(),"TEMP_BINARY_NOT_BYTE_EXACT") + executable.chmod(0o500) + args[0]=str(executable) + p=subprocess.run(args,capture_output=True,text=True,timeout=12,check=False) need(p.returncode==0 and "command line parameters verified" in p.stdout, "ORIGINAL_NFQWS1_NATIVE_PARSER_FAILED") print("R698_NATIVE_ORIGINAL_NFQWS1_3_PROFILE_DRYRUN_PASS") From 58d8ef8ef7bc52d28fc7bfdd91ada30e957dcc44 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:11:22 +0330 Subject: [PATCH 099/188] test(security): isolate and verify Windows probe argv and curl provenance (R057) --- windows/gui/ProbeProcessBuilder.cs | 64 ++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 windows/gui/ProbeProcessBuilder.cs diff --git a/windows/gui/ProbeProcessBuilder.cs b/windows/gui/ProbeProcessBuilder.cs new file mode 100644 index 00000000..192ebf7f --- /dev/null +++ b/windows/gui/ProbeProcessBuilder.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Sockets; + +namespace DirectInternetMethod; + +// This is a pure process-creation plan: it never launches curl or changes host routes. +// Restrict executable discovery to the OS protected system directory, not PATH/CWD. +internal static class ProbeProcessBuilder +{ + private static readonly HashSet ProbeHosts = new(StringComparer.OrdinalIgnoreCase) + { + "www.youtube.com", "api.openai.com", "github.com", "gemini.google.com", + "www.pornhub.com", "www.xvideos.com", "www.xnxx.com", "xhamster.com" + }; + + internal static ProcessStartInfo Create(string url, string localIp) + { + var ipText = localIp?.Trim(); + if (string.IsNullOrEmpty(ipText) || + !IPAddress.TryParse(ipText, out var ip) || + ip.AddressFamily != AddressFamily.InterNetwork || + !string.Equals(ip.ToString(), ipText, StringComparison.Ordinal)) + throw new ArgumentException("Probe requires one canonical physical IPv4 address.", nameof(localIp)); + + if (!Uri.TryCreate(url, UriKind.Absolute, out var endpoint) || + endpoint.Scheme != Uri.UriSchemeHttps || + endpoint.Port != 443 || + !string.IsNullOrEmpty(endpoint.UserInfo) || + !string.IsNullOrEmpty(endpoint.Fragment) || + !string.IsNullOrEmpty(endpoint.Query) || + !ProbeHosts.Contains(endpoint.IdnHost)) + throw new ArgumentException("Probe endpoint is not an allowed HTTPS destination.", nameof(url)); + + var systemFolder = Environment.GetFolderPath(Environment.SpecialFolder.System); + if (string.IsNullOrEmpty(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) + throw new InvalidOperationException("Trusted Windows system directory unavailable."); + var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); + if (!File.Exists(curl)) + throw new FileNotFoundException("Protected Windows curl.exe unavailable.", curl); + + var psi = new ProcessStartInfo(curl) + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + // ArgumentList prevents endpoint/interface data from being reparsed as extra curl flags. + foreach (var argument in new[] + { + "-4", "--interface", ip.ToString(), "--noproxy", "*", + "-A", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36", + "-sS", "-o", "NUL", "-w", "%{http_code}|%{remote_ip}|%{time_total}", + "--connect-timeout", "5", "--max-time", "8", endpoint.AbsoluteUri + }) + psi.ArgumentList.Add(argument); + return psi; + } +} From c587a83ea44eefadf415cbfea520ea248d9534e0 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:11:29 +0330 Subject: [PATCH 100/188] test(security): isolate and verify Windows probe argv and curl provenance (R057) --- .../ProbeProcessSecurity.csproj | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/probe_process_security/ProbeProcessSecurity.csproj diff --git a/tests/probe_process_security/ProbeProcessSecurity.csproj b/tests/probe_process_security/ProbeProcessSecurity.csproj new file mode 100644 index 00000000..00f77602 --- /dev/null +++ b/tests/probe_process_security/ProbeProcessSecurity.csproj @@ -0,0 +1,11 @@ + + + Exe + net10.0 + enable + enable + + + + + From e6cc5f4f4dec2e463822c8925029760d33170042 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:11:36 +0330 Subject: [PATCH 101/188] test(security): isolate and verify Windows probe argv and curl provenance (R057) --- tests/probe_process_security/Program.cs | 99 +++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/probe_process_security/Program.cs diff --git a/tests/probe_process_security/Program.cs b/tests/probe_process_security/Program.cs new file mode 100644 index 00000000..76edc361 --- /dev/null +++ b/tests/probe_process_security/Program.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Json; +using DirectInternetMethod; + +var passed = 0; +void Check(string name, Action test) +{ + test(); + passed++; + Console.WriteLine("PASS " + name); +} +void Need(bool value, string reason) +{ + if (!value) throw new InvalidOperationException("R057_SECURITY_REGRESSION:" + reason); +} +void Reject(string label, string url, string ip) +{ + Check(label, () => + { + try + { + _ = ProbeProcessBuilder.Create(url, ip); + throw new InvalidOperationException("R057_UNSAFE_ARGUMENT_ACCEPTED:" + label); + } + catch (ArgumentException) { } + }); +} + +const string valid = "https://www.youtube.com/generate_204"; +const string physical = "192.168.8.42"; + +Check("fixed_system32_curl_not_path_or_cwd", () => +{ + var plan = ProbeProcessBuilder.Create(valid, physical); + var expected = Path.GetFullPath(Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.System), "curl.exe")); + Need(string.Equals(plan.FileName, expected, StringComparison.OrdinalIgnoreCase), + "Executable is not the OS system curl"); + Need(Path.IsPathFullyQualified(plan.FileName), "Executable path not absolute"); + Need(!plan.UseShellExecute && plan.CreateNoWindow && + plan.RedirectStandardOutput && plan.RedirectStandardError, "Unsafe process shell IO"); +}); +Check("atomic_seventeen_curl_arguments", () => +{ + var args = ProbeProcessBuilder.Create(valid, physical).ArgumentList.ToArray(); + Need(args.Length == 17, "Unexpected argv count"); + Need(args[0] == "-4" && args[1] == "--interface" && args[2] == physical, + "Interface not a single argv token"); + Need(args[3] == "--noproxy" && args[4] == "*", "Proxy bypass drift"); + Need(args[8] == "-o" && args[9] == "NUL", "No-output behavior drift"); + Need(args[10] == "-w" && args[11] == "%{http_code}|%{remote_ip}|%{time_total}", + "Live metrics parser contract drift"); + Need(args[16] == valid, "Endpoint changed"); +}); +Check("path_poisoning_does_not_change_executable", () => +{ + var old = Environment.GetEnvironmentVariable("PATH"); + try + { + Environment.SetEnvironmentVariable("PATH", Path.Combine(Path.GetTempPath(), "R057-untrusted-bin")); + var plan = ProbeProcessBuilder.Create(valid, physical); + Need(plan.FileName.EndsWith("curl.exe", StringComparison.OrdinalIgnoreCase) && + !plan.FileName.Contains("R057-untrusted-bin", StringComparison.OrdinalIgnoreCase), + "User-controlled PATH influenced process filename"); + } + finally { Environment.SetEnvironmentVariable("PATH", old); } +}); +Check("approved_https_hosts_remain_usable", () => +{ + foreach (var host in new[] { + "www.youtube.com", "api.openai.com", "github.com", "gemini.google.com", + "www.pornhub.com", "www.xvideos.com", "www.xnxx.com", "xhamster.com" + }) + Need(ProbeProcessBuilder.Create("https://" + host + "/", physical) + .ArgumentList.Last() == "https://" + host + "/", + "Approved origin silently blocked: " + host); +}); +foreach (var (label, url, ip) in new (string, string, string)[] { + ("injected_interface_flags", valid, "192.168.8.42 --output C:\\evil.txt"), + ("injected_interface_newline", valid, "192.168.8.42\n--data-binary @C:\\s"), + ("injected_interface_quotes", valid, "192.168.8.42\" -K cfg"), + ("ipv6_rejected", valid, "::1"), + ("invalid_octet_rejected", valid, "999.168.8.42"), + ("leading_dash_rejected", valid, "--config"), + ("noncanonical_ipv4_rejected", valid, "127.1"), + ("scheme_http_rejected", "http://github.com/", physical), + ("host_spoof_rejected", "https://github.com.evil.example/", physical), + ("userinfo_rejected", "https://user:pass@github.com/", physical), + ("unapproved_host_rejected", "https://attacker.example/", physical), + ("custom_port_rejected", "https://github.com:8443/", physical), + ("fragment_rejected", "https://github.com/#data", physical), + ("query_rejected", "https://github.com/?test=1", physical), +}) Reject(label, url, ip); +Console.WriteLine(JsonSerializer.Serialize(new { Status = "PASS", Version = "R057", + Checks = passed, ActualProcessSpawned = false, NetworkChanged = false })); From 3704e573cf153cfe1e0b27a222bb5eb0b2c6f648 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:11:53 +0330 Subject: [PATCH 102/188] fix(security): use pinned Windows system curl with tokenized validated probes (R057) --- windows/gui/MainWindow.xaml.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index d6900617..b9ae148c 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -211,15 +211,7 @@ async Task ProbeAsync(string url, string localIp, string[] allowed) var sw = Stopwatch.StartNew(); try { - var psi = new ProcessStartInfo("curl.exe", - $"-4 --interface {localIp} --noproxy * -A \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36\" -sS -o NUL -w \"%{{http_code}}|%{{remote_ip}}|%{{time_total}}\" --connect-timeout 5 --max-time 8 \"{url}\"") - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden - }; + var psi = ProbeProcessBuilder.Create(url, localIp); using var p = Process.Start(psi)!; var stdout = await p.StandardOutput.ReadToEndAsync(); await p.WaitForExitAsync(); From 0d4fd6ef1d44c508e6a2be347c9cdb3b604158f4 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:12:54 +0330 Subject: [PATCH 103/188] ci(security): run R057 hostile IPv4, URL and PATH process-origin regressions on Windows --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c6670ff..d086f07c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,6 +178,12 @@ jobs: run: dotnet build windows/service/DirectInternetMethod.Service.csproj -c Release --nologo - name: Build GUI run: dotnet build windows/gui/DirectInternetMethod.csproj -c Release --nologo + - name: R057 Windows probe curl origin and argv hostile regression + shell: pwsh + run: | + $ErrorActionPreference='Stop' + dotnet run --project tests/probe_process_security/ProbeProcessSecurity.csproj -c Release --no-launch-profile + if($LASTEXITCODE -ne 0){throw 'R057_GUI_PROBE_SECURITY_REGRESSION_FAILED'} - name: Restore pinned PowerShell runtime shell: pwsh run: pwsh.exe -NoProfile -ExecutionPolicy Bypass -File tests/prepare_powershell_runtime.ps1 From 93eac4857d0d27ff29ae7f0e6b37d69efb8ce25b Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:16:57 +0330 Subject: [PATCH 104/188] fix(linux): exempt marked nfqws reinjection before owned NFQUEUE --- .github/workflows/ci.yml | 33 +++++++++++------ linux/app/direct_internet_method.py | 2 +- linux/app/direct_method_helper.sh | 15 +++++--- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +-- tests/linux_nft_kernel_namespace_contract.py | 29 +++++++++++---- tests/r519_versioned_package_contract.py | 4 +-- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 ++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r702_nfqws_reinjected_mark_contract.py | 36 +++++++++++++++++++ tests/tailscale_cleanup_ownership_contract.py | 8 ++++- tests/tailscale_nft_ownership_contract.py | 6 ++++ ...e_transient_rollback_ownership_contract.py | 12 +++++-- 19 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 tests/r702_nfqws_reinjected_mark_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf3e75d1..fb9669c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84" - assert p.is_file() and p.stat().st_size==8371329 + expected="C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332" + assert p.is_file() and p.stat().st_size==8371711 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -88,6 +88,11 @@ jobs: python3 -B tests/r545_periodic_gui_status_contract.py python3 -B tests/r678_linux_gui_full_youtube_health_contract.py python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py + python3 -B tests/r702_nfqws_reinjected_mark_contract.py + python3 -B tests/tailscale_nft_ownership_contract.py + python3 -B tests/tailscale_cleanup_ownership_contract.py + python3 -B tests/tailscale_transient_rollback_ownership_contract.py + python3 -B tests/linux_nft_kernel_namespace_contract.py --source-only python3 -B tests/r552_native_upgrade_guard_contract.py python3 -B tests/tailscale_ui_state_contract.py python3 -B tests/tailscale_gui_scope_guard_contract.py @@ -135,6 +140,14 @@ jobs: # All absolute-root writes occur inside the disposable GH-hosted Ubuntu VM only. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r528_ephemeral_native_root_cli_contract.py + - name: R702 strict NFT ownership on disposable kernel network namespace + shell: bash + run: | + set -euo pipefail + # The test validates root and real nft inside a fresh unshare --net + # child; it rejects host netns and never edits the runner's routes. + sudo -n /usr/bin/python3 -B tests/linux_nft_kernel_namespace_contract.py --kernel + linux-root-user-integrated-ephemeral: name: linux-root-user-integrated-ephemeral needs: linux-native-root-ephemeral @@ -147,7 +160,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -176,7 +189,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -209,7 +222,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -238,7 +251,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -267,7 +280,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -297,7 +310,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -342,7 +355,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -368,7 +381,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 06ecf45f..e0772473 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -48,7 +48,7 @@ SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="4bb82cecaf5ba09880eb3fe2dff78948545fefccb77311a82df31e6212213644" +COEXIST_HELPER_SHA256="d4169f5be2c11290b018905f9ade16dc8d70dc16000ebab1a8013e3162796b15" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index 3a0a8940..38360605 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -157,7 +157,7 @@ import re, sys table, iface, mode = sys.argv[1:] lines = [line.strip() for line in sys.stdin if line.strip()] coexist = mode == "system-preserved" -expected_count = 9 if coexist else 8 +expected_count = 10 if coexist else 9 if len(lines) != expected_count: sys.exit(1) if lines[:2] != [f"table inet {table} {{", "chain output {"]: @@ -166,11 +166,13 @@ if not re.fullmatch(r"type filter hook output priority (?:mangle|-150); policy a sys.exit(1) if lines[-2:] != ["}", "}"]: sys.exit(1) -offset = 3 +if not re.fullmatch(r"meta mark & 0x0*40000000 == 0x0*40000000 return", lines[3]): + sys.exit(1) +offset = 4 if coexist: - if not re.fullmatch(r"meta mark & 0x0*ff0000 == 0x0*80000 return", lines[3]): + if not re.fullmatch(r"meta mark & 0x0*ff0000 == 0x0*80000 return", lines[4]): sys.exit(1) - offset = 4 + offset = 5 queue = r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? bypass)" dev = r"oifname (?:" + re.escape(iface) + r"|" + re.escape(chr(34) + iface + chr(34)) + r")" for index, (proto, port) in enumerate((("tcp", 80), ("tcp", 443), ("udp", 443))): @@ -308,7 +310,7 @@ if not re.fullmatch(r"type filter hook output priority (?:mangle|-150); policy a sys.exit(1) dev=r"oifname (?:"+re.escape(iface)+r"|" + re.escape(chr(34)+iface+chr(34))+r")" queue=r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? bypass)" -patterns=[] +patterns=[r"meta mark & 0x0*40000000 == 0x0*40000000 return"] if coexist=="1": patterns.append(r"meta mark & 0x0*ff0000 == 0x0*80000 return") for proto,port in (("tcp",80),("tcp",443),("udp",443)): @@ -803,6 +805,9 @@ EOF nft add table inet "$TABLE" CREATED_TABLE=1 nft "add chain inet $TABLE output { type filter hook output priority mangle; policy accept; }" + # nfqws v72.13 marks injected packets 0x40000000. Ignore those before + # queues to prevent requeue loops, independent of Tailscale safeguards. + nft "add rule inet $TABLE output meta mark & 0x40000000 == 0x40000000 return" if [ "$TAILSCALE_COEXIST" -eq 1 ]; then # Tailscale LinuxBypassMark 0x80000/0xff0000 protects its own DERP/control traffic. nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return" diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 57ee125e..e4a9b7f6 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -23,7 +23,7 @@ FILES=( ("helper","app/direct_method_helper.sh","usr/lib/directinternetmethod/direct_method_helper.sh", "E52FCAEB9C60D9E43D4A5B75462455AD8C5EE4D82FEE679A0CC4A3520D8ABEA5", - "4BB82CECAF5BA09880EB3FE2DFF78948545FEFCCB77311A82DF31E6212213644",0o755), + "D4169F5BE2C11290B018905F9ADE16DC8D70DC16000EBAB1A8013E3162796B15",0o755), ("uninstall","system/uninstall_system.sh","usr/lib/directinternetmethod/uninstall_system.sh", "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 4bc53270..d8d4d905 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="975D38545187F90F3F173824287152358087F2EABE747FADC509CE16DC74E4EB" +PIN_ROOT="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 2b6541f2..62cdfce5 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="975D38545187F90F3F173824287152358087F2EABE747FADC509CE16DC74E4EB" +TX_SHA="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" +GUARD_SHA="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/linux_nft_kernel_namespace_contract.py b/tests/linux_nft_kernel_namespace_contract.py index 34dff782..3ebf4a72 100644 --- a/tests/linux_nft_kernel_namespace_contract.py +++ b/tests/linux_nft_kernel_namespace_contract.py @@ -107,6 +107,9 @@ def kernel_script() -> str: new_chain(){ nft "add chain inet $TABLE output { type filter hook output priority mangle; policy accept; }" } +injected_mark(){ + nft "add rule inet $TABLE output meta mark & 0x40000000 == 0x40000000 return" +} mark(){ nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return" } @@ -126,17 +129,31 @@ def kernel_script() -> str: } # Positive complete table: production ownership checker and cleanup. put_state -new_table; new_chain; mark; queues +new_table; new_chain; injected_mark; mark; queues nft_table_presence nft_table_owned "$PHY" cleanup_state_owned verify_clean expect_absent echo "PASS_KERNEL_COMPLETE_OWNED_CLEANUP" +# A table lacking nfqws injected-packet exemption is NOT owned. Root must +# preserve it rather than deleting a rule set that may loop reinjected packets. +put_state +new_table; new_chain; mark; queues +if nft_table_owned "$PHY"; then + echo "FALSE_OWNERSHIP_WITH_MISSING_INJECTED_MARK" >&2; exit 92 +fi +if cleanup_state_owned; then rc=0; else rc=$?; fi +test "$rc" -eq 81 +test -f "$STATE" +nft list table inet "$TABLE" >/dev/null +nft delete table inet "$TABLE" +expect_absent +echo "PASS_KERNEL_MISSING_NFQWS_INJECTED_MARK_FAIL_CLOSED" # Prefix-safe rollback: table only, chain+mark, complete table. for mode in empty partial full; do put_state; new_table - if [ "$mode" != "empty" ]; then new_chain; mark; fi + if [ "$mode" != "empty" ]; then new_chain; injected_mark; mark; fi if [ "$mode" = "full" ]; then queues; fi if cleanup_transient; then rc=0; else rc=$?; fi test "$rc" -eq 0 @@ -145,7 +162,7 @@ def kernel_script() -> str: done # Foreign rule after Start: state must be preserved with explicit diagnostic. put_state -new_table; new_chain; mark; queues +new_table; new_chain; injected_mark; mark; queues nft add rule inet "$TABLE" output ip daddr 203.0.113.7 drop if nft_table_transient_owned "$PHY" "$TAILSCALE_COEXIST"; then echo "FALSE_FOREIGN_OWNERSHIP" >&2; exit 90 @@ -159,7 +176,7 @@ def kernel_script() -> str: echo "PASS_KERNEL_FOREIGN_ROLLBACK_PRESERVATION" # Foreign rule after Start completion: stop must fail before killing process. put_state -new_table; new_chain; mark; queues +new_table; new_chain; injected_mark; mark; queues nft add rule inet "$TABLE" output ip daddr 203.0.113.7 drop if cleanup_state_owned; then rc=0; else rc=$?; fi test "$rc" -eq 81 @@ -170,7 +187,7 @@ def kernel_script() -> str: echo "PASS_KERNEL_FOREIGN_STATE_CLEANUP_PRESERVATION" # A real foreign table must cause the production EXIT trap to fail. put_state -new_table; new_chain; mark; queues +new_table; new_chain; injected_mark; mark; queues nft add rule inet "$TABLE" output ip daddr 203.0.113.7 drop export TABLE PHY TAILSCALE_COEXIST CREATED_TABLE CREATED_LINK CREATED_STATE RUN_HOSTS NPID CPID CTRLD NFQWS export -f rollback_on_exit cleanup_transient nft_table_presence nft_table_transient_owned pid_owned remove_dns_link @@ -188,7 +205,7 @@ def kernel_script() -> str: nft delete table inet "$TABLE" expect_absent echo "PASS_KERNEL_FAILED_ROLLBACK_EXIT_NONZERO" -echo "PASS_KERNEL_NAMESPACE_NFT_CONTRACT_7_OF_7" +echo "PASS_KERNEL_NAMESPACE_NFT_CONTRACT_8_OF_8" ''' script = functions + "\n" + cases # Fixture variables must be initialized before Bash set -u cleanup. diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 2383d3a2..36788d8f 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84"} +EXPECTED={"version":VERSION,"files":27,"sha256":"C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -87,7 +87,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "E7FA3D02979EE2E35871C75A9EEF2549AC69774CC96EB7EA10328CCA0D01C636") + "D3A9B4D6F8D75C41FC2D3A12B1376BEB305E7388F852192FA6DAE2D65C5CBDCA") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 0741f8fb..ec4e3a1e 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="4fc8afc9bbe8bc5492e4b8c10c905e773c9daf1f1c2e806a0a9c321d25a42c84" +CANDIDATE_SHA="c1e644e53ca3eb106b6dca5ed942a2ecb2052fb636ff0e60fd9e5a5b0783e332" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 8e9cfea7..a2a4f46f 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "975d38545187f90f3f173824287152358087f2eabe747fadc509ce16dc74e4eb" +ROOT_SHA = "121d9151958901036a50c0c9ef93568136f1ee11433e0021b5496cf003240682" USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 8b962378..7a565778 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "975d38545187f90f3f173824287152358087f2eabe747fadc509ce16dc74e4eb" +PIN_ROOT = "121d9151958901036a50c0c9ef93568136f1ee11433e0021b5496cf003240682" PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 68655127..ba546c42 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" +SOURCE_SHA="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index da140a7b..849c558c 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84" -PIN_ROOT="975D38545187F90F3F173824287152358087F2EABE747FADC509CE16DC74E4EB" -PIN_GUARD="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" +PIN_PACKAGE="C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332" +PIN_ROOT="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" +PIN_GUARD="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index bb422512..d85b7963 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="EE8149A4A5288AC8058B9091D2AA0E15A5A994246BE0D10C0A0AD36F7B42457A" +PIN_GUARD="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 140547f8..8f50da71 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="4FC8AFC9BBE8BC5492E4B8C10C905E773C9DAF1F1C2E806A0A9C321D25A42C84" +ARCHIVE="C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r702_nfqws_reinjected_mark_contract.py b/tests/r702_nfqws_reinjected_mark_contract.py new file mode 100644 index 00000000..03b97019 --- /dev/null +++ b/tests/r702_nfqws_reinjected_mark_contract.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""R702 reject queued nfqws reinjected traffic: source+owned-table policy. + +Negative-first source fixture; no privileged commands, netfilter state, +network connections, DNS, VPN, or user settings are modified. +""" +from __future__ import annotations +from pathlib import Path +import re + +ROOT=Path(__file__).resolve().parents[1] +helper=(ROOT/"linux/app/direct_method_helper.sh").read_text("utf-8-sig") +mark='meta mark & 0x40000000 == 0x40000000 return' +tailscale='meta mark & 0xff0000 == 0x80000 return' +chain='nft "add chain inet $TABLE output { type filter hook output priority mangle; policy accept; }"' +owned='nft_table_owned(){' +transient='nft_table_transient_owned(){' +queue='nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 80 ct original packets 1-6 queue num "$QNUM" bypass' +expected='nft "add rule inet $TABLE output '+mark+'"' +assert helper.count(expected)==1,"MISSING_UNIQUE_NFQWS_REINJECTED_MARK_EXCLUSION" +assert helper.count('nft "add rule inet $TABLE output '+tailscale+'"')==1,"TAILSCALE_MARK_RULE_DRIFT" +assert helper.index(chain) Date: Sat, 17 Oct 2026 08:18:14 +0330 Subject: [PATCH 105/188] fix(security): resolve verified curl from OS SystemDirectory, not folder-path input (R057) --- windows/gui/ProbeProcessBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/gui/ProbeProcessBuilder.cs b/windows/gui/ProbeProcessBuilder.cs index 192ebf7f..48212b60 100644 --- a/windows/gui/ProbeProcessBuilder.cs +++ b/windows/gui/ProbeProcessBuilder.cs @@ -35,7 +35,7 @@ internal static ProcessStartInfo Create(string url, string localIp) !ProbeHosts.Contains(endpoint.IdnHost)) throw new ArgumentException("Probe endpoint is not an allowed HTTPS destination.", nameof(url)); - var systemFolder = Environment.GetFolderPath(Environment.SpecialFolder.System); + var systemFolder = Environment.SystemDirectory; if (string.IsNullOrEmpty(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) throw new InvalidOperationException("Trusted Windows system directory unavailable."); var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); From 1718a2416ac234c4d8953262cb2608e8db91e88e Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:18:14 +0330 Subject: [PATCH 106/188] test(linux): include expected empty selective restore in owned mock --- tests/tailscale_cleanup_ownership_contract.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tailscale_cleanup_ownership_contract.py b/tests/tailscale_cleanup_ownership_contract.py index 7abf76a0..ef28c56e 100644 --- a/tests/tailscale_cleanup_ownership_contract.py +++ b/tests/tailscale_cleanup_ownership_contract.py @@ -112,6 +112,7 @@ def f(name): f'NFQWS=/nonexistent/nfqws\n' + ('pid_owned(){ return 0; }\nkill(){ touch "$MOCK_KILLS"; }\n' if name=="split_coexistence_foreign_extra_rule" else 'pid_owned(){ return 1; }\n') + 'remove_dns_link(){ echo UNEXPECTED_DNS_LINK_REMOVAL >&2; return 89; }\n' + 'restore_selective_dns(){ test ! -e /run/directinternetmethod-selective/session.json; }\n' +functions+ '\ncleanup_state_owned\n' 'rc=$?\n' From df2cd6070b567190d2b140a2cc1bc1d8c579e78c Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:18:23 +0330 Subject: [PATCH 107/188] fix(security): resolve verified curl from OS SystemDirectory, not folder-path input (R057) --- tests/probe_process_security/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/probe_process_security/Program.cs b/tests/probe_process_security/Program.cs index 76edc361..e29c369a 100644 --- a/tests/probe_process_security/Program.cs +++ b/tests/probe_process_security/Program.cs @@ -37,7 +37,7 @@ void Reject(string label, string url, string ip) { var plan = ProbeProcessBuilder.Create(valid, physical); var expected = Path.GetFullPath(Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.System), "curl.exe")); + Environment.SystemDirectory, "curl.exe")); Need(string.Equals(plan.FileName, expected, StringComparison.OrdinalIgnoreCase), "Executable is not the OS system curl"); Need(Path.IsPathFullyQualified(plan.FileName), "Executable path not absolute"); From 4f44cf807575e7660bd0d68a45dbcc4f040f37a7 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:19:14 +0330 Subject: [PATCH 108/188] test(linux): emulate no-op selected DNS restoration in rollback mock --- tests/tailscale_transient_rollback_ownership_contract.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tailscale_transient_rollback_ownership_contract.py b/tests/tailscale_transient_rollback_ownership_contract.py index c568dcfd..cad32591 100644 --- a/tests/tailscale_transient_rollback_ownership_contract.py +++ b/tests/tailscale_transient_rollback_ownership_contract.py @@ -84,6 +84,7 @@ def fragment(name,optional=False): CPID=0 pid_owned(){{ return 1; }} remove_dns_link(){{ echo "UNEXPECTED_REMOVE_DNS" >&2;return 90; }} +restore_selective_dns(){ test ! -e /run/directinternetmethod-selective/session.json; } '''+real_fragment+''' cleanup_transient rc=$? From dfe20e0aa7c49e24acea3205bb0a50d4966d051c Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:20:17 +0330 Subject: [PATCH 109/188] test(linux): escape Bash braces in transient rollback fixture --- tests/tailscale_transient_rollback_ownership_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tailscale_transient_rollback_ownership_contract.py b/tests/tailscale_transient_rollback_ownership_contract.py index cad32591..db4f74b4 100644 --- a/tests/tailscale_transient_rollback_ownership_contract.py +++ b/tests/tailscale_transient_rollback_ownership_contract.py @@ -84,7 +84,7 @@ def fragment(name,optional=False): CPID=0 pid_owned(){{ return 1; }} remove_dns_link(){{ echo "UNEXPECTED_REMOVE_DNS" >&2;return 90; }} -restore_selective_dns(){ test ! -e /run/directinternetmethod-selective/session.json; } +restore_selective_dns(){{ test ! -e /run/directinternetmethod-selective/session.json; }} '''+real_fragment+''' cleanup_transient rc=$? From 4a9d2e3bdb16e2d669aa9f669f417aca18c44c7f Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:23:32 +0330 Subject: [PATCH 110/188] test(linux): model empty selective-DNS restore in isolated nft namespace --- tests/linux_nft_kernel_namespace_contract.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/linux_nft_kernel_namespace_contract.py b/tests/linux_nft_kernel_namespace_contract.py index 3ebf4a72..648da2bf 100644 --- a/tests/linux_nft_kernel_namespace_contract.py +++ b/tests/linux_nft_kernel_namespace_contract.py @@ -96,6 +96,7 @@ def kernel_script() -> str: NFQWS="$TMP/not-nfqws" pid_owned(){ return 1; } remove_dns_link(){ echo "UNEXPECTED_DNS_LINK" >&2; return 99; } +restore_selective_dns(){ test ! -e /run/directinternetmethod-selective/session.json; } put_state(){ printf '%s\n' '{"dnsMode":"system-preserved","physicalInterface":"enp1s0","ctrldPid":0,"nfqwsPid":0}' > "$STATE" From 24139d9918d36ba1276765f160212474d27484f2 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:24:31 +0330 Subject: [PATCH 111/188] test(linux): export isolated rollback DNS fixture to trap child --- tests/linux_nft_kernel_namespace_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/linux_nft_kernel_namespace_contract.py b/tests/linux_nft_kernel_namespace_contract.py index 648da2bf..319d2129 100644 --- a/tests/linux_nft_kernel_namespace_contract.py +++ b/tests/linux_nft_kernel_namespace_contract.py @@ -191,7 +191,7 @@ def kernel_script() -> str: new_table; new_chain; injected_mark; mark; queues nft add rule inet "$TABLE" output ip daddr 203.0.113.7 drop export TABLE PHY TAILSCALE_COEXIST CREATED_TABLE CREATED_LINK CREATED_STATE RUN_HOSTS NPID CPID CTRLD NFQWS -export -f rollback_on_exit cleanup_transient nft_table_presence nft_table_transient_owned pid_owned remove_dns_link +export -f rollback_on_exit cleanup_transient nft_table_presence nft_table_transient_owned pid_owned remove_dns_link restore_selective_dns if trap_output="$(bash -c 'set -euo pipefail; ACTION=start; START_COMMITTED=0; trap rollback_on_exit EXIT; exit 0' 2>&1)";then trap_rc=0 else From 8f31bdc0f15b503a82c2f1215082863f11744e54 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:40:18 +0330 Subject: [PATCH 112/188] fix(linux): avoid package-source pyc cache during in-place update --- .github/workflows/ci.yml | 21 +++++----- linux/install.sh | 3 ++ tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ...09_installer_no_bytecode_cache_contract.py | 38 +++++++++++++++++++ 7 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 tests/r709_installer_no_bytecode_cache_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb9669c6..4537403d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332" - assert p.is_file() and p.stat().st_size==8371711 + expected="BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146" + assert p.is_file() and p.stat().st_size==8371898 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -89,6 +89,7 @@ jobs: python3 -B tests/r678_linux_gui_full_youtube_health_contract.py python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py python3 -B tests/r702_nfqws_reinjected_mark_contract.py + python3 -B tests/r709_installer_no_bytecode_cache_contract.py python3 -B tests/tailscale_nft_ownership_contract.py python3 -B tests/tailscale_cleanup_ownership_contract.py python3 -B tests/tailscale_transient_rollback_ownership_contract.py @@ -160,7 +161,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -189,7 +190,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -222,7 +223,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -251,7 +252,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -280,7 +281,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -310,7 +311,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -355,7 +356,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -381,7 +382,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/install.sh b/linux/install.sh index 1be18005..0baa1001 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -euo pipefail umask 077 +# The source archive must remain read-only/clean during an in-place update. +# Python imports run from this directory; never accumulate __pycache__ files. +export PYTHONDONTWRITEBYTECODE=1 HERE="$(cd "$(dirname "$0")" && pwd)" APP="$HOME/.local/share/DirectInternetMethod" BIN="$HOME/.local/bin" diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 36788d8f..d7b14fb3 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332"} +EXPECTED={"version":VERSION,"files":27,"sha256":"BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -85,7 +85,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self changed={name for name in before if before[name]!=after[name]} self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", - "system/upgrade_system.sh"}) + "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], "D3A9B4D6F8D75C41FC2D3A12B1376BEB305E7388F852192FA6DAE2D65C5CBDCA") self.assertIn("encrypted=encrypted_dns_optin_state()", diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index ec4e3a1e..a1ae51a0 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="c1e644e53ca3eb106b6dca5ed942a2ecb2052fb636ff0e60fd9e5a5b0783e332" +CANDIDATE_SHA="bbcfc4ab1c3688c4e07d3bdcc7edea65a104ce32b659d52f22db1aeb559e6146" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 849c558c..dba27e0e 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332" +PIN_PACKAGE="BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146" PIN_ROOT="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" PIN_GUARD="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 8f50da71..65e73e89 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="C1E644E53CA3EB106B6DCA5ED942A2ECB2052FB636FF0E60FD9E5A5B0783E332" +ARCHIVE="BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r709_installer_no_bytecode_cache_contract.py b/tests/r709_installer_no_bytecode_cache_contract.py new file mode 100644 index 00000000..b17984ff --- /dev/null +++ b/tests/r709_installer_no_bytecode_cache_contract.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""R709 install.sh prevents its Python helper from leaving staged .pyc cache. + +Negative-first exact source prefix plus rootless interpreter probe. This test +never runs the real installer, pkexec, nft, systemctl, DNS or network utilities. +""" +from pathlib import Path +import os +import subprocess +import tempfile +import sys +import shlex + +ROOT=Path(__file__).resolve().parents[1] +installer=(ROOT/"linux/install.sh").read_text(encoding="utf-8-sig") +needle="export PYTHONDONTWRITEBYTECODE=1" +assert installer.count(needle)==1,"USER_INSTALLER_BYTECODE_OPT_OUT_NOT_UNIQUE" +prefix=installer.split('HERE="$(cd "$(dirname "$0")" && pwd)"',1) +assert len(prefix)==2,"INSTALLER_SOURCE_PREFIX_CHANGED" +assert needle in prefix[0] and prefix[0].index(needle)>prefix[0].index("umask 077") +assert 'pkexec /bin/bash "$HERE/system/upgrade_system.sh"' in installer,"PROTECTED_ROOT_AUTH_REMOVED" +assert 'R519_CANDIDATE_PENDING_NATIVE_ROOT_USER_COMMIT' in installer,"UNCOMMITTED_SAFETY_SEMANTICS_CHANGED" +assert 'python3 "$USER_TRANSACTION" --rollback' in installer,"USER_ROLLBACK_REMOVED" +if sys.platform.startswith("linux"): + with tempfile.TemporaryDirectory(prefix="dim-r709-no-bytecode-") as td: + path=Path(td) + module=path/"dim_r709_probe.py" + module.write_text("VALUE='ok'\n",encoding="utf-8") + pycode='import dim_r709_probe; assert dim_r709_probe.VALUE=="ok"' + script="set -euo pipefail\n"+prefix[0]+"\nPYTHONPATH="+shlex.quote(td)+" python3 -c "+shlex.quote(pycode)+"\n" + env=os.environ.copy() + env.pop("PYTHONDONTWRITEBYTECODE",None) + p=subprocess.run(["bash","-c",script],text=True,capture_output=True, + check=False,timeout=10,env=env) + assert p.returncode==0,(p.stdout,p.stderr) + assert not (path/"__pycache__").exists(),"CANDIDATE_EXTRACTED_PATH_WOULD_ACCUMULATE_PYC" + print("R709_NATIVE_INSTALLER_EXPORTED_ENV_IMPORT_NO_PYC_CACHE=PASS") +print("R709_INSTALLER_SOURCE_GUARDED_NO_BYTECODE_CACHE=PASS") From d0cff22ae156b1197d9a709577c683345064a7be Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:45:46 +0330 Subject: [PATCH 113/188] fix(service): pin updater curl to Windows SystemDirectory (R061) --- windows/service/Program.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/windows/service/Program.cs b/windows/service/Program.cs index ee5f3ca0..7571745d 100644 --- a/windows/service/Program.cs +++ b/windows/service/Program.cs @@ -259,7 +259,12 @@ static string GetRegisteredInstallDirectory() static async Task DownloadWithSystemCurlAsync(string url, string destination, CancellationToken ct) { - var curl = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "curl.exe"); + // Resolve the updater helper exclusively from the Windows-owned system directory. + // Never let caller PATH or a resolved folder string choose the executable. + var systemFolder = Environment.SystemDirectory; + if (string.IsNullOrWhiteSpace(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) + throw new InvalidOperationException("Trusted Windows system directory unavailable."); + var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); if (!File.Exists(curl)) throw new FileNotFoundException("Windows curl.exe is unavailable.", curl); From 3fd7e8260535788cd9982b0fa7633b602422895d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:46:12 +0330 Subject: [PATCH 114/188] test(service): fail closed on unsafe curl execution provenance (R061) --- tests/r061_windows_service_curl_contract.py | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/r061_windows_service_curl_contract.py diff --git a/tests/r061_windows_service_curl_contract.py b/tests/r061_windows_service_curl_contract.py new file mode 100644 index 00000000..41102f80 --- /dev/null +++ b/tests/r061_windows_service_curl_contract.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""R061 no-execution Windows service curl provenance contract, with negative fixtures.""" +import pathlib +import re +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +SOURCE = ROOT / "windows" / "service" / "Program.cs" + + +def service_method(text): + match = re.search( + r"static async Task DownloadWithSystemCurlAsync\b[\s\S]*?(?=\n static async Task RunUpdateAsync\b)", + text, + ) + if not match: + raise AssertionError("R061_DOWNLOAD_METHOD_NOT_FOUND") + return match.group(0) + + +def require_contract(text): + method = service_method(text) + required = ( + "Environment.SystemDirectory", + "string.IsNullOrWhiteSpace(systemFolder)", + "Path.IsPathFullyQualified(systemFolder)", + 'Path.GetFullPath(Path.Combine(systemFolder, "curl.exe"))', + "if (!File.Exists(curl))", + "new ProcessStartInfo(curl)", + "UseShellExecute = false", + "psi.ArgumentList.Add(arg)", + "using var p = Process.Start(psi)", + "await p.WaitForExitAsync(ct)", + ) + for fragment in required: + if fragment not in method: + raise AssertionError("R061_MISSING_" + fragment) + forbidden = ( + "Environment.GetFolderPath(", + "Environment.GetEnvironmentVariable(", + 'new ProcessStartInfo("curl.exe")', + "UseShellExecute = true", + "psi.Arguments =", + 'Process.Start("curl.exe"', + ) + for fragment in forbidden: + if fragment in method: + raise AssertionError("R061_UNSAFE_" + fragment) + if method.index("Path.IsPathFullyQualified(systemFolder)") > method.index("new ProcessStartInfo(curl)"): + raise AssertionError("R061_GUARD_AFTER_PROCESS_PLAN") + if method.index("if (!File.Exists(curl))") > method.index("new ProcessStartInfo(curl)"): + raise AssertionError("R061_EXISTENCE_AFTER_PROCESS_PLAN") + return True + + +class TestSystemCurlProvenance(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.src = SOURCE.read_text(encoding="utf-8") + + def test_valid_service_source(self): + self.assertTrue(require_contract(self.src)) + + def test_fail_closed_to_old_special_folder_lookup(self): + text = self.src.replace( + "Environment.SystemDirectory", + "Environment.GetFolderPath(Environment.SpecialFolder.System)", + ) + with self.assertRaises(AssertionError): + require_contract(text) + + def test_fail_closed_to_path_lookup(self): + text = self.src.replace("Environment.SystemDirectory", 'Environment.GetEnvironmentVariable("PATH")') + with self.assertRaises(AssertionError): + require_contract(text) + + def test_fail_closed_if_absolute_path_guard_removed(self): + text = self.src.replace("!Path.IsPathFullyQualified(systemFolder)", "false") + with self.assertRaises(AssertionError): + require_contract(text) + + def test_fail_closed_if_process_shell_enabled(self): + method = service_method(self.src).replace("UseShellExecute = false", "UseShellExecute = true") + with self.assertRaises(AssertionError): + require_contract(self.src.replace(service_method(self.src), method)) + + def test_fail_closed_to_non_tokenized_arguments(self): + method = service_method(self.src).replace("psi.ArgumentList.Add(arg)", "psi.Arguments = arg") + with self.assertRaises(AssertionError): + require_contract(self.src.replace(service_method(self.src), method)) + + def test_fail_closed_to_bare_executable(self): + method = service_method(self.src).replace("new ProcessStartInfo(curl)", 'new ProcessStartInfo("curl.exe")') + with self.assertRaises(AssertionError): + require_contract(self.src.replace(service_method(self.src), method)) + + def test_checksum_still_verified_before_installer_execution(self): + source = self.src + self.assertIn("await Sha256FileAsync(partPath, timeout.Token)", source) + self.assertIn("actual.Equals(expected, StringComparison.OrdinalIgnoreCase)", source) + self.assertIn("var psi = new ProcessStartInfo(finalPath)", source) + self.assertLess( + source.index("await Sha256FileAsync(partPath, timeout.Token)"), + source.index("var psi = new ProcessStartInfo(finalPath)"), + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) From 21cc8dd1e834c462c244e25f10d24479f1f42ab2 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:46:33 +0330 Subject: [PATCH 115/188] ci(security): enforce Windows service curl provenance negative fixtures (R061) --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d086f07c..821ac2c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,6 +184,12 @@ jobs: $ErrorActionPreference='Stop' dotnet run --project tests/probe_process_security/ProbeProcessSecurity.csproj -c Release --no-launch-profile if($LASTEXITCODE -ne 0){throw 'R057_GUI_PROBE_SECURITY_REGRESSION_FAILED'} + - name: R061 Windows service system curl provenance negative fixtures + shell: pwsh + run: | + $ErrorActionPreference='Stop' + python -B tests/r061_windows_service_curl_contract.py + if($LASTEXITCODE -ne 0){throw 'R061_SERVICE_SYSTEM_CURL_SECURITY_REGRESSION_FAILED'} - name: Restore pinned PowerShell runtime shell: pwsh run: pwsh.exe -NoProfile -ExecutionPolicy Bypass -File tests/prepare_powershell_runtime.ps1 From 3a5b57e3556b60989c36c1d22bd81baafde0a74d Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:59:51 +0330 Subject: [PATCH 116/188] fix(linux): bypass TCP443 pure SYN before nfqws TLS queue --- .github/workflows/ci.yml | 21 ++++----- linux/app/direct_internet_method.py | 2 +- linux/app/direct_method_helper.sh | 11 ++++- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/linux_nft_kernel_namespace_contract.py | 18 +++++++- tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r702_nfqws_reinjected_mark_contract.py | 2 +- tests/r711_tls_syn_fastpath_contract.py | 45 +++++++++++++++++++ tests/tailscale_cleanup_ownership_contract.py | 5 +++ tests/tailscale_nft_ownership_contract.py | 5 +++ ...e_transient_rollback_ownership_contract.py | 7 ++- 20 files changed, 115 insertions(+), 31 deletions(-) create mode 100644 tests/r711_tls_syn_fastpath_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4537403d..71fb330b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146" - assert p.is_file() and p.stat().st_size==8371898 + expected="8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4" + assert p.is_file() and p.stat().st_size==8372459 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -90,6 +90,7 @@ jobs: python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py python3 -B tests/r702_nfqws_reinjected_mark_contract.py python3 -B tests/r709_installer_no_bytecode_cache_contract.py + python3 -B tests/r711_tls_syn_fastpath_contract.py python3 -B tests/tailscale_nft_ownership_contract.py python3 -B tests/tailscale_cleanup_ownership_contract.py python3 -B tests/tailscale_transient_rollback_ownership_contract.py @@ -161,7 +162,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -190,7 +191,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -223,7 +224,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -252,7 +253,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -281,7 +282,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -311,7 +312,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -356,7 +357,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -382,7 +383,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index e0772473..dc77408f 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -48,7 +48,7 @@ SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="d4169f5be2c11290b018905f9ade16dc8d70dc16000ebab1a8013e3162796b15" +COEXIST_HELPER_SHA256="07622ee0592bd5843ad60c58a3535681ea57eb51951355ab2649a845b44bcac3" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index 38360605..70f18791 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -157,7 +157,7 @@ import re, sys table, iface, mode = sys.argv[1:] lines = [line.strip() for line in sys.stdin if line.strip()] coexist = mode == "system-preserved" -expected_count = 10 if coexist else 9 +expected_count = 11 if coexist else 10 if len(lines) != expected_count: sys.exit(1) if lines[:2] != [f"table inet {table} {{", "chain output {"]: @@ -175,6 +175,10 @@ if coexist: offset = 5 queue = r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? bypass)" dev = r"oifname (?:" + re.escape(iface) + r"|" + re.escape(chr(34) + iface + chr(34)) + r")" +syn_rule = dev + r" tcp dport 443 tcp flags & \(syn \| ack\) == syn return" +if not re.fullmatch(syn_rule, lines[offset]): + sys.exit(1) +offset += 1 for index, (proto, port) in enumerate((("tcp", 80), ("tcp", 443), ("udp", 443))): rule = dev + " " + proto + " dport " + str(port) + " " + queue if not re.fullmatch(rule, lines[offset + index]): @@ -313,6 +317,7 @@ queue=r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? patterns=[r"meta mark & 0x0*40000000 == 0x0*40000000 return"] if coexist=="1": patterns.append(r"meta mark & 0x0*ff0000 == 0x0*80000 return") +patterns.append(dev+r" tcp dport 443 tcp flags & \(syn \| ack\) == syn return") for proto,port in (("tcp",80),("tcp",443),("udp",443)): patterns.append(dev+r" "+proto+r" dport "+str(port)+r" "+queue) rules=inner[2:-1] @@ -812,6 +817,10 @@ EOF # Tailscale LinuxBypassMark 0x80000/0xff0000 protects its own DERP/control traffic. nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return" fi + # Original TCP SYN has no TLS payload. Let the kernel establish the + # connection without a potentially delayed NFQUEUE round-trip. Only + # physical WAN TCP/443 pure SYN is excluded; TLS data still reaches nfqws. + nft "add rule inet $TABLE output oifname \"$PHY\" tcp dport 443 tcp flags & (syn | ack) == syn return" nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 80 ct original packets 1-6 queue num "$QNUM" bypass nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 443 ct original packets 1-6 queue num "$QNUM" bypass nft add rule inet "$TABLE" output oifname "$PHY" udp dport 443 ct original packets 1-6 queue num "$QNUM" bypass diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index e4a9b7f6..b0304b9c 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -23,7 +23,7 @@ FILES=( ("helper","app/direct_method_helper.sh","usr/lib/directinternetmethod/direct_method_helper.sh", "E52FCAEB9C60D9E43D4A5B75462455AD8C5EE4D82FEE679A0CC4A3520D8ABEA5", - "D4169F5BE2C11290B018905F9ADE16DC8D70DC16000EBAB1A8013E3162796B15",0o755), + "07622EE0592BD5843AD60C58A3535681EA57EB51951355AB2649A845B44BCAC3",0o755), ("uninstall","system/uninstall_system.sh","usr/lib/directinternetmethod/uninstall_system.sh", "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index d8d4d905..e86bcc4e 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" +PIN_ROOT="25E0904648B726D2F01B8CD16590623F92F4CA198822CBBBC3C8113BA530AF1C" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 62cdfce5..6712a64a 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" +TX_SHA="25E0904648B726D2F01B8CD16590623F92F4CA198822CBBBC3C8113BA530AF1C" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" +GUARD_SHA="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/linux_nft_kernel_namespace_contract.py b/tests/linux_nft_kernel_namespace_contract.py index 319d2129..ab1c7307 100644 --- a/tests/linux_nft_kernel_namespace_contract.py +++ b/tests/linux_nft_kernel_namespace_contract.py @@ -115,6 +115,7 @@ def kernel_script() -> str: nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return" } queues(){ + nft "add rule inet $TABLE output oifname enp1s0 tcp dport 443 tcp flags & (syn | ack) == syn return" nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 80 ct original packets 1-6 queue num 200 bypass nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 443 ct original packets 1-6 queue num 200 bypass nft add rule inet "$TABLE" output oifname enp1s0 udp dport 443 ct original packets 1-6 queue num 200 bypass @@ -137,6 +138,21 @@ def kernel_script() -> str: verify_clean expect_absent echo "PASS_KERNEL_COMPLETE_OWNED_CLEANUP" +# A table without its SYN fast-path cannot be claimed for destructive cleanup. +put_state +new_table; new_chain; injected_mark; mark +nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 80 ct original packets 1-6 queue num 200 bypass +nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 443 ct original packets 1-6 queue num 200 bypass +nft add rule inet "$TABLE" output oifname enp1s0 udp dport 443 ct original packets 1-6 queue num 200 bypass +if nft_table_owned "$PHY"; then echo FALSE_SYNSKIP_MISSING_OWNERSHIP >&2; exit 94;fi +if cleanup_state_owned; then rc=0; else rc=$?;fi +test "$rc" -eq 81 +test -f "$STATE" +nft list table inet "$TABLE" >/dev/null +nft delete table inet "$TABLE" +expect_absent +echo "PASS_KERNEL_MISSING_TCP443_SYN_EXEMPTION_FAIL_CLOSED" + # A table lacking nfqws injected-packet exemption is NOT owned. Root must # preserve it rather than deleting a rule set that may loop reinjected packets. put_state @@ -206,7 +222,7 @@ def kernel_script() -> str: nft delete table inet "$TABLE" expect_absent echo "PASS_KERNEL_FAILED_ROLLBACK_EXIT_NONZERO" -echo "PASS_KERNEL_NAMESPACE_NFT_CONTRACT_8_OF_8" +echo "PASS_KERNEL_NAMESPACE_NFT_CONTRACT_9_OF_9" ''' script = functions + "\n" + cases # Fixture variables must be initialized before Bash set -u cleanup. diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index d7b14fb3..e926c694 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146"} +EXPECTED={"version":VERSION,"files":27,"sha256":"8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -87,7 +87,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "D3A9B4D6F8D75C41FC2D3A12B1376BEB305E7388F852192FA6DAE2D65C5CBDCA") + "8607D87599617058A4DA362A1CD140EE2DCBD3C3F23BD0A68083B06880486445") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index a1ae51a0..2fa4754b 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="bbcfc4ab1c3688c4e07d3bdcc7edea65a104ce32b659d52f22db1aeb559e6146" +CANDIDATE_SHA="8f975e52f006fe9105dac7d593f7a6eb67913b5b73c3aeef705ea11cccfcfcc4" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index a2a4f46f..582d5ce1 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "121d9151958901036a50c0c9ef93568136f1ee11433e0021b5496cf003240682" +ROOT_SHA = "25e0904648b726d2f01b8cd16590623f92f4ca198822cbbbc3c8113ba530af1c" USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 7a565778..76cfe3de 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "121d9151958901036a50c0c9ef93568136f1ee11433e0021b5496cf003240682" +PIN_ROOT = "25e0904648b726d2f01b8cd16590623f92f4ca198822cbbbc3c8113ba530af1c" PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index ba546c42..10b58fca 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" +SOURCE_SHA="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index dba27e0e..d3b47402 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146" -PIN_ROOT="121D9151958901036A50C0C9EF93568136F1EE11433E0021B5496CF003240682" -PIN_GUARD="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" +PIN_PACKAGE="8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4" +PIN_ROOT="25E0904648B726D2F01B8CD16590623F92F4CA198822CBBBC3C8113BA530AF1C" +PIN_GUARD="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index d85b7963..4536411b 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="868CBD5BA1425610BFAB724194A3CD6359D5AADCE6D64656BD2AEBEC865117B6" +PIN_GUARD="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 65e73e89..feb9a807 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="BBCFC4AB1C3688C4E07D3BDCC7EDEA65A104CE32B659D52F22DB1AEB559E6146" +ARCHIVE="8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r702_nfqws_reinjected_mark_contract.py b/tests/r702_nfqws_reinjected_mark_contract.py index 03b97019..cbec07ba 100644 --- a/tests/r702_nfqws_reinjected_mark_contract.py +++ b/tests/r702_nfqws_reinjected_mark_contract.py @@ -23,7 +23,7 @@ assert helper.count(owned)==1 and helper.count(transient)==1 live=helper.split(owned,1)[1].split('\n}\n',1)[0] rollback=helper.split(transient,1)[1].split('\n}\n',1)[0] -assert 'expected_count = 10 if coexist else 9' in live +assert 'expected_count = 11 if coexist else 10' in live assert 'offset = 4' in live assert 'lines[3]' in live and 'lines[4]' in live assert 'offset = 5' in live diff --git a/tests/r711_tls_syn_fastpath_contract.py b/tests/r711_tls_syn_fastpath_contract.py new file mode 100644 index 00000000..d12f578d --- /dev/null +++ b/tests/r711_tls_syn_fastpath_contract.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""R711 TCP443 original SYN packets bypass NFQUEUE before L7 nfqws queues. + +Negative-first source contract, no root, real netfilter, network or user state. +Ensures the SYN optimization is narrowly limited to original WAN TCP443 and +the independent owner-only Stop/Recovery paths recognize only exact rules. +""" +from pathlib import Path +import re + +BASE=Path(__file__).resolve().parents[1] +h=(BASE/"linux/app/direct_method_helper.sh").read_text(encoding="utf-8") +SYN='tcp flags & (syn | ack) == syn return' +def verify(yes, name): + if not yes:raise AssertionError("R711_"+name) +def fragment(name): + match=re.findall(r'^'+re.escape(name)+r'\(\)\{\n.*?^\}\n', + h,flags=re.S|re.M) + verify(len(match)==1,"SHELL_FRAGMENT_NOT_EXACT_"+name) + return match[0] + +start=fragment("start_engine") if "start_engine(){" in h else h +print("R711_SOURCE_CURRENT_HEALTH_TARGET_PATH") +rule='nft "add rule inet $TABLE output oifname \\"$PHY\\" tcp dport 443 '+SYN+'"' +verify(h.count(rule)==1,"SYNSKIP_RULE_NOT_UNIQUE") +a=h.index('nft "add rule inet $TABLE output meta mark & 0x40000000 == 0x40000000 return"') +b=h.index('nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return"') +c=h.index(rule) +queue=h.index('nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 443 ct original packets 1-6 queue num "$QNUM" bypass') +verify(a Date: Sat, 17 Oct 2026 09:02:36 +0330 Subject: [PATCH 117/188] test(linux): show native nft table on strict SYN ownership mismatch --- tests/linux_nft_kernel_namespace_contract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/linux_nft_kernel_namespace_contract.py b/tests/linux_nft_kernel_namespace_contract.py index ab1c7307..02bec9a3 100644 --- a/tests/linux_nft_kernel_namespace_contract.py +++ b/tests/linux_nft_kernel_namespace_contract.py @@ -133,7 +133,11 @@ def kernel_script() -> str: put_state new_table; new_chain; injected_mark; mark; queues nft_table_presence -nft_table_owned "$PHY" +if ! nft_table_owned "$PHY"; then + echo "R711_FAIL_CLOSED_NATIVE_COMPLETE_OWNER_NO_MATCH" >&2 + nft list table inet "$TABLE" >&2 + exit 97 +fi cleanup_state_owned verify_clean expect_absent From 46b0724f565df20f81f0a6b3c1c9537c11a51c3c Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:08:33 +0330 Subject: [PATCH 118/188] fix(linux): recognize canonical nft TCP SYN flags in owner guard --- .github/workflows/ci.yml | 20 +++++++++---------- linux/app/direct_internet_method.py | 2 +- linux/app/direct_method_helper.sh | 4 ++-- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 4 ++-- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r711_tls_syn_fastpath_contract.py | 6 +++--- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71fb330b..1d060d21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4" - assert p.is_file() and p.stat().st_size==8372459 + expected="238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0" + assert p.is_file() and p.stat().st_size==8372495 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -162,7 +162,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -191,7 +191,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -224,7 +224,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -253,7 +253,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -282,7 +282,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -312,7 +312,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -357,7 +357,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -383,7 +383,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index dc77408f..26092283 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -48,7 +48,7 @@ SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="07622ee0592bd5843ad60c58a3535681ea57eb51951355ab2649a845b44bcac3" +COEXIST_HELPER_SHA256="a137f7c2924037486aea8b7ec1a41556a374868c282f846f84ae0fc368e84b8b" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index 70f18791..37692d00 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -175,7 +175,7 @@ if coexist: offset = 5 queue = r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? bypass)" dev = r"oifname (?:" + re.escape(iface) + r"|" + re.escape(chr(34) + iface + chr(34)) + r")" -syn_rule = dev + r" tcp dport 443 tcp flags & \(syn \| ack\) == syn return" +syn_rule = dev + r" tcp dport 443 tcp flags (?:syn / syn,ack|& \(syn \| ack\) == syn) return" if not re.fullmatch(syn_rule, lines[offset]): sys.exit(1) offset += 1 @@ -317,7 +317,7 @@ queue=r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? patterns=[r"meta mark & 0x0*40000000 == 0x0*40000000 return"] if coexist=="1": patterns.append(r"meta mark & 0x0*ff0000 == 0x0*80000 return") -patterns.append(dev+r" tcp dport 443 tcp flags & \(syn \| ack\) == syn return") +patterns.append(dev+r" tcp dport 443 tcp flags (?:syn / syn,ack|& \(syn \| ack\) == syn) return") for proto,port in (("tcp",80),("tcp",443),("udp",443)): patterns.append(dev+r" "+proto+r" dport "+str(port)+r" "+queue) rules=inner[2:-1] diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index b0304b9c..291e3652 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -23,7 +23,7 @@ FILES=( ("helper","app/direct_method_helper.sh","usr/lib/directinternetmethod/direct_method_helper.sh", "E52FCAEB9C60D9E43D4A5B75462455AD8C5EE4D82FEE679A0CC4A3520D8ABEA5", - "07622EE0592BD5843AD60C58A3535681EA57EB51951355AB2649A845B44BCAC3",0o755), + "A137F7C2924037486AEA8B7EC1A41556A374868C282F846F84AE0FC368E84B8B",0o755), ("uninstall","system/uninstall_system.sh","usr/lib/directinternetmethod/uninstall_system.sh", "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index e86bcc4e..c7401b3a 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="25E0904648B726D2F01B8CD16590623F92F4CA198822CBBBC3C8113BA530AF1C" +PIN_ROOT="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 6712a64a..b65e26a3 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="25E0904648B726D2F01B8CD16590623F92F4CA198822CBBBC3C8113BA530AF1C" +TX_SHA="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" +GUARD_SHA="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index e926c694..963cd020 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4"} +EXPECTED={"version":VERSION,"files":27,"sha256":"238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -87,7 +87,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "8607D87599617058A4DA362A1CD140EE2DCBD3C3F23BD0A68083B06880486445") + "BBB7B86B423B367DF58CF12E127180C7CA0FFC06BEC471E8B3B062A9D175963C") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 2fa4754b..127ec992 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="8f975e52f006fe9105dac7d593f7a6eb67913b5b73c3aeef705ea11cccfcfcc4" +CANDIDATE_SHA="238f479608bd43a2be3fdcd0afc4c9df4e23c243b25e31adf29bbac255a605e0" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 582d5ce1..eb388abc 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "25e0904648b726d2f01b8cd16590623f92f4ca198822cbbbc3c8113ba530af1c" +ROOT_SHA = "ba68fd4ab7581d86886dce25ed53df8c6be9df52eb2e6dc4c6925dac8e4a4635" USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 76cfe3de..fc4a457d 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "25e0904648b726d2f01b8cd16590623f92f4ca198822cbbbc3c8113ba530af1c" +PIN_ROOT = "ba68fd4ab7581d86886dce25ed53df8c6be9df52eb2e6dc4c6925dac8e4a4635" PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 10b58fca..f5bc647f 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" +SOURCE_SHA="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index d3b47402..2fb2d44e 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4" -PIN_ROOT="25E0904648B726D2F01B8CD16590623F92F4CA198822CBBBC3C8113BA530AF1C" -PIN_GUARD="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" +PIN_PACKAGE="238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0" +PIN_ROOT="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" +PIN_GUARD="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 4536411b..f2268dd5 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="31D4D9D8DB502FC60494E1C94897501729A4B3E8D4B44AA1E961C8472BB653D7" +PIN_GUARD="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index feb9a807..1331d584 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="8F975E52F006FE9105DAC7D593F7A6EB67913B5B73C3AEEF705EA11CCCFCFCC4" +ARCHIVE="238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r711_tls_syn_fastpath_contract.py b/tests/r711_tls_syn_fastpath_contract.py index d12f578d..4fdda987 100644 --- a/tests/r711_tls_syn_fastpath_contract.py +++ b/tests/r711_tls_syn_fastpath_contract.py @@ -32,10 +32,10 @@ def fragment(name): own=fragment("nft_table_owned") trans=fragment("nft_table_transient_owned") verify('expected_count = 11 if coexist else 10' in own,"COMPLETE_OWNED_ROW_COUNT") -verify('tcp flags & \\(syn \\| ack\\) == syn return' in own,"COMPLETE_OWNED_SYN_EXACT_MATCH") -verify('tcp flags & \\(syn \\| ack\\) == syn return' in trans,"TRANSIENT_OWNED_SYN_EXACT_MATCH") +verify('tcp flags (?:syn / syn,ack|&' in own,"COMPLETE_OWNED_SYN_EXACT_MATCH") +verify('tcp flags (?:syn / syn,ack|&' in trans,"TRANSIENT_OWNED_SYN_EXACT_MATCH") verify('patterns.append(' in trans and - trans.index('tcp flags & \\(syn \\| ack\\)')< + trans.index('tcp flags (?:syn / syn,ack|&')< trans.index('for proto,port in'),"TRANSIENT_SEQUENCE") verify('meta mark & 0x0*40000000' in own and 'meta mark & 0x0*ff0000' in own,"PRESERVE_MARKS") verify('ROLLBACK_FOREIGN_NFT_PRESERVED' in h,"FAIL_CLOSED_TRANSIENT") From c6c68de6a2100f78c3003d9decd849f1e0a801de Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:41:35 +0330 Subject: [PATCH 119/188] test(service): native hosted loopback curl with poisoned PATH, no installer (R062) --- tests/R062-NativeServiceCurl.ps1 | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/R062-NativeServiceCurl.ps1 diff --git a/tests/R062-NativeServiceCurl.ps1 b/tests/R062-NativeServiceCurl.ps1 new file mode 100644 index 00000000..e40cc848 --- /dev/null +++ b/tests/R062-NativeServiceCurl.ps1 @@ -0,0 +1,116 @@ +[CmdletBinding()] +param() +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# R062 exercises ONLY the compiled private downloader on a disposable GitHub Windows runner. +# Never run against the owner's workstation, installed service, production installer or VPN. +if ($env:GITHUB_ACTIONS -cne 'true' -or + $env:RUNNER_ENVIRONMENT -cne 'github-hosted' -or + $env:RUNNER_OS -cne 'Windows' -or + [string]::IsNullOrWhiteSpace($env:GITHUB_WORKSPACE) -or + [string]::IsNullOrWhiteSpace($env:RUNNER_TEMP) -or + [string]::IsNullOrWhiteSpace($env:GITHUB_SHA) -or + $env:COMPUTERNAME -match '(?i)EMAD-PC') { + throw 'R062_ONLY_DISPOSABLE_GITHUB_WINDOWS_RUNNER' +} + +$service = Join-Path $env:GITHUB_WORKSPACE 'windows/service/bin/Release/net10.0-windows/DirectInternetMethod.Service.dll' +if (!(Test-Path -LiteralPath $service -PathType Leaf)) { + throw 'R062_EXACT_COMPILED_SERVICE_ASSEMBLY_MISSING' +} +$root = [IO.Path]::GetFullPath($env:RUNNER_TEMP) +$work = Join-Path $root ('R062_native_curl_' + [Guid]::NewGuid().ToString('N')) +if (Test-Path -LiteralPath $work) { throw 'R062_TEMP_PATH_ALREADY_EXISTS' } +[IO.Directory]::CreateDirectory($work) | Out-Null + +$listener = $null +$client = $null +$cancel = $null +$oldPath = [Environment]::GetEnvironmentVariable('PATH', 'Process') +$oldNoProxy = [Environment]::GetEnvironmentVariable('NO_PROXY', 'Process') +try { + $fake = Join-Path $work 'untrusted-PATH-first' + [IO.Directory]::CreateDirectory($fake) | Out-Null + # Deliberately invalid Windows executable. If PATH selected it, the test MUST fail. + [IO.File]::WriteAllBytes((Join-Path $fake 'curl.exe'), [byte[]]@(0x4D, 0x5A, 0x90, 0x00)) + + $listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0) + $listener.Start(1) + $port = ([Net.IPEndPoint]$listener.LocalEndpoint).Port + $uri = 'http://127.0.0.1:' + $port + '/r062-exact-service-fixture' + $destination = Join-Path $work 'pinned-loopback-fixture.txt' + $body = [Text.Encoding]::UTF8.GetBytes('R062_NATIVE_SYSTEM_CURL_' + $env:GITHUB_SHA + [Environment]::NewLine) + $expectedSha = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($body)) + $incoming = $listener.AcceptTcpClientAsync() + + [Environment]::SetEnvironmentVariable('PATH', ($fake + ';' + $oldPath), 'Process') + [Environment]::SetEnvironmentVariable('NO_PROXY', '127.0.0.1,localhost', 'Process') + + $assembly = [Reflection.Assembly]::LoadFile([IO.Path]::GetFullPath($service)) + $type = $assembly.GetType('DirectInternetMethod.Service.Program', $true) + $binding = [Reflection.BindingFlags]'Static,NonPublic' + $download = $type.GetMethod('DownloadWithSystemCurlAsync', $binding) + if ($null -eq $download) { throw 'R062_EXACT_PRIVATE_DOWNLOADER_MISSING' } + + $cancel = [Threading.CancellationTokenSource]::new([TimeSpan]::FromSeconds(25)) + # The service main entrypoint / Windows SCM are NOT invoked. + $task = $download.Invoke($null, [object[]]@($uri, $destination, $cancel.Token)) + if ($task -isnot [Threading.Tasks.Task]) { throw 'R062_DOWNLOADER_NOT_ASYNC_TASK' } + + $client = $incoming.WaitAsync([TimeSpan]::FromSeconds(15)).GetAwaiter().GetResult() + $client.ReceiveTimeout = 12000 + $stream = $client.GetStream() + $buffer = [byte[]]::new(4096) + $count = $stream.Read($buffer, 0, $buffer.Length) + if ($count -le 0) { throw 'R062_LOOPBACK_EMPTY_HTTP_REQUEST' } + $request = [Text.Encoding]::ASCII.GetString($buffer, 0, $count) + if (!$request.StartsWith('GET /r062-exact-service-fixture HTTP/1.1')) { + throw 'R062_UNEXPECTED_HTTP_METHOD_OR_PATH' + } + + $crlf = [string][char]13 + [string][char]10 + $headers = [Text.Encoding]::ASCII.GetBytes( + 'HTTP/1.1 200 OK' + $crlf + + 'Content-Type: text/plain' + $crlf + + ('Content-Length: ' + $body.Length) + $crlf + + 'Connection: close' + $crlf + $crlf) + $stream.Write($headers, 0, $headers.Length) + $stream.Write($body, 0, $body.Length) + $stream.Flush() + $client.Close() + $client = $null + + $task.WaitAsync([TimeSpan]::FromSeconds(25)).GetAwaiter().GetResult() + if (!(Test-Path -LiteralPath $destination -PathType Leaf)) { + throw 'R062_SERVICE_CURL_DID_NOT_WRITE_FIXTURE' + } + $actualSha = (Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash + if ($actualSha -cne $expectedSha) { throw 'R062_FIXTURE_DIGEST_MISMATCH' } + if (!(Test-Path -LiteralPath (Join-Path [Environment]::SystemDirectory 'curl.exe') -PathType Leaf)) { + throw 'R062_WINDOWS_SYSTEM_CURL_UNAVAILABLE' + } + $receipt = [ordered]@{ + State = 'PASS_R062_GITHUB_HOSTED_NATIVE_DOWNLOADER' + ExactCheckedOutSha = $env:GITHUB_SHA + NativeServiceDownloaderInvoked = $true + WindowsServiceInstalledOrStarted = $false + CurlChosenFromUntrustedPATH = $false + LoopbackOnlyTransport = $true + PinnedFixtureSha256Matched = $true + DownloadedBytes = $body.Length + PhysicalNetworkOrVPNModified = $false + ProductionInstallerExecuted = $false + } + $receipt | ConvertTo-Json -Compress +} +finally { + if ($null -ne $cancel) { try { $cancel.Cancel() } catch {} ; $cancel.Dispose() } + if ($null -ne $client) { try { $client.Close() } catch {} } + if ($null -ne $listener) { try { $listener.Stop() } catch {} } + [Environment]::SetEnvironmentVariable('PATH', $oldPath, 'Process') + [Environment]::SetEnvironmentVariable('NO_PROXY', $oldNoProxy, 'Process') + if (Test-Path -LiteralPath $work) { + Remove-Item -LiteralPath $work -Recurse -Force + } +} From 89c5a349151f397436439a25a41ea8f2a8320c41 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:42:03 +0330 Subject: [PATCH 120/188] ci(service): verify system curl on disposable Windows loopback with PATH decoy (R062) --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 821ac2c7..3f17c585 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -190,6 +190,12 @@ jobs: $ErrorActionPreference='Stop' python -B tests/r061_windows_service_curl_contract.py if($LASTEXITCODE -ne 0){throw 'R061_SERVICE_SYSTEM_CURL_SECURITY_REGRESSION_FAILED'} + - name: R062 hosted Windows native service downloader loopback and PATH-poison guard + shell: pwsh + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/R062-NativeServiceCurl.ps1 + if($LASTEXITCODE -ne 0){throw 'R062_NATIVE_SERVICE_CURL_LOOPBACK_TEST_FAILED'} - name: Restore pinned PowerShell runtime shell: pwsh run: pwsh.exe -NoProfile -ExecutionPolicy Bypass -File tests/prepare_powershell_runtime.ps1 From c0d17627e0130dd10e22e015edcdd2b595192c22 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:46:36 +0330 Subject: [PATCH 121/188] test(service): pass CLR strings to reflection on hosted PowerShell (R062) --- tests/R062-NativeServiceCurl.ps1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/R062-NativeServiceCurl.ps1 b/tests/R062-NativeServiceCurl.ps1 index e40cc848..4b44e2d1 100644 --- a/tests/R062-NativeServiceCurl.ps1 +++ b/tests/R062-NativeServiceCurl.ps1 @@ -55,7 +55,15 @@ try { $cancel = [Threading.CancellationTokenSource]::new([TimeSpan]::FromSeconds(25)) # The service main entrypoint / Windows SCM are NOT invoked. - $task = $download.Invoke($null, [object[]]@($uri, $destination, $cancel.Token)) + # Reflection.Invoke must receive actual CLR strings, not PowerShell PSObject wrappers. + $nativeArgs = [object[]]::new(3) + $nativeArgs[0] = [string][Management.Automation.PSObject]::Base($uri) + $nativeArgs[1] = [string][Management.Automation.PSObject]::Base($destination) + $nativeArgs[2] = $cancel.Token + if ($nativeArgs[0] -isnot [string] -or $nativeArgs[1] -isnot [string]) { + throw 'R062_REFLECTION_CLR_STRINGS_NOT_NORMALIZED' + } + $task = $download.Invoke($null, $nativeArgs) if ($task -isnot [Threading.Tasks.Task]) { throw 'R062_DOWNLOADER_NOT_ASYNC_TASK' } $client = $incoming.WaitAsync([TimeSpan]::FromSeconds(15)).GetAwaiter().GetResult() From f50b5ffa8a9158869c2c5b61aef4b75a709bdb75 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:50:47 +0330 Subject: [PATCH 122/188] test(service): hosted .NET10 typed native curl loopback regression (R062) --- tests/service_curl_native/ServiceCurlNative.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/service_curl_native/ServiceCurlNative.csproj diff --git a/tests/service_curl_native/ServiceCurlNative.csproj b/tests/service_curl_native/ServiceCurlNative.csproj new file mode 100644 index 00000000..ef4ebbbc --- /dev/null +++ b/tests/service_curl_native/ServiceCurlNative.csproj @@ -0,0 +1,8 @@ + + + Exe + net10.0-windows + enable + enable + + From 621ddb62404a4d68766ee2a6ec726d21997bf586 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:50:58 +0330 Subject: [PATCH 123/188] test(service): hosted .NET10 typed native curl loopback regression (R062) --- tests/service_curl_native/Program.cs | 118 +++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/service_curl_native/Program.cs diff --git a/tests/service_curl_native/Program.cs b/tests/service_curl_native/Program.cs new file mode 100644 index 00000000..43f13eab --- /dev/null +++ b/tests/service_curl_native/Program.cs @@ -0,0 +1,118 @@ +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +// An exact-source, real native downloader regression. It runs ONLY on disposable +// GitHub-hosted Windows runners. No service is registered and no installer is run. +if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") != "true" || + Environment.GetEnvironmentVariable("RUNNER_ENVIRONMENT") != "github-hosted" || + Environment.GetEnvironmentVariable("RUNNER_OS") != "Windows" || + string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("RUNNER_TEMP")) || + string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_WORKSPACE")) || + string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_SHA")) || + (Environment.GetEnvironmentVariable("COMPUTERNAME") ?? "") + .Contains("EMAD-PC", StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException("R062_ONLY_DISPOSABLE_GITHUB_WINDOWS_RUNNER"); + +var root = Environment.GetEnvironmentVariable("GITHUB_WORKSPACE")!; +var service = Path.Combine(root, "windows", "service", "bin", "Release", + "net10.0-windows", "DirectInternetMethod.Service.dll"); +if (!File.Exists(service)) + throw new InvalidOperationException("R062_COMPILED_SERVICE_MISSING"); + +var tempRoot = Path.GetFullPath(Environment.GetEnvironmentVariable("RUNNER_TEMP")!); +var sandbox = Path.Combine(tempRoot, "R062_" + Guid.NewGuid().ToString("N")); +if (Directory.Exists(sandbox)) + throw new InvalidOperationException("R062_UNEXPECTED_SANDBOX_COLLISION"); +Directory.CreateDirectory(sandbox); +var oldPath = Environment.GetEnvironmentVariable("PATH"); +var oldNoProxy = Environment.GetEnvironmentVariable("NO_PROXY"); +var listener = new TcpListener(IPAddress.Loopback, 0); +using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(25)); +try +{ + var decoy = Path.Combine(sandbox, "untrusted-PATH-first"); + Directory.CreateDirectory(decoy); + // This is deliberately NOT a runnable Windows executable. + // Any command search via PATH must cause the test to fail. + File.WriteAllBytes(Path.Combine(decoy, "curl.exe"), + new byte[] { 0x4D, 0x5A, 0x90, 0x00 }); + + listener.Start(1); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; + var uri = "http://127.0.0.1:" + port + "/r062-exact-service-fixture"; + var download = Path.Combine(sandbox, "immutable-fixture.txt"); + var expectedBytes = Encoding.UTF8.GetBytes( + "R062_NATIVE_SERVICE_CURL_" + Environment.GetEnvironmentVariable("GITHUB_SHA") + "\n"); + var expectedSha = Convert.ToHexString(SHA256.HashData(expectedBytes)); + + Environment.SetEnvironmentVariable("PATH", decoy + Path.PathSeparator + oldPath); + Environment.SetEnvironmentVariable("NO_PROXY", "127.0.0.1,localhost"); + + var assembly = Assembly.LoadFrom(Path.GetFullPath(service)); + var type = assembly.GetType("DirectInternetMethod.Service.Program", throwOnError: true) + ?? throw new InvalidOperationException("R062_SERVICE_TYPE_NOT_FOUND"); + var method = type.GetMethod("DownloadWithSystemCurlAsync", + BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("R062_DOWNLOADER_METHOD_NOT_FOUND"); + + // Native CLR arguments: no PowerShell/PSObject reflection conversions. + var task = method.Invoke(null, new object?[] { uri, download, deadline.Token }) as Task + ?? throw new InvalidOperationException("R062_PRIVATE_METHOD_NOT_TASK"); + using (var client = await listener.AcceptTcpClientAsync(deadline.Token)) + { + await using var stream = client.GetStream(); + var buffer = new byte[4096]; + var received = await stream.ReadAsync(buffer.AsMemory(), deadline.Token); + if (received <= 0) + throw new InvalidOperationException("R062_EMPTY_LOOPBACK_REQUEST"); + var requestLine = Encoding.ASCII.GetString(buffer, 0, received); + if (!requestLine.StartsWith("GET /r062-exact-service-fixture HTTP/1.1", + StringComparison.Ordinal)) + throw new InvalidOperationException("R062_UNEXPECTED_HTTP_REQUEST"); + + const string crlf = "\r\n"; + var headers = Encoding.ASCII.GetBytes( + "HTTP/1.1 200 OK" + crlf + + "Content-Type: text/plain" + crlf + + "Content-Length: " + expectedBytes.Length + crlf + + "Connection: close" + crlf + crlf); + await stream.WriteAsync(headers.AsMemory(), deadline.Token); + await stream.WriteAsync(expectedBytes.AsMemory(), deadline.Token); + await stream.FlushAsync(deadline.Token); + } + + await task.WaitAsync(TimeSpan.FromSeconds(25)); + if (!File.Exists(download)) + throw new InvalidOperationException("R062_DOWNLOADED_FIXTURE_MISSING"); + var actualSha = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(download))); + if (!string.Equals(expectedSha, actualSha, StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException("R062_EXACT_DOWNLOAD_DIGEST_MISMATCH"); + if (!File.Exists(Path.Combine(Environment.SystemDirectory, "curl.exe"))) + throw new InvalidOperationException("R062_OS_SYSTEM_CURL_MISSING"); + + Console.WriteLine(JsonSerializer.Serialize(new + { + State = "PASS_R062_HOSTED_NATIVE_SERVICE_DOWNLOADER", + SourceSha = Environment.GetEnvironmentVariable("GITHUB_SHA"), + TrustedExecutableUsedDespiteFakePATH = true, + LoopbackHttpOnly = true, + DownloadSha256Matched = true, + DownloadedBytes = expectedBytes.Length, + WindowsServiceStarted = false, + InstallerExecuted = false, + VPNOrSystemNetworkChanged = false + })); +} +finally +{ + deadline.Cancel(); + listener.Stop(); + Environment.SetEnvironmentVariable("PATH", oldPath); + Environment.SetEnvironmentVariable("NO_PROXY", oldNoProxy); + if (Directory.Exists(sandbox)) + Directory.Delete(sandbox, recursive: true); +} From be81c312b70d88bbd259ff54373cfd18178839b1 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:51:28 +0330 Subject: [PATCH 124/188] test(service): execute hosted R062 using typed .NET10 harness, no PS reflection --- tests/R062-NativeServiceCurl.ps1 | 117 +++---------------------------- 1 file changed, 10 insertions(+), 107 deletions(-) diff --git a/tests/R062-NativeServiceCurl.ps1 b/tests/R062-NativeServiceCurl.ps1 index 4b44e2d1..e611602c 100644 --- a/tests/R062-NativeServiceCurl.ps1 +++ b/tests/R062-NativeServiceCurl.ps1 @@ -3,8 +3,8 @@ param() Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' -# R062 exercises ONLY the compiled private downloader on a disposable GitHub Windows runner. -# Never run against the owner's workstation, installed service, production installer or VPN. +# Only GitHub-hosted disposable Windows. This guard runs BEFORE dotnet starts. +# The typed .NET 10 fixture itself independently repeats this host check. if ($env:GITHUB_ACTIONS -cne 'true' -or $env:RUNNER_ENVIRONMENT -cne 'github-hosted' -or $env:RUNNER_OS -cne 'Windows' -or @@ -15,110 +15,13 @@ if ($env:GITHUB_ACTIONS -cne 'true' -or throw 'R062_ONLY_DISPOSABLE_GITHUB_WINDOWS_RUNNER' } -$service = Join-Path $env:GITHUB_WORKSPACE 'windows/service/bin/Release/net10.0-windows/DirectInternetMethod.Service.dll' -if (!(Test-Path -LiteralPath $service -PathType Leaf)) { - throw 'R062_EXACT_COMPILED_SERVICE_ASSEMBLY_MISSING' +$project = Join-Path $env:GITHUB_WORKSPACE 'tests/service_curl_native/ServiceCurlNative.csproj' +if (!(Test-Path -LiteralPath $project -PathType Leaf)) { + throw 'R062_TYPED_NATIVE_DOTNET_PROJECT_MISSING' } -$root = [IO.Path]::GetFullPath($env:RUNNER_TEMP) -$work = Join-Path $root ('R062_native_curl_' + [Guid]::NewGuid().ToString('N')) -if (Test-Path -LiteralPath $work) { throw 'R062_TEMP_PATH_ALREADY_EXISTS' } -[IO.Directory]::CreateDirectory($work) | Out-Null - -$listener = $null -$client = $null -$cancel = $null -$oldPath = [Environment]::GetEnvironmentVariable('PATH', 'Process') -$oldNoProxy = [Environment]::GetEnvironmentVariable('NO_PROXY', 'Process') -try { - $fake = Join-Path $work 'untrusted-PATH-first' - [IO.Directory]::CreateDirectory($fake) | Out-Null - # Deliberately invalid Windows executable. If PATH selected it, the test MUST fail. - [IO.File]::WriteAllBytes((Join-Path $fake 'curl.exe'), [byte[]]@(0x4D, 0x5A, 0x90, 0x00)) - - $listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0) - $listener.Start(1) - $port = ([Net.IPEndPoint]$listener.LocalEndpoint).Port - $uri = 'http://127.0.0.1:' + $port + '/r062-exact-service-fixture' - $destination = Join-Path $work 'pinned-loopback-fixture.txt' - $body = [Text.Encoding]::UTF8.GetBytes('R062_NATIVE_SYSTEM_CURL_' + $env:GITHUB_SHA + [Environment]::NewLine) - $expectedSha = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($body)) - $incoming = $listener.AcceptTcpClientAsync() - - [Environment]::SetEnvironmentVariable('PATH', ($fake + ';' + $oldPath), 'Process') - [Environment]::SetEnvironmentVariable('NO_PROXY', '127.0.0.1,localhost', 'Process') - - $assembly = [Reflection.Assembly]::LoadFile([IO.Path]::GetFullPath($service)) - $type = $assembly.GetType('DirectInternetMethod.Service.Program', $true) - $binding = [Reflection.BindingFlags]'Static,NonPublic' - $download = $type.GetMethod('DownloadWithSystemCurlAsync', $binding) - if ($null -eq $download) { throw 'R062_EXACT_PRIVATE_DOWNLOADER_MISSING' } - - $cancel = [Threading.CancellationTokenSource]::new([TimeSpan]::FromSeconds(25)) - # The service main entrypoint / Windows SCM are NOT invoked. - # Reflection.Invoke must receive actual CLR strings, not PowerShell PSObject wrappers. - $nativeArgs = [object[]]::new(3) - $nativeArgs[0] = [string][Management.Automation.PSObject]::Base($uri) - $nativeArgs[1] = [string][Management.Automation.PSObject]::Base($destination) - $nativeArgs[2] = $cancel.Token - if ($nativeArgs[0] -isnot [string] -or $nativeArgs[1] -isnot [string]) { - throw 'R062_REFLECTION_CLR_STRINGS_NOT_NORMALIZED' - } - $task = $download.Invoke($null, $nativeArgs) - if ($task -isnot [Threading.Tasks.Task]) { throw 'R062_DOWNLOADER_NOT_ASYNC_TASK' } - - $client = $incoming.WaitAsync([TimeSpan]::FromSeconds(15)).GetAwaiter().GetResult() - $client.ReceiveTimeout = 12000 - $stream = $client.GetStream() - $buffer = [byte[]]::new(4096) - $count = $stream.Read($buffer, 0, $buffer.Length) - if ($count -le 0) { throw 'R062_LOOPBACK_EMPTY_HTTP_REQUEST' } - $request = [Text.Encoding]::ASCII.GetString($buffer, 0, $count) - if (!$request.StartsWith('GET /r062-exact-service-fixture HTTP/1.1')) { - throw 'R062_UNEXPECTED_HTTP_METHOD_OR_PATH' - } - - $crlf = [string][char]13 + [string][char]10 - $headers = [Text.Encoding]::ASCII.GetBytes( - 'HTTP/1.1 200 OK' + $crlf + - 'Content-Type: text/plain' + $crlf + - ('Content-Length: ' + $body.Length) + $crlf + - 'Connection: close' + $crlf + $crlf) - $stream.Write($headers, 0, $headers.Length) - $stream.Write($body, 0, $body.Length) - $stream.Flush() - $client.Close() - $client = $null - - $task.WaitAsync([TimeSpan]::FromSeconds(25)).GetAwaiter().GetResult() - if (!(Test-Path -LiteralPath $destination -PathType Leaf)) { - throw 'R062_SERVICE_CURL_DID_NOT_WRITE_FIXTURE' - } - $actualSha = (Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash - if ($actualSha -cne $expectedSha) { throw 'R062_FIXTURE_DIGEST_MISMATCH' } - if (!(Test-Path -LiteralPath (Join-Path [Environment]::SystemDirectory 'curl.exe') -PathType Leaf)) { - throw 'R062_WINDOWS_SYSTEM_CURL_UNAVAILABLE' - } - $receipt = [ordered]@{ - State = 'PASS_R062_GITHUB_HOSTED_NATIVE_DOWNLOADER' - ExactCheckedOutSha = $env:GITHUB_SHA - NativeServiceDownloaderInvoked = $true - WindowsServiceInstalledOrStarted = $false - CurlChosenFromUntrustedPATH = $false - LoopbackOnlyTransport = $true - PinnedFixtureSha256Matched = $true - DownloadedBytes = $body.Length - PhysicalNetworkOrVPNModified = $false - ProductionInstallerExecuted = $false - } - $receipt | ConvertTo-Json -Compress -} -finally { - if ($null -ne $cancel) { try { $cancel.Cancel() } catch {} ; $cancel.Dispose() } - if ($null -ne $client) { try { $client.Close() } catch {} } - if ($null -ne $listener) { try { $listener.Stop() } catch {} } - [Environment]::SetEnvironmentVariable('PATH', $oldPath, 'Process') - [Environment]::SetEnvironmentVariable('NO_PROXY', $oldNoProxy, 'Process') - if (Test-Path -LiteralPath $work) { - Remove-Item -LiteralPath $work -Recurse -Force - } +# Avoid PowerShell Reflection.Invoke and its PSObject/CLR parameter wrapping. +# Native CLR strings + CancellationToken are handled inside a net10.0-windows executable. +& dotnet run --project $project -c Release --no-launch-profile +if ($LASTEXITCODE -ne 0) { + throw 'R062_NATIVE_SERVICE_CURL_LOOPBACK_TEST_FAILED' } From febda720e21c6a47a26bcc139c8bd0e1784d4265 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 10:36:36 +0330 Subject: [PATCH 125/188] fix(windows): count real YouTube page and 204 in core health --- .github/workflows/ci.yml | 2 + .../r685_windows_gui_real_youtube_contract.py | 2 +- ...20_windows_start_youtube_truth_contract.py | 86 +++++++++++++++++++ tests/windows_contract_audit.py | 14 ++- windows/app/Start-Direct.ps1 | 11 +-- 5 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 tests/r720_windows_start_youtube_truth_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d060d21..4348b1d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -503,6 +503,8 @@ jobs: if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python -B tests/r685_windows_gui_real_youtube_contract.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } + python -B tests/r720_windows_start_youtube_truth_contract.py + if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/adult_coverage_data_audit.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/router_gateway_contract_audit.py diff --git a/tests/r685_windows_gui_real_youtube_contract.py b/tests/r685_windows_gui_real_youtube_contract.py index 5ad9c8c9..ec245e0d 100644 --- a/tests/r685_windows_gui_real_youtube_contract.py +++ b/tests/r685_windows_gui_real_youtube_contract.py @@ -22,7 +22,7 @@ def need(cond,label): if not cond:raise SystemExit("R685_FAIL_"+label) need('StatusBadge.Text = "PARTIAL";' in GUI,"PRESERVE_PARTIAL_STATUS") -need("function Get-CoreSiteHealth($yt,$oa,$gh)" in START and +need("function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)" in START and "$httpsProbePassCount -lt 2" in START,"PRESERVE_TWO_OF_THREE_START") need('var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });' in GUI, "REAL_YOUTUBE_PAGE_PROBE_MISSING") diff --git a/tests/r720_windows_start_youtube_truth_contract.py b/tests/r720_windows_start_youtube_truth_contract.py new file mode 100644 index 00000000..df316261 --- /dev/null +++ b/tests/r720_windows_start_youtube_truth_contract.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""R720: exact production Windows Start health must require YouTube's real page. + +Only extract and run the production Get-CoreSiteHealth AST function in pwsh. +Do not source the lifecycle script, send SCM actions, or change networking. +""" +from __future__ import annotations +import pathlib +import shutil +import subprocess +import sys +import tempfile + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SCRIPT=ROOT/"windows/app/Start-Direct.ps1" +text=SCRIPT.read_text(encoding="utf-8-sig") +required=( + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", + "if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')}", + "$ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\\|' 8 2", + "$yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\\|' 8 2", + "$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh", + "youtubePage=$ytPage;youtube204=$yt", + "required=2", + "passed=3-$failed.Count", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", +) +for key in required: + assert text.count(key)==1,("R720_MISSING_OR_DUPLICATE_PRODUCTION_GUARD",key) +assert "function Get-CoreSiteHealth($yt,$oa,$gh)" not in text +assert "$httpsHealth=Get-CoreSiteHealth $yt $oa $gh" not in text +assert "$httpsProbePassCount -lt 2" in text,"OLD_2_OF_3_ENGINE_PARTIAL_SEMANTICS_LOST" +assert "if(-not $dy.ok){throw 'YOUTUBE_DNS_NOT_CLEAN'}" in text +assert "Curl-Probe-Retry" in text and "--noproxy '*'" in text + +native=shutil.which("pwsh") +if sys.platform=="win32": + assert native is not None,"R720_NATIVE_WINDOWS_POWERSHELL_REQUIRED" +if native is not None: + code=r''' +$ErrorActionPreference='Stop' +$astTokens=$null +$astErrors=$null +$source=[System.Management.Automation.Language.Parser]::ParseFile( + $args[0],[ref]$astTokens,[ref]$astErrors) +if($astErrors.Count){throw 'R720_SOURCE_START_SCRIPT_PARSE_ERROR'} +$defs=@($source.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and + $node.Name -eq 'Get-CoreSiteHealth' +},$true)) +if($defs.Count -ne 1){throw 'R720_SOURCE_HEALTH_AST_NOT_UNIQUE'} +. ([scriptblock]::Create($defs[0].Extent.Text)) +for($mask=0;$mask -lt 16;$mask++){ + $page=[pscustomobject]@{ok=($mask -band 1) -ne 0} + $ping=[pscustomobject]@{ok=($mask -band 2) -ne 0} + $openai=[pscustomobject]@{ok=($mask -band 4) -ne 0} + $github=[pscustomobject]@{ok=($mask -band 8) -ne 0} + $truth=Get-CoreSiteHealth $page $ping $openai $github + $ytOk=[bool]($page.ok -and $ping.ok) + $expectedPassed=[int]$ytOk+[int]$openai.ok+[int]$github.ok + $expectedHealthy=$expectedPassed -eq 3 + $list=@($truth.unreachableCoreSites) + if([int]$truth.required -ne 2 -or + [int]$truth.passed -ne $expectedPassed -or + [bool]$truth.allCoreSitesOk -ne $expectedHealthy -or + ($truth.reachability -eq 'HEALTHY') -ne $expectedHealthy -or + ($list -contains 'youtube') -ne (-not $ytOk) -or + ($list -contains 'openai') -ne (-not $openai.ok) -or + ($list -contains 'github') -ne (-not $github.ok)){ + throw "R720_CORE_SITE_TRUTH_TABLE_MISMATCH_$mask" + } +} +Write-Output 'R720_PRODUCTION_NATIVE_POWERSHELL_16_OF_16_TRUTH_PASS' +''' + with tempfile.TemporaryDirectory(prefix="dim-r720-native-") as d: + p=pathlib.Path(d)/"verify.ps1" + p.write_text(code,encoding="utf-8") + result=subprocess.run([native,"-NoProfile","-NonInteractive","-File", + str(p),str(SCRIPT)],capture_output=True,text=True,timeout=45) + assert result.returncode==0,("R720_NATIVE_POWERSHELL_FAILED",result.stdout,result.stderr) + assert "16_OF_16_TRUTH_PASS" in result.stdout,"R720_NATIVE_SCRIPT_NOT_PROVEN" + print(result.stdout.strip()) +else: + print("R720_STATIC_GUARDS_PASS_NONWINDOWS_NATIVE_MATRIX_DEFERRED") +print("R720_WINDOWS_ENGINE_YOUTUBE_PAGE_AND_204_FULL_HEALTH_CONTRACT_PASS") diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 9ba9e5ae..9bb9c724 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -80,7 +80,7 @@ def idx(s,x): return s.find(x) check("ui_adult_check_default_off_persisted", all(x in gui for x in ("adultSiteLiveCheck","settings.json","AdultSiteCheckToggle_Changed")) and "bool adultCheckEnabled;" in gui) check("adult_catalog_opt_in", all(x in gui for x in ("adultCoverageEnabled","adult-enabled.txt","adult-hosts.txt","nsfw-onlydomains.txt","EnsureAdultCatalogAsync","Adult catalog is unexpectedly small","xvideos.com","xnxx.com","xhamster.com")) and all(x in start for x in ("$AdultEnabledFile","$AdultHosts","$AdultFallback","adultHostCount","ADULT_CATALOG_INVALID")) and 'adult-fallback-hosts.txt' in iss) check("startup_core_site_health_fields", all(x in start for x in ( - "function Get-CoreSiteHealth($yt,$oa,$gh)", + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", "required=2", "passed=3-$failed.Count", "allCoreSitesOk=($failed.Count -eq 0)", @@ -89,7 +89,17 @@ def idx(s,x): return s.find(x) "$httpsProbePassCount=[int]$httpsHealth.passed", "$ev.status='PASS_ACTIVE'", ))) -check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) +check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) +check("startup_true_youtube_page_plus_204_required", all(x in start for x in ( + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", + "if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')}", + "$ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\\|' 8 2", + "$yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\\|' 8 2", + "$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh", + "youtubePage=$ytPage;youtube204=$yt", + "$httpsProbePassCount -lt 2", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", +)) and "function Get-CoreSiteHealth($yt,$oa,$gh)" not in start) check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) check("ui_openai_403_cannot_be_chatgpt_login_pass", 'FmtOpenAiApiProbe(openAiProbe)' in gui diff --git a/windows/app/Start-Direct.ps1 b/windows/app/Start-Direct.ps1 index 860bca62..c491da7f 100644 --- a/windows/app/Start-Direct.ps1 +++ b/windows/app/Start-Direct.ps1 @@ -217,10 +217,10 @@ function Curl-Probe-Retry([string]$Url,[string]$LocalIp,[string]$AcceptPattern,[ $last=$runs[-1] [ordered]@{url=$Url;exit=$last.exit;meta=$last.meta;ok=$false;attempts=$Attempts;runs=$runs} } -function Get-CoreSiteHealth($yt,$oa,$gh) { +function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh) { # A healthy engine/2-of-3 quorum does NOT prove all core sites accessible. $failed=[Collections.Generic.List[string]]::new() - if(-not [bool]$yt.ok){[void]$failed.Add('youtube')} + if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')} if(-not [bool]$oa.ok){[void]$failed.Add('openai')} if(-not [bool]$gh.ok){[void]$failed.Add('github')} [ordered]@{ @@ -471,10 +471,11 @@ try{ $dy=Dns-Probe 'www.youtube.com' $yh=Curl-Probe-Retry 'http://www.youtube.com/' $route.LocalIp '^(200|301|302|303|307|308)\|' 7 2 - $yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^(200|204)\|' 8 2 + $ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\|' 8 2 + $yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\|' 8 2 $oa=Curl-Probe-Retry 'https://api.openai.com/v1/models' $route.LocalIp '^(200|401)\|' 7 2 $gh=Curl-Probe-Retry 'https://github.com/' $route.LocalIp '^(200|301|302|303|307|308)\|' 7 2 - $httpsHealth=Get-CoreSiteHealth $yt $oa $gh + $httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh $httpsProbePassCount=[int]$httpsHealth.passed $routes=@(Get-NetRoute -AddressFamily IPv4 -ErrorAction SilentlyContinue|Where-Object{$_.DestinationPrefix -in @('0.0.0.0/1','128.0.0.0/1')}) $proxy=(netsh winhttp show proxy|Out-String).Trim() @@ -490,7 +491,7 @@ try{ Write-JsonAtomic $StatePath $state $ev.status='PASS_ACTIVE' $ev.live=[ordered]@{ - ctrldPid=$cpid;winwsPid=$wp.Id;directMethods=$DirectMethods;strategy=$strategy;scope=$scope;runtimeHostCount=$merged.Count;adultCoverage=$adultEnabled;adultHostCount=$adultHostCount;youtubeHttp80=$yh;dns=$dy;youtube=$yt;openai=$oa;github=$gh;httpsHealth=$httpsHealth + ctrldPid=$cpid;winwsPid=$wp.Id;directMethods=$DirectMethods;strategy=$strategy;scope=$scope;runtimeHostCount=$merged.Count;adultCoverage=$adultEnabled;adultHostCount=$adultHostCount;youtubeHttp80=$yh;dns=$dy;youtube=$yt;youtubePage=$ytPage;youtube204=$yt;openai=$oa;github=$gh;httpsHealth=$httpsHealth ics=$icsLive;proxy=$proxy;broadRouteCount=$routes.Count drivers=@(Get-OwnedDrivers|Select-Object Name,State,PathName) } From ae0941a69c3878981c0072f9c261d22ae85bf14b Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 11:37:05 +0330 Subject: [PATCH 126/188] fix(windows): pin privileged startup curl to Windows system binary --- .github/workflows/ci.yml | 2 + ...ows_privileged_curl_provenance_contract.py | 109 ++++++++++++++++++ windows/app/Start-Direct.ps1 | 4 +- 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 tests/r726_windows_privileged_curl_provenance_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4348b1d4..d02b85c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -505,6 +505,8 @@ jobs: if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python -B tests/r720_windows_start_youtube_truth_contract.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } + python -B tests/r726_windows_privileged_curl_provenance_contract.py + if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/adult_coverage_data_audit.py if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } python tests/router_gateway_contract_audit.py diff --git a/tests/r726_windows_privileged_curl_provenance_contract.py b/tests/r726_windows_privileged_curl_provenance_contract.py new file mode 100644 index 00000000..caaed7c7 --- /dev/null +++ b/tests/r726_windows_privileged_curl_provenance_contract.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""R726: privileged Windows Start must never resolve curl.exe from PATH. + +Read-only native exercise of the production Curl-Probe function (via PowerShell AST). +The chosen target is closed IPv4 loopback port 1, with no SCM, engine, DNS or +network configuration changes. A genuine Microsoft executable copied under the +name curl.exe is placed first on PATH to prove that the system-owned curl wins. +""" +from __future__ import annotations + +import pathlib +import re +import shutil +import subprocess +import sys +import tempfile + +ROOT = pathlib.Path(__file__).resolve().parents[1] +SOURCE = ROOT / "windows/app/Start-Direct.ps1" +source = SOURCE.read_text(encoding="utf-8-sig") +functions = re.findall( + r"^function Curl-Probe\([^\r\n]*\)\{\s*\n.*?^\}", + source, + flags=re.MULTILINE | re.DOTALL, +) +assert len(functions) == 1, "R726_PRIVILEGED_CURL_FUNCTION_NOT_UNIQUE" +fn = functions[0] +assert fn.count("Join-Path ([Environment]::SystemDirectory) 'curl.exe'") == 1, \ + "R726_SYSTEM32_CURL_NOT_PINNED" +assert "if(-not [IO.File]::Exists($SystemCurl)){throw 'WINDOWS_SYSTEM_CURL_MISSING'}" in fn, \ + "R726_MISSING_FAIL_CLOSED_GUARD" +assert "$m=& $SystemCurl -4 --interface $LocalIp --noproxy '*'" in fn, \ + "R726_CURL_CALL_NOT_ABSOLUTE" +assert "& curl.exe" not in fn and "Invoke-Expression" not in fn, \ + "R726_PATH_OR_SHELL_RESOLUTION_FORBIDDEN" +assert "%{http_code}|%{remote_ip}|%{time_connect}|%{time_appconnect}|%{time_total}" in fn, \ + "R726_PROBE_OUTPUT_CONTRACT_CHANGED" +assert "$LASTEXITCODE" in fn and "--max-time $Timeout" in fn, \ + "R726_NATIVE_EXIT_AND_TIMEOUT_PRESERVATION" + +pwsh = shutil.which("pwsh") +if sys.platform == "win32": + assert pwsh, "R726_NATIVE_WINDOWS_PWSH_REQUIRED" + +if pwsh: + verify = r""" +$ErrorActionPreference='Stop' +$PSNativeCommandUseErrorActionPreference=$false +$native=$args[0] +$fixture=$args[1] +$windowsCurl=Join-Path ([Environment]::SystemDirectory) 'curl.exe' +$decoySource=Join-Path ([Environment]::SystemDirectory) 'whoami.exe' +if(-not (Test-Path -LiteralPath $windowsCurl -PathType Leaf)){throw 'R726_WINDOWS_CURL_NOT_PRESENT'} +if(-not (Test-Path -LiteralPath $decoySource -PathType Leaf)){throw 'R726_WINDOWS_DECOY_FIXTURE_MISSING'} +Copy-Item -LiteralPath $decoySource -Destination (Join-Path $fixture 'curl.exe') -ErrorAction Stop +$tokens=$null +$errors=$null +$ast=[System.Management.Automation.Language.Parser]::ParseFile($native,[ref]$tokens,[ref]$errors) +if($errors.Count){throw 'R726_PRODUCTION_SOURCE_PARSE_FAILED'} +$defs=@($ast.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and + $node.Name -eq 'Curl-Probe' +},$true)) +if($defs.Count -ne 1){throw 'R726_CURL_AST_NOT_UNIQUE'} +. ([scriptblock]::Create($defs[0].Extent.Text)) +$oldPath=$env:PATH +try { + $env:PATH=$fixture+';'+$oldPath + $resolution=(Get-Command -Name curl.exe -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source + if(-not [string]::Equals($resolution,(Join-Path $fixture 'curl.exe'),[StringComparison]::OrdinalIgnoreCase)){ + throw ('R726_PATH_DECOY_NOT_FIRST_'+$resolution) + } + # The production function must choose the OS-owned executable, not the decoy. + # Port 1 is local, closed, and never uses a real external network. + $probe=Curl-Probe 'https://127.0.0.1:1/' '127.0.0.1' 1 + if([int]$probe.exit -notin @(7,28)){ + throw ('R726_NATIVE_EXIT_NOT_SYSTEM_CURL_'+$probe.exit+'_'+$probe.meta) + } + if([string]$probe.meta -notmatch 'curl:.*\((7|28)\)'){ + throw ('R726_NATIVE_CURL_OUTPUT_MISSING_'+$probe.meta) + } + Write-Output 'R726_NATIVE_SYSTEM_CURL_PATH_DECOY_PASS' +} +finally { + $env:PATH=$oldPath +} +""" + with tempfile.TemporaryDirectory(prefix="dim-r726-curl-") as td: + script = pathlib.Path(td) / "verify.ps1" + script.write_text(verify, encoding="utf-8") + result = subprocess.run( + [pwsh, "-NoProfile", "-NonInteractive", "-File", str(script), str(SOURCE), td], + capture_output=True, + text=True, + timeout=35, + ) + assert result.returncode == 0, ( + "R726_NATIVE_PATH_DECOY_FAILED", + result.stdout, + result.stderr, + ) + assert "R726_NATIVE_SYSTEM_CURL_PATH_DECOY_PASS" in result.stdout, \ + "R726_NATIVE_ACCEPTANCE_RECEIPT_MISSING" + print(result.stdout.strip()) +else: + print("R726_STATIC_ONLY_NONWINDOWS_NATIVE_HOST_TEST_DEFERRED") + +print("R726_PRIVILEGED_START_SYSTEM_CURL_PIN_CONTRACT_PASS") diff --git a/windows/app/Start-Direct.ps1 b/windows/app/Start-Direct.ps1 index c491da7f..e695cebd 100644 --- a/windows/app/Start-Direct.ps1 +++ b/windows/app/Start-Direct.ps1 @@ -202,7 +202,9 @@ function Dns-Probe([string]$Name){ }catch{[ordered]@{name=$Name;ok=$false;ms=-1;answers=@();private=@();error=$_.Exception.Message}} } function Curl-Probe([string]$Url,[string]$LocalIp,[int]$Timeout=7){ - $m=& curl.exe -4 --interface $LocalIp --noproxy '*' -sS -o NUL -w '%{http_code}|%{remote_ip}|%{time_connect}|%{time_appconnect}|%{time_total}' --max-time $Timeout $Url 2>&1 + $SystemCurl=Join-Path ([Environment]::SystemDirectory) 'curl.exe' + if(-not [IO.File]::Exists($SystemCurl)){throw 'WINDOWS_SYSTEM_CURL_MISSING'} + $m=& $SystemCurl -4 --interface $LocalIp --noproxy '*' -sS -o NUL -w '%{http_code}|%{remote_ip}|%{time_connect}|%{time_appconnect}|%{time_total}' --max-time $Timeout $Url 2>&1 [ordered]@{url=$Url;exit=$LASTEXITCODE;meta=($m -join ' ')} } function Curl-Probe-Retry([string]$Url,[string]$LocalIp,[string]$AcceptPattern,[int]$Timeout=7,[int]$Attempts=2){ From 96612b3100b42360ca5fb6d08d2a147572214622 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 15:19:51 +0330 Subject: [PATCH 127/188] fix(linux): show core HTTPS truth before green active status --- .github/workflows/ci.yml | 1 + linux/app/direct_internet_method.py | 60 +++++- tests/r735_linux_live_core_truth_contract.py | 213 +++++++++++++++++++ 3 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 tests/r735_linux_live_core_truth_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d02b85c8..f9c4a374 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,7 @@ jobs: python3 -B tests/r541_route_interface_drift_contract.py python3 -B tests/r545_periodic_gui_status_contract.py python3 -B tests/r678_linux_gui_full_youtube_health_contract.py + python3 -B tests/r735_linux_live_core_truth_contract.py python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py python3 -B tests/r702_nfqws_reinjected_mark_contract.py python3 -B tests/r709_installer_no_bytecode_cache_contract.py diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 26092283..64417556 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -10,6 +10,7 @@ import subprocess import tempfile import threading +from concurrent.futures import ThreadPoolExecutor import time import urllib.parse import urllib.request @@ -670,6 +671,8 @@ def __init__(self,app): self.adult_switch=Gtk.Switch(active=self.adult_check_enabled);live_header.append(self.adult_switch) self.adult_switch.connect("notify::active",self.on_adult_toggle) live.append(live_header) + self.core_live=Gtk.Label(label="Core HTTPS: Available when Direct Method is active",xalign=0) + self.core_live.add_css_class("dim-label");live.append(self.core_live) self.gemini_live=Gtk.Label(label="Gemini: —",xalign=0);self.gemini_live.add_css_class("dim-label");live.append(self.gemini_live) self.adult_live=Gtk.Label(label="Adult coverage: "+("Checking…" if self.adult_check_enabled else "Disabled"),xalign=0);self.adult_live.add_css_class("dim-label");live.append(self.adult_live) box.append(live) @@ -719,9 +722,9 @@ def periodic_state_tick(self): current={"mode":"STALE", "detail":"Automatic status verification failed. Use Refresh or Recovery."} visible=self.status.get_text().removeprefix("Status: ") - if current["mode"]!=visible: + if current["mode"]!=visible and not (current["mode"]=="ACTIVE" and visible=="PARTIAL"): self.refresh(state=current,start_checks=False) - elif current["detail"]!=self.detail.get_text(): + elif visible!="PARTIAL" and current["detail"]!=self.detail.get_text(): self.detail.set_text(current["detail"]) return True @@ -730,7 +733,8 @@ def refresh(self,state=None,start_checks=True): self.status.set_text("Status: "+mode);self.detail.set_text(s["detail"]) for css in ("success","warning","error","dim-label"): self.status.remove_css_class(css) - self.status.add_css_class("success" if mode=="ACTIVE" else "warning" if mode in ("BLOCKED","CONFIG_REQUIRED","BACKEND_UPDATE_REQUIRED","CONFLICT") else "error" if mode=="STALE" else "dim-label") + # R735_GUI_NO_UNVERIFIED_GREEN: ACTIVE proves engine ownership, not site HTTPS. + self.status.add_css_class("warning" if mode in ("ACTIVE","BLOCKED","CONFIG_REQUIRED","BACKEND_UPDATE_REQUIRED","CONFLICT") else "error" if mode=="STALE" else "dim-label") start_ok=(mode=="OFF") stop_ok=(mode in ("ACTIVE","CONFLICT","STALE")) recovery_ok=(mode in ("CONFLICT","STALE")) @@ -746,13 +750,16 @@ def refresh(self,state=None,start_checks=True): self.live_check_generation+=1 generation=self.live_check_generation if mode=="ACTIVE" and start_checks: + if hasattr(self,"core_live"):self.core_live.set_text("Core HTTPS: Checking YouTube, OpenAI, GitHub…") self.gemini_live.set_text("Gemini: Checking…") self.adult_live.set_text("Adult coverage: Checking…" if self.adult_check_enabled else "Adult coverage: Disabled") threading.Thread(target=self.live_checks_worker,args=(generation,),daemon=True).start() elif mode=="ACTIVE": + if hasattr(self,"core_live"):self.core_live.set_text("Core HTTPS: Press Refresh to verify") self.gemini_live.set_text("Gemini: Press Refresh for a live check") self.adult_live.set_text("Adult coverage: Press Refresh for a live check" if self.adult_check_enabled else "Adult coverage: Disabled") else: + if hasattr(self,"core_live"):self.core_live.set_text("Core HTTPS: Available when Direct Method is active") self.gemini_live.set_text("Gemini: Available when Direct Method is active") self.adult_live.set_text("Adult coverage: Disabled" if not self.adult_check_enabled else "Adult coverage: Available when Direct Method is active") @@ -760,11 +767,31 @@ def live_checks_worker(self,generation): try: state=json.loads(STATE.read_text(encoding="utf-8")) physical=str(state.get("physicalInterface") or "") + selected_dns=bool(state.get("selectiveDns") or state.get("selectiveDnsEncrypted")) except Exception: physical="" + selected_dns=False if not physical: + GLib.idle_add(self.finish_core_checks,generation,{"error":"PHYSICAL_INTERFACE_MISSING"}) GLib.idle_add(self.finish_live_checks,generation,{"ok":False,"reached":False},[{"ok":False,"reached":False}] if self.adult_check_enabled else None) return + # Explicit Refresh only; no periodic web polling, no DNS/route mutation. + checks={ + "youtube":("https://www.youtube.com/",{"200"}), + "openai":("https://api.openai.com/v1/models",{"200","401","403"}), + "github":("https://github.com/",{"200"}), + } + if not selected_dns: + checks["youtube204"]=("https://www.youtube.com/generate_204",{"204"}) + try: + with ThreadPoolExecutor(max_workers=4) as pool: + futures={name:pool.submit(probe_url,url,physical,allowed) + for name,(url,allowed) in checks.items()} + core={name:future.result() for name,future in futures.items()} + core["selected_dns"]=selected_dns + except Exception: + core={"error":"CORE_PROBE_FAILED"} + GLib.idle_add(self.finish_core_checks,generation,core) gemini=probe_url("https://gemini.google.com/",physical,{"200","301","302","303","307","308"}) adult=None if self.adult_check_enabled: @@ -777,6 +804,33 @@ def live_checks_worker(self,generation): ] GLib.idle_add(self.finish_live_checks,generation,gemini,adult) + def finish_core_checks(self,generation,core): + if generation!=self.live_check_generation: + return False + try: + if load_state().get("mode")!="ACTIVE": + return False + except Exception: + return False + # Selected DNS acceptance intentionally requires the real page only. + # DPI-only acceptance requires BOTH page200 and generate_204=204. + selected=bool(core.get("selected_dns")) + healthy=(all(isinstance(core.get(site),dict) and core[site].get("ok") is True + for site in ("youtube","openai","github")) + and (selected or (isinstance(core.get("youtube204"),dict) + and core["youtube204"].get("ok") is True))) + self.status.set_text("Status: ACTIVE" if healthy else "Status: PARTIAL") + self.status.remove_css_class("success") + self.status.remove_css_class("warning") + self.status.add_css_class("success" if healthy else "warning") + if hasattr(self,"core_live"): + if healthy: + self.core_live.set_text("Core HTTPS: healthy · YouTube page, OpenAI, GitHub" + + (" · selected DNS" if selected else " · YouTube 204")) + else: + self.core_live.set_text("Core HTTPS: PARTIAL · at least one service unreachable; Stop remains available") + return False + def finish_live_checks(self,generation,gemini,adult): if generation!=self.live_check_generation: return False diff --git a/tests/r735_linux_live_core_truth_contract.py b/tests/r735_linux_live_core_truth_contract.py new file mode 100644 index 00000000..98926978 --- /dev/null +++ b/tests/r735_linux_live_core_truth_contract.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +"""R735: the native Linux GUI must not turn green from engine ownership alone. + +Production AST-extracted Window methods are executed with synthetic health and +route state, never with real GTK, network, DNS, services or user settings. +""" +import ast +import concurrent.futures +import json +import pathlib +import threading +import types +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +SOURCE = (ROOT / "linux/app/direct_internet_method.py").read_text("utf-8") +TREE = ast.parse(SOURCE) +WINDOW = next(n for n in TREE.body if isinstance(n, ast.ClassDef) and n.name == "Window") +METHODS = {n.name: n for n in WINDOW.body if isinstance(n, ast.FunctionDef)} +NAMES = {"refresh", "periodic_state_tick", "finish_core_checks", "live_checks_worker"} +assert NAMES <= METHODS.keys(), "R735_PRODUCTION_GUI_CORE_SITE_HEALTH_METHODS_MISSING" +assert "R735_GUI_NO_UNVERIFIED_GREEN" in SOURCE, "R735_PRODUCTION_GREEN_GUARD_MISSING" + +class Label: + def __init__(self, text=""): + self.value = text + self.classes = set() + def set_text(self, text): + self.value = str(text) + def get_text(self): + return self.value + def remove_css_class(self, x): + self.classes.discard(x) + def add_css_class(self, x): + self.classes.add(x) + +class Button: + def __init__(self): + self.enabled = False + def set_sensitive(self, v): + self.enabled = bool(v) + def remove_css_class(self, *_): + pass + def add_css_class(self, *_): + pass + +STATE_DOC = [{"mode": "ACTIVE", "detail": "Backend owns nft"}] +ACTUAL = [{"physicalInterface": "enp1s0", "selectiveDns": False, "selectiveDnsEncrypted": False}] +CALLS = [] +QUEUED = [] +def fake_probe(url, iface, allowed): + CALLS.append((url, iface, tuple(sorted(allowed)))) + code = {"https://www.youtube.com/": "200", + "https://www.youtube.com/generate_204": "204", + "https://api.openai.com/v1/models": "401", + "https://github.com/": "200", + "https://gemini.google.com/": "200"}[url] + return {"ok": code in allowed, "reached": True, "code": code} + +class FakeStatePath: + def read_text(self, encoding="utf-8"): + return json.dumps(ACTUAL[0]) + +class GLib: + @staticmethod + def idle_add(fn, *args): + QUEUED.append((fn, args)) + return 1 + +class NoThread: + def __init__(self, *args, **kwargs): + raise AssertionError("R735_PERIODIC_BACKGROUND_WEB_PROBE_FORBIDDEN") + +def read_state(): + if isinstance(STATE_DOC[0], Exception): + raise STATE_DOC[0] + return STATE_DOC[0] + +env = { + "load_state": read_state, "STATE": FakeStatePath(), + "json": json, "GLib": GLib, "probe_url": fake_probe, + "format_probe": lambda name, result: name + ": " + ("PASS" if result["ok"] else "FAIL"), + "format_adult_probes": lambda results: "Adult coverage: disabled", + "threading": types.SimpleNamespace(Thread=NoThread), + "ThreadPoolExecutor": concurrent.futures.ThreadPoolExecutor, +} +exec(compile(ast.Module(body=[METHODS[n] for n in sorted(NAMES)], type_ignores=[]), + "r735_real_source_methods", "exec"), env) + +class Dummy: + def __init__(self, mode="ACTIVE"): + self.status = Label("Status: " + mode) + self.detail = Label("Backend owns nft") + self.gemini_live = Label("Gemini: unverified") + self.adult_live = Label("Adult coverage: Disabled") + self.core_live = Label("Core HTTPS: unverified") + self.live_check_generation = 17 + self.adult_check_enabled = False + self.finish_live_checks = lambda *args: False + self.buttons = {n: Button() for n in ("start", "stop", "recovery", "refresh", "update")} + self.buttons["stop"].enabled = True + for n in NAMES: + setattr(self, n, types.MethodType(env[n], self)) + +def core(*, home=True, light=True, openai=True, github=True, selected=False): + return { + "youtube": {"ok": home, "code": "200" if home else "000"}, + "youtube204": None if selected else {"ok": light, "code": "204" if light else "000"}, + "openai": {"ok": openai, "code": "401" if openai else "000"}, + "github": {"ok": github, "code": "200" if github else "000"}, + "selected_dns": selected, + } + +class Contract(unittest.TestCase): + def setUp(self): + STATE_DOC[0] = {"mode": "ACTIVE", "detail": "Backend owns nft"} + ACTUAL[0] = {"physicalInterface": "enp1s0", "selectiveDns": False, "selectiveDnsEncrypted": False} + CALLS.clear() + QUEUED.clear() + def test_01_engine_active_starts_unverified_not_green(self): + d=Dummy() + d.refresh(state=STATE_DOC[0], start_checks=False) + self.assertEqual(d.status.get_text(), "Status: ACTIVE") + self.assertNotIn("success", d.status.classes) + self.assertIn("warning", d.status.classes) + self.assertTrue(d.buttons["stop"].enabled) + self.assertFalse(d.buttons["start"].enabled) + def test_02_complete_core_https_proofs_required_for_green(self): + d=Dummy() + d.finish_core_checks(17, core()) + self.assertEqual(d.status.get_text(), "Status: ACTIVE") + self.assertIn("success", d.status.classes) + self.assertIn("healthy", d.core_live.get_text().lower()) + def test_03_lightweight_success_cannot_hide_youtube_home_failure(self): + d=Dummy() + d.finish_core_checks(17, core(home=False, light=True)) + self.assertEqual(d.status.get_text(), "Status: PARTIAL") + self.assertNotIn("success", d.status.classes) + self.assertIn("warning", d.status.classes) + self.assertTrue(d.buttons["stop"].enabled) + self.assertFalse(d.buttons["start"].enabled) + def test_04_page_success_cannot_hide_lightweight_failure(self): + d=Dummy() + d.finish_core_checks(17, core(home=True,light=False)) + self.assertEqual(d.status.get_text(), "Status: PARTIAL") + def test_05_selected_dns_preserves_original_page_only_gate(self): + d=Dummy() + d.finish_core_checks(17, core(selected=True)) + self.assertIn("success",d.status.classes) + def test_13_missing_or_malformed_lightweight_proof_is_partial(self): + d=Dummy() + payload=core() + payload["youtube204"]=None + d.finish_core_checks(17,payload) + self.assertEqual(d.status.get_text(),"Status: PARTIAL") + self.assertIn("warning",d.status.classes) + def test_14_missing_worker_proof_fails_closed(self): + d=Dummy() + d.finish_core_checks(17,{"error":"CORE_PROBE_FAILED"}) + self.assertEqual(d.status.get_text(),"Status: PARTIAL") + self.assertNotIn("success",d.status.classes) + def test_06_stale_generation_rejected(self): + d=Dummy() + d.finish_core_checks(16,core()) + self.assertEqual(d.status.get_text(), "Status: ACTIVE") + self.assertNotIn("success", d.status.classes) + def test_07_stale_after_stop_rejected(self): + d=Dummy() + STATE_DOC[0]={"mode":"OFF","detail":"Clean"} + d.finish_core_checks(17,core()) + self.assertNotIn("success",d.status.classes) + def test_08_partial_persists_periodic_observation_without_network(self): + d=Dummy() + d.finish_core_checks(17,core(home=False)) + self.assertEqual(d.status.get_text(),"Status: PARTIAL") + self.assertTrue(d.periodic_state_tick()) + self.assertEqual(d.status.get_text(),"Status: PARTIAL") + self.assertEqual(CALLS,[]) + def test_09_backend_off_overrides_partial_and_stop_disabled(self): + d=Dummy() + d.finish_core_checks(17,core(home=False)) + STATE_DOC[0]={"mode":"OFF","detail":"Clean"} + d.periodic_state_tick() + self.assertEqual(d.status.get_text(),"Status: OFF") + self.assertFalse(d.buttons["stop"].enabled) + self.assertTrue(d.buttons["start"].enabled) + def test_10_worker_checks_real_home_and_204_before_green(self): + d=Dummy() + d.live_checks_worker(17) + urls=[c[0] for c in CALLS] + self.assertIn("https://www.youtube.com/",urls) + self.assertIn("https://www.youtube.com/generate_204",urls) + self.assertIn("https://github.com/",urls) + self.assertIn("https://api.openai.com/v1/models",urls) + self.assertTrue(any(fn.__name__=="finish_core_checks" for fn,_ in QUEUED)) + def test_11_selected_dns_worker_page_only(self): + ACTUAL[0]={"physicalInterface":"enp1s0","selectiveDns":False,"selectiveDnsEncrypted":True} + d=Dummy() + d.live_checks_worker(17) + self.assertIn("https://www.youtube.com/",[c[0] for c in CALLS]) + self.assertNotIn("https://www.youtube.com/generate_204",[c[0] for c in CALLS]) + def test_12_empty_iface_fails_closed_without_network(self): + ACTUAL[0]={"physicalInterface":""} + d=Dummy() + d.live_checks_worker(17) + self.assertEqual(CALLS,[]) + self.assertTrue(any(fn.__name__=="finish_core_checks" for fn,_ in QUEUED)) + +if __name__ == "__main__": + result = unittest.main(verbosity=1, exit=False) + if not result.result.wasSuccessful(): + raise SystemExit(1) + print("R735_LINUX_GUI_CORE_HTTP_TRUTH_NEGATIVE_FIRST_14_CASES_PASS") From 234b7da601507efb3ba7116f100fcaa447adc4f4 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 15:26:29 +0330 Subject: [PATCH 128/188] test(ci): pin reproducible R735 Linux candidate archive --- .github/workflows/ci.yml | 20 +++++++++---------- tests/r519_versioned_package_contract.py | 12 ++++++----- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9c4a374..4fc51150 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0" - assert p.is_file() and p.stat().st_size==8372495 + expected="3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE" + assert p.is_file() and p.stat().st_size==8375763 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -163,7 +163,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -192,7 +192,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -225,7 +225,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -254,7 +254,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -283,7 +283,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -313,7 +313,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -358,7 +358,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -384,7 +384,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 963cd020..b45bc771 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0"} +EXPECTED={"version":VERSION,"files":27,"sha256":"3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -72,11 +72,13 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self fixtures=R/"tests/fixtures/r519_original" old_manifest=fixtures/"PACKAGE_MANIFEST.json" old_source=fixtures/"user_uninstall_preserve.py" - self.assertEqual(digest(old_manifest.read_bytes()), + # Compare canonical historical LF bytes, not Git-for-Windows CRLF checkout. + old_manifest_bytes=old_manifest.read_bytes().replace(b"\r\n",b"\n") + self.assertEqual(digest(old_manifest_bytes), "EA43CD77EAA2979AC970A8B47145881C767519E3D024BF907167A2FE3FD6C05E") self.assertEqual(digest(old_source.read_bytes()), "9A4A4C37E3F63F1602E15454100150D209E3CD099DE80504B560F31D42D3C0EF") - prev=json.loads(old_manifest.read_text()) + prev=json.loads(old_manifest_bytes) self.assertEqual((prev["schema"],prev["version"],prev["platform"]),(1,VERSION,"linux-x86_64")) before={row["file"]:row for row in prev["files"]} after={row["file"]:row for row in self.rows} @@ -87,7 +89,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "BBB7B86B423B367DF58CF12E127180C7CA0FFC06BEC471E8B3B062A9D175963C") + "DEC2CE52487943F52C15E665562022F0F3AB6AECD08454715004595B6CFD2C82") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', @@ -108,7 +110,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "47776640A1BB8B5D4490244565DDD47A6A817A999347566917CC73E21F41EC24") with zipfile.ZipFile(old) as z: self.assertEqual(set(self.n),set(z.namelist())|{self.prefix+"system/upgrade_guard.py"}) - self.assertEqual(z.read(self.prefix+"PACKAGE_MANIFEST.json"),old_manifest.read_bytes()) + self.assertEqual(z.read(self.prefix+"PACKAGE_MANIFEST.json"),old_manifest_bytes) self.assertEqual(z.read(self.prefix+"app/user_uninstall_preserve.py"),old_source.read_bytes()) self.assertEqual(z.testzip(),None) def test_original_published_metadata_untouched(self): diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 127ec992..90670faf 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="238f479608bd43a2be3fdcd0afc4c9df4e23c243b25e31adf29bbac255a605e0" +CANDIDATE_SHA="3d705b7d5e72e10b988293fd8f45a95bc9dce20326dfddf7bbc756b5c87d6aee" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 2fb2d44e..1486aca7 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0" +PIN_PACKAGE="3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE" PIN_ROOT="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" PIN_GUARD="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 1331d584..a1305502 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="238F479608BD43A2BE3FDCD0AFC4C9DF4E23C243B25E31ADF29BBAC255A605E0" +ARCHIVE="3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 1071c97faa90e5dc9c8e4de5723954997eec160c Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 15:27:56 +0330 Subject: [PATCH 129/188] test(linux): require warning before HTTPS-backed active health --- tests/tailscale_gui_scope_guard_contract.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tailscale_gui_scope_guard_contract.py b/tests/tailscale_gui_scope_guard_contract.py index 2cd40a44..7c28392b 100644 --- a/tests/tailscale_gui_scope_guard_contract.py +++ b/tests/tailscale_gui_scope_guard_contract.py @@ -82,7 +82,8 @@ def mode():return env["load_state"]() assert mode()["mode"]=="BLOCKED",("exit node / foreign VPN must still block",mode()) print("PASS unknown VPN/exit node still fail-closed") -assert 'mode in ("BLOCKED","CONFIG_REQUIRED","BACKEND_UPDATE_REQUIRED","CONFLICT")' in src +assert 'mode in ("ACTIVE","BLOCKED","CONFIG_REQUIRED","BACKEND_UPDATE_REQUIRED","CONFLICT")' in src +assert 'R735_GUI_NO_UNVERIFIED_GREEN' in src assert 'start_ok=(mode=="OFF")' in src assert 'w.close();self.refresh()' in src print("PASS CONFIG_REQUIRED warning; no Start until Strategy save; immediate refresh") From 69220b342da3e9727a0aa47fb21711bbcf38bc6a Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 16:56:50 +0330 Subject: [PATCH 130/188] fix(linux): isolate verified update downloads and clean cache --- .github/workflows/ci.yml | 21 +- linux/app/direct_internet_method.py | 166 ++++++++++---- tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ...9_linux_update_cache_lifecycle_contract.py | 217 ++++++++++++++++++ 7 files changed, 358 insertions(+), 56 deletions(-) create mode 100644 tests/r739_linux_update_cache_lifecycle_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fc51150..3515cad0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE" - assert p.is_file() and p.stat().st_size==8375763 + expected="EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291" + assert p.is_file() and p.stat().st_size==8381071 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -88,6 +88,7 @@ jobs: python3 -B tests/r545_periodic_gui_status_contract.py python3 -B tests/r678_linux_gui_full_youtube_health_contract.py python3 -B tests/r735_linux_live_core_truth_contract.py + python3 -B tests/r739_linux_update_cache_lifecycle_contract.py python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py python3 -B tests/r702_nfqws_reinjected_mark_contract.py python3 -B tests/r709_installer_no_bytecode_cache_contract.py @@ -163,7 +164,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -192,7 +193,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -225,7 +226,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -254,7 +255,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -283,7 +284,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -313,7 +314,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -358,7 +359,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -384,7 +385,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 64417556..ec1aac91 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -7,6 +7,7 @@ import pathlib import re import shutil +import stat import subprocess import tempfile import threading @@ -568,48 +569,131 @@ def _download(url,path): shutil.copyfileobj(src,dst) def download_and_install_update(info): + # R739: one private, automatically disposed cache slot per update attempt. + # Never overwrite stale/user-owned downloads, nor retain a second installer. + release_version=str(info.get("version") or "") + if not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+",release_version): + raise RuntimeError("Unsafe Linux update release version.") + archive_name=f"DirectInternetMethod_{release_version}_Linux_x86_64.zip" + asset=info.get("asset") or {} + sums=info.get("sums") or {} + if (asset.get("name")!=archive_name or sums.get("name")!="SHA256SUMS.txt"): + raise RuntimeError("Unexpected Linux update asset name.") + for item in (asset,sums): + if not re.fullmatch(r"sha256:[0-9A-Fa-f]{64}",str(item.get("digest") or "")): + raise RuntimeError("Linux update asset digest is invalid.") cache=pathlib.Path.home()/".cache/DirectInternetMethod/updates" - cache.mkdir(parents=True,exist_ok=True) - sums_path=cache/"SHA256SUMS.txt" - zip_path=cache/info["asset"]["name"] - _download(info["sums"]["url"],sums_path) - sums_actual=hashlib.sha256(sums_path.read_bytes()).hexdigest().upper() - sums_api=str(info["sums"]["digest"]).split(":",1)[1].upper() - if sums_actual!=sums_api: - sums_path.unlink(missing_ok=True) - raise RuntimeError("Downloaded SHA256SUMS.txt does not match GitHub asset digest.") - matches=[] - for raw in sums_path.read_text(encoding="utf-8").splitlines(): - parts=raw.strip().split() - if len(parts)>=2 and parts[-1].lstrip("*")==info["asset"]["name"]: - matches.append(parts[0].upper()) - if len(matches)!=1 or not re.fullmatch(r"[0-9A-F]{64}",matches[0]): - raise RuntimeError("SHA256SUMS.txt must contain exactly one valid checksum for the Linux update.") - expected=matches[0] - api_expected=str(info["asset"]["digest"]).split(":",1)[1].upper() - if expected!=api_expected: - raise RuntimeError("Linux update checksum disagrees with GitHub asset digest.") - _download(info["asset"]["url"],zip_path) - actual=hashlib.sha256(zip_path.read_bytes()).hexdigest().upper() - if actual!=expected or actual!=api_expected: - zip_path.unlink(missing_ok=True) - raise RuntimeError("Downloaded Linux update failed three-way SHA-256 verification.") - - temp=pathlib.Path(tempfile.mkdtemp(prefix="dim-update-",dir=str(cache))) - with zipfile.ZipFile(zip_path) as z: - base=temp.resolve() - for member in z.infolist(): - target=(temp/member.filename).resolve() - if base not in target.parents and target!=base: - raise RuntimeError("Unsafe update archive path.") - z.extractall(temp) - installers=list(temp.rglob("install.sh")) - if len(installers)!=1: - raise RuntimeError("Update archive does not contain exactly one installer.") - p=subprocess.run(["bash",str(installers[0])],text=True,capture_output=True,timeout=180) - if p.returncode!=0: - raise RuntimeError((p.stderr or p.stdout or f"installer exit {p.returncode}").strip()) - return p.stdout.strip() + if (cache.is_symlink() or cache.parent.is_symlink() + or cache.parent.parent.is_symlink()): + raise RuntimeError("Update cache path contains a symlink.") + created_parent=not cache.parent.exists() + created_cache=not cache.exists() + cache.mkdir(parents=True,exist_ok=True,mode=0o700) + if (not cache.is_dir() or cache.is_symlink() or + (hasattr(os,"getuid") and cache.stat().st_uid!=os.getuid())): + raise RuntimeError("Update cache is not owned by the current user.") + try: + with tempfile.TemporaryDirectory(prefix="dim-update-",dir=str(cache)) as work: + temp=pathlib.Path(work) + sums_path=temp/"SHA256SUMS.txt" + zip_path=temp/archive_name + _download(sums["url"],sums_path) + actual_sums=hashlib.sha256(sums_path.read_bytes()).hexdigest().upper() + if actual_sums!=sums["digest"].split(":",1)[1].upper(): + raise RuntimeError("Downloaded SHA256SUMS.txt does not match GitHub asset digest.") + matches=[] + for raw in sums_path.read_text(encoding="utf-8").splitlines(): + parts=raw.strip().split() + if len(parts)>=2 and parts[-1].lstrip("*")==archive_name: + matches.append(parts[0].upper()) + if len(matches)!=1 or not re.fullmatch(r"[0-9A-F]{64}",matches[0]): + raise RuntimeError("SHA256SUMS.txt must contain exactly one Linux update checksum.") + expected=matches[0] + if expected!=asset["digest"].split(":",1)[1].upper(): + raise RuntimeError("Linux update checksum disagrees with GitHub asset digest.") + _download(asset["url"],zip_path) + if hashlib.sha256(zip_path.read_bytes()).hexdigest().upper()!=expected: + raise RuntimeError("Downloaded Linux update failed three-way SHA-256 verification.") + + prefix=archive_name.removesuffix(".zip")+"/" + with zipfile.ZipFile(zip_path) as z: + members=z.infolist() + if len(members)!=29 or len({m.filename for m in members})!=len(members): + raise RuntimeError("Linux update archive has an unexpected file inventory.") + names={} + total=0 + for member in members: + name=member.filename + parts=pathlib.PurePosixPath(name).parts + if (not name.startswith(prefix) or "\\" in name or + ".." in parts or member.is_dir() or + pathlib.PurePosixPath(name).as_posix()!=name or + stat.S_ISLNK(member.external_attr>>16) or + member.file_size>64000000): + raise RuntimeError("Unsafe Linux update archive entry.") + total+=member.file_size + if total>256000000: + raise RuntimeError("Linux update archive is oversized.") + names[name]=member + manifest_name=prefix+"PACKAGE_MANIFEST.json" + checksum_name=prefix+"SHA256SUMS.txt" + try: + manifest=json.loads(z.read(manifest_name)) + inside_sums=z.read(checksum_name).decode("utf-8") + except (KeyError,ValueError,UnicodeError) as exc: + raise RuntimeError("Linux update internal manifest is invalid.") from exc + if (manifest.get("schema")!=1 or + manifest.get("product")!="Direct Internet Method" or + manifest.get("version")!=release_version or + manifest.get("platform")!="linux-x86_64" or + not isinstance(manifest.get("files"),list) or + len(manifest["files"])!=27): + raise RuntimeError("Linux update package identity does not match release.") + referenced=set() + expected_lines=[] + for entry in manifest["files"]: + if not isinstance(entry,dict) or set(entry)!={"file","bytes","sha256"}: + raise RuntimeError("Linux update manifest member is invalid.") + rel=entry["file"] + if (not isinstance(rel,str) or not rel or "\\" in rel or + pathlib.PurePosixPath(rel).is_absolute() or + ".." in pathlib.PurePosixPath(rel).parts or + pathlib.PurePosixPath(rel).as_posix()!=rel or + rel in referenced): + raise RuntimeError("Linux update manifest contains an unsafe path.") + referenced.add(rel) + name=prefix+rel + if (name not in names or type(entry["bytes"]) is not int or + entry["bytes"]<0 or not isinstance(entry["sha256"],str) or + not re.fullmatch(r"[0-9A-F]{64}",entry["sha256"])): + raise RuntimeError("Linux update manifest digest is invalid.") + data=z.read(name) + if (len(data)!=entry["bytes"] or + hashlib.sha256(data).hexdigest().upper()!=entry["sha256"]): + raise RuntimeError("Linux update package payload digest mismatch.") + expected_lines.append(f'{entry["sha256"]} {rel}\n') + if (set(names)!={prefix+rel for rel in referenced}|{manifest_name,checksum_name} + or inside_sums!="".join(expected_lines) or + "install.sh" not in referenced): + raise RuntimeError("Linux update package manifest/file inventory mismatch.") + z.extractall(temp) + installer=temp/prefix/"install.sh" + if not installer.is_file() or installer.is_symlink(): + raise RuntimeError("Linux update installer path is invalid.") + p=subprocess.run(["bash",str(installer)],text=True,capture_output=True,timeout=180) + if p.returncode!=0: + raise RuntimeError((p.stderr or p.stdout or + f"installer exit {p.returncode}").strip()) + return p.stdout.strip() + finally: + # Only remove empty directories *created by this attempt*. Never touch + # preexisting cache files or any unrelated user data. + if created_cache: + try:cache.rmdir() + except OSError:pass + if created_parent: + try:cache.parent.rmdir() + except OSError:pass class Window(Adw.ApplicationWindow): def __init__(self,app): diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index b45bc771..d3ee49ef 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE"} +EXPECTED={"version":VERSION,"files":27,"sha256":"EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -89,7 +89,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "DEC2CE52487943F52C15E665562022F0F3AB6AECD08454715004595B6CFD2C82") + "B7322CF3132B44029F409B2BD0015A516FFA0C04EA904F3129D813ED4FB9B4D5") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 90670faf..0176e143 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="3d705b7d5e72e10b988293fd8f45a95bc9dce20326dfddf7bbc756b5c87d6aee" +CANDIDATE_SHA="eaf1ae50924fe9d8b440044c7bfb3232a51eb912786a235cc70fb2e2911e6291" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 1486aca7..3b2d6573 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE" +PIN_PACKAGE="EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291" PIN_ROOT="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" PIN_GUARD="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index a1305502..daf1a922 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="3D705B7D5E72E10B988293FD8F45A95BC9DCE20326DFDDF7BBC756B5C87D6AEE" +ARCHIVE="EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r739_linux_update_cache_lifecycle_contract.py b/tests/r739_linux_update_cache_lifecycle_contract.py new file mode 100644 index 00000000..e064238c --- /dev/null +++ b/tests/r739_linux_update_cache_lifecycle_contract.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +"""R739: verify production Linux update installer never leaves new cache or code debris. + +All HTTPS downloads and installer invocations are replaced by explicit in-memory +fixtures inside a disposable HOME; no real operating-system service, network, +release publication, user state or installer is touched. +""" +from __future__ import annotations + +import ast +import hashlib +import io +import json +import os +import pathlib +import stat +import tempfile +import types +import unittest +import zipfile +from unittest import mock + +ROOT = pathlib.Path(__file__).resolve().parents[1] +SOURCE = (ROOT / "linux/app/direct_internet_method.py").read_text("utf-8-sig") +TREE = ast.parse(SOURCE) +METHOD = [n for n in TREE.body if isinstance(n, ast.FunctionDef) and + n.name == "download_and_install_update"] +assert len(METHOD) == 1, "R739_ONE_PRODUCTION_UPDATE_FUNCTION_REQUIRED" +fn_scope = { + "pathlib": pathlib, "tempfile": tempfile, "hashlib": hashlib, + "json": json, "zipfile": zipfile, "os": os, "stat": stat, + "re": __import__("re"), +} +exec(compile(ast.Module(body=METHOD, type_ignores=[]), "installed_linux_updater_real_function", "exec"), fn_scope) +install = fn_scope["download_and_install_update"] + +VERSION = "1.5.6" +ARCHIVE_NAME = f"DirectInternetMethod_{VERSION}_Linux_x86_64.zip" +PREFIX = ARCHIVE_NAME.removesuffix(".zip") + "/" + +def build_zip(kind="good"): + source = {"install.sh": b"#!/bin/bash\nexit 0\n"} + source.update({f"app/fixture{i:02d}.bin": f"fixture-{i}\n".encode() + for i in range(26)}) + if kind == "missing_installer": + source["another_installer.sh"] = source.pop("install.sh") + rows = [{"file": key, "bytes": len(raw), + "sha256": hashlib.sha256(raw).hexdigest().upper()} + for key, raw in source.items()] + manifest = {"schema": 1, "product": "Direct Internet Method", + "platform": "linux-x86_64", "version": VERSION, "files": rows} + memory = io.BytesIO() + with zipfile.ZipFile(memory, "w", compression=zipfile.ZIP_STORED) as z: + for key, data in source.items(): + zi = zipfile.ZipInfo(PREFIX + key) + if kind == "symlink" and key == "app/fixture00.bin": + zi.create_system = 3 + zi.external_attr = ((stat.S_IFLNK | 0o777) << 16) + z.writestr(zi, data) + z.writestr(PREFIX + "PACKAGE_MANIFEST.json", + json.dumps(manifest).encode()) + z.writestr(PREFIX + "SHA256SUMS.txt", + "".join(f"{r['sha256']} {r['file']}\n" for r in rows)) + if kind == "traversal": + z.writestr(PREFIX + "../escape.txt", b"should-never-extract") + if kind == "duplicate": + z.writestr(PREFIX + "install.sh", b"bad-extra-installer") + return memory.getvalue() + +class CacheTruth(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="r739-updater-real-fixture-") + self.addCleanup(self.temp.cleanup) + self.home = pathlib.Path(self.temp.name) / "home" + self.home.mkdir() + self.cache = self.home / ".cache/DirectInternetMethod/updates" + self.bad_kind = "good" + self.payload = build_zip() + self.sums_raw = hashlib.sha256(self.payload).hexdigest().upper() + " " + ARCHIVE_NAME + "\n" + self.sums = self.sums_raw.encode() + self.info = { + "version": VERSION, "tag": "v"+VERSION, + "asset": {"name": ARCHIVE_NAME, "url": "fixture://zip", + "digest": "sha256:" + hashlib.sha256(self.payload).hexdigest()}, + "sums": {"name": "SHA256SUMS.txt", "url": "fixture://sums", + "digest": "sha256:" + hashlib.sha256(self.sums).hexdigest()}, + } + self.install_calls = [] + def fake_download(url, path): + data = self.sums if url == "fixture://sums" else self.payload + if url not in ("fixture://sums", "fixture://zip"): + raise AssertionError("NON_FIXTURE_DOWNLOAD_ATTEMPT") + pathlib.Path(path).write_bytes(data) + def fake_run(argv, **kwargs): + self.install_calls.append(list(argv)) + target = pathlib.Path(argv[1]) + assert argv[0] == "bash" and target.is_file() + assert target.read_bytes().startswith(b"#!/bin/bash\n") + return types.SimpleNamespace(returncode=0, stdout="INSTALLER_MOCK_PASS\n", stderr="") + fn_scope["_download"] = fake_download + fn_scope["subprocess"] = types.SimpleNamespace(run=fake_run) + self.home_patch = mock.patch.object(pathlib.Path, "home", return_value=self.home) + self.home_patch.start() + self.addCleanup(self.home_patch.stop) + + def assert_no_new_residue(self): + self.assertFalse(self.cache.exists() and any(self.cache.iterdir()), + "R739_NEW_CACHE_DEBRIS_MUST_BE_EMPTY_AFTER_ANY_RESULT") + self.assertFalse(any(self.home.rglob("install.sh")), + "R739_EXTRACTED_INSTALLER_REMAINS") + + def test_01_good_release_installer_returns_and_cleans_all_cache(self): + result = install(self.info) + self.assertEqual(result, "INSTALLER_MOCK_PASS") + self.assertEqual(len(self.install_calls), 1) + self.assert_no_new_residue() + + def test_02_preexisting_unrelated_file_is_preserved(self): + self.cache.mkdir(parents=True) + keep = self.cache / "unrelated-user-file" + keep.write_bytes(b"KEEP_USER_DATA") + self.assertEqual(install(self.info), "INSTALLER_MOCK_PASS") + self.assertEqual(keep.read_bytes(), b"KEEP_USER_DATA") + self.assertEqual(sorted(x.name for x in self.cache.iterdir()), [keep.name]) + + def test_03_checksum_list_digest_mismatch_cleans(self): + self.sums += b"tampered\n" + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + def test_04_zip_sha256_mismatch_cleans(self): + self.payload += b"tamper" + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + def test_05_installer_failure_also_cleans(self): + def failed(argv, **kwargs): + return types.SimpleNamespace(returncode=1, stdout="", stderr="MOCK_INSTALL_FAILED") + fn_scope["subprocess"] = types.SimpleNamespace(run=failed) + with self.assertRaisesRegex(RuntimeError, "MOCK_INSTALL_FAILED"): + install(self.info) + self.assert_no_new_residue() + + def test_06_missing_expected_root_installer_refused(self): + self.payload = build_zip("missing_installer") + self.info["asset"]["digest"] = "sha256:" + hashlib.sha256(self.payload).hexdigest() + self.sums = (hashlib.sha256(self.payload).hexdigest().upper() + " " + ARCHIVE_NAME + "\n").encode() + self.info["sums"]["digest"] = "sha256:" + hashlib.sha256(self.sums).hexdigest() + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + def test_07_symlink_member_refused_even_with_correct_zip_sha(self): + self.payload = build_zip("symlink") + self.info["asset"]["digest"] = "sha256:" + hashlib.sha256(self.payload).hexdigest() + self.sums = (hashlib.sha256(self.payload).hexdigest().upper() + " " + ARCHIVE_NAME + "\n").encode() + self.info["sums"]["digest"] = "sha256:" + hashlib.sha256(self.sums).hexdigest() + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + def test_08_wrong_or_traversal_asset_name_rejected_before_download(self): + self.info["asset"]["name"] = "../unrelated-file.zip" + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + def test_09_bad_zip_member_traversal_refused(self): + self.payload = build_zip("traversal") + self.info["asset"]["digest"] = "sha256:" + hashlib.sha256(self.payload).hexdigest() + self.sums = (hashlib.sha256(self.payload).hexdigest().upper() + " " + ARCHIVE_NAME + "\n").encode() + self.info["sums"]["digest"] = "sha256:" + hashlib.sha256(self.sums).hexdigest() + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + @unittest.skipUnless(hasattr(os, "symlink"), "host cannot make symlinks") + def test_10_symlink_cache_root_refused_without_outside_writes(self): + outside = self.home / "outside" + outside.mkdir() + self.cache.parent.mkdir(parents=True) + try: + self.cache.symlink_to(outside, target_is_directory=True) + except (OSError, NotImplementedError): + self.skipTest("Host policy prohibits symlink creation; exercised on Linux CI") + with self.assertRaises(Exception): + install(self.info) + self.assertEqual(list(outside.iterdir()), []) + self.assertEqual(self.install_calls, []) + + def test_11_duplicate_zip_member_refused(self): + self.payload = build_zip("duplicate") + self.info["asset"]["digest"] = "sha256:" + hashlib.sha256(self.payload).hexdigest() + self.sums = (hashlib.sha256(self.payload).hexdigest().upper() + " " + ARCHIVE_NAME + "\n").encode() + self.info["sums"]["digest"] = "sha256:" + hashlib.sha256(self.sums).hexdigest() + with self.assertRaises(RuntimeError): + install(self.info) + self.assertEqual(self.install_calls, []) + self.assert_no_new_residue() + + def test_12_repeated_installs_do_not_accumulate_tmp_dirs(self): + for i in range(3): + self.assertEqual(install(self.info), "INSTALLER_MOCK_PASS") + self.assert_no_new_residue() + self.assertEqual(len(self.install_calls), 3) + +if __name__ == "__main__": + unittest.main(verbosity=1) + print("R739_REAL_PRODUCTION_LINUX_UPDATER_CACHE_SECURITY_12_TESTS_PASS") From e9c77c24636dbc731501adde6457efe0b58bfb43 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 18:47:35 +0330 Subject: [PATCH 131/188] fix(linux): forward TCP SYN into nfqws conntrack before TLS --- .github/workflows/ci.yml | 21 +-- linux/app/direct_internet_method.py | 2 +- linux/app/direct_method_helper.sh | 14 +- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/linux_nft_kernel_namespace_contract.py | 8 +- tests/r519_versioned_package_contract.py | 4 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r702_nfqws_reinjected_mark_contract.py | 2 +- tests/r711_tls_syn_fastpath_contract.py | 75 ++++++----- ...qws_conntrack_syn_first_packet_contract.py | 125 ++++++++++++++++++ tests/tailscale_cleanup_ownership_contract.py | 9 +- tests/tailscale_nft_ownership_contract.py | 9 +- ...e_transient_rollback_ownership_contract.py | 9 +- 21 files changed, 212 insertions(+), 92 deletions(-) create mode 100644 tests/r742_nfqws_conntrack_syn_first_packet_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3515cad0..6a5731d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291" - assert p.is_file() and p.stat().st_size==8381071 + expected="C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503" + assert p.is_file() and p.stat().st_size==8380692 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -93,6 +93,7 @@ jobs: python3 -B tests/r702_nfqws_reinjected_mark_contract.py python3 -B tests/r709_installer_no_bytecode_cache_contract.py python3 -B tests/r711_tls_syn_fastpath_contract.py + python3 -B tests/r742_nfqws_conntrack_syn_first_packet_contract.py python3 -B tests/tailscale_nft_ownership_contract.py python3 -B tests/tailscale_cleanup_ownership_contract.py python3 -B tests/tailscale_transient_rollback_ownership_contract.py @@ -164,7 +165,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -193,7 +194,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -226,7 +227,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -255,7 +256,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -284,7 +285,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -314,7 +315,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -359,7 +360,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -385,7 +386,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index ec1aac91..a86dbfba 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -50,7 +50,7 @@ SELECTIVE_DNS_TOKEN="I_ACCEPT_UNENCRYPTED_SELECTIVE_DNS_V1" ENCRYPTED_DNS_OPT_IN=APP_HOME/"encrypted-dns-optin.txt" ENCRYPTED_DNS_TOKEN="I_ACCEPT_ENCRYPTED_SELECTIVE_DNS_V1" -COEXIST_HELPER_SHA256="a137f7c2924037486aea8b7ec1a41556a374868c282f846f84ae0fc368e84b8b" +COEXIST_HELPER_SHA256="82051dce83480a9c1c291ab9f13acff44b359655387dc253cb6db00eaa27795a" LATEST_API="https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest" def selective_dns_optin_state(): diff --git a/linux/app/direct_method_helper.sh b/linux/app/direct_method_helper.sh index 37692d00..e49af695 100644 --- a/linux/app/direct_method_helper.sh +++ b/linux/app/direct_method_helper.sh @@ -157,7 +157,7 @@ import re, sys table, iface, mode = sys.argv[1:] lines = [line.strip() for line in sys.stdin if line.strip()] coexist = mode == "system-preserved" -expected_count = 11 if coexist else 10 +expected_count = 10 if coexist else 9 if len(lines) != expected_count: sys.exit(1) if lines[:2] != [f"table inet {table} {{", "chain output {"]: @@ -175,10 +175,6 @@ if coexist: offset = 5 queue = r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? bypass)" dev = r"oifname (?:" + re.escape(iface) + r"|" + re.escape(chr(34) + iface + chr(34)) + r")" -syn_rule = dev + r" tcp dport 443 tcp flags (?:syn / syn,ack|& \(syn \| ack\) == syn) return" -if not re.fullmatch(syn_rule, lines[offset]): - sys.exit(1) -offset += 1 for index, (proto, port) in enumerate((("tcp", 80), ("tcp", 443), ("udp", 443))): rule = dev + " " + proto + " dport " + str(port) + " " + queue if not re.fullmatch(rule, lines[offset + index]): @@ -317,7 +313,6 @@ queue=r"ct original packets 1-6 queue (?:flags bypass to 200|num 200(?: flags)? patterns=[r"meta mark & 0x0*40000000 == 0x0*40000000 return"] if coexist=="1": patterns.append(r"meta mark & 0x0*ff0000 == 0x0*80000 return") -patterns.append(dev+r" tcp dport 443 tcp flags (?:syn / syn,ack|& \(syn \| ack\) == syn) return") for proto,port in (("tcp",80),("tcp",443),("udp",443)): patterns.append(dev+r" "+proto+r" dport "+str(port)+r" "+queue) rules=inner[2:-1] @@ -817,10 +812,9 @@ EOF # Tailscale LinuxBypassMark 0x80000/0xff0000 protects its own DERP/control traffic. nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return" fi - # Original TCP SYN has no TLS payload. Let the kernel establish the - # connection without a potentially delayed NFQUEUE round-trip. Only - # physical WAN TCP/443 pure SYN is excluded; TLS data still reaches nfqws. - nft "add rule inet $TABLE output oifname \"$PHY\" tcp dport 443 tcp flags & (syn | ack) == syn return" + # R742: nfqws internal conntrack needs the original SYN to initialize + # direction/sequence. Preserve the 1-6 original-packet bound, interface, + # fail-open NFQUEUE, injected mark and Tailscale bypass above. nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 80 ct original packets 1-6 queue num "$QNUM" bypass nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 443 ct original packets 1-6 queue num "$QNUM" bypass nft add rule inet "$TABLE" output oifname "$PHY" udp dport 443 ct original packets 1-6 queue num "$QNUM" bypass diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 291e3652..e3d497ac 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -23,7 +23,7 @@ FILES=( ("helper","app/direct_method_helper.sh","usr/lib/directinternetmethod/direct_method_helper.sh", "E52FCAEB9C60D9E43D4A5B75462455AD8C5EE4D82FEE679A0CC4A3520D8ABEA5", - "A137F7C2924037486AEA8B7EC1A41556A374868C282F846F84AE0FC368E84B8B",0o755), + "82051DCE83480A9C1C291AB9F13ACFF44B359655387DC253CB6DB00EAA27795A",0o755), ("uninstall","system/uninstall_system.sh","usr/lib/directinternetmethod/uninstall_system.sh", "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index c7401b3a..be44698f 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" +PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index b65e26a3..032cd3c7 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" +TX_SHA="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" +GUARD_SHA="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/linux_nft_kernel_namespace_contract.py b/tests/linux_nft_kernel_namespace_contract.py index 02bec9a3..f7cc9684 100644 --- a/tests/linux_nft_kernel_namespace_contract.py +++ b/tests/linux_nft_kernel_namespace_contract.py @@ -115,7 +115,6 @@ def kernel_script() -> str: nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return" } queues(){ - nft "add rule inet $TABLE output oifname enp1s0 tcp dport 443 tcp flags & (syn | ack) == syn return" nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 80 ct original packets 1-6 queue num 200 bypass nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 443 ct original packets 1-6 queue num 200 bypass nft add rule inet "$TABLE" output oifname enp1s0 udp dport 443 ct original packets 1-6 queue num 200 bypass @@ -142,20 +141,21 @@ def kernel_script() -> str: verify_clean expect_absent echo "PASS_KERNEL_COMPLETE_OWNED_CLEANUP" -# A table without its SYN fast-path cannot be claimed for destructive cleanup. +# A table with a *foreign* legacy SYN bypass is not owned and must be preserved. put_state new_table; new_chain; injected_mark; mark +nft "add rule inet $TABLE output oifname enp1s0 tcp dport 443 tcp flags & (syn | ack) == syn return" nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 80 ct original packets 1-6 queue num 200 bypass nft add rule inet "$TABLE" output oifname enp1s0 tcp dport 443 ct original packets 1-6 queue num 200 bypass nft add rule inet "$TABLE" output oifname enp1s0 udp dport 443 ct original packets 1-6 queue num 200 bypass -if nft_table_owned "$PHY"; then echo FALSE_SYNSKIP_MISSING_OWNERSHIP >&2; exit 94;fi +if nft_table_owned "$PHY"; then echo FALSE_FOREIGN_SYN_BYPASS_ACCEPTED >&2; exit 94;fi if cleanup_state_owned; then rc=0; else rc=$?;fi test "$rc" -eq 81 test -f "$STATE" nft list table inet "$TABLE" >/dev/null nft delete table inet "$TABLE" expect_absent -echo "PASS_KERNEL_MISSING_TCP443_SYN_EXEMPTION_FAIL_CLOSED" +echo "PASS_KERNEL_FOREIGN_TCP443_SYN_EXEMPTION_FAIL_CLOSED" # A table lacking nfqws injected-packet exemption is NOT owned. Root must # preserve it rather than deleting a rule set that may loop reinjected packets. diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index d3ee49ef..1a966939 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291"} +EXPECTED={"version":VERSION,"files":27,"sha256":"C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -89,7 +89,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "B7322CF3132B44029F409B2BD0015A516FFA0C04EA904F3129D813ED4FB9B4D5") + "57B452CF570E8A1545340F13A140E9E6D86F4F2B6F282BA2AA3D992F64FE11AF") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 0176e143..f6dc975b 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="eaf1ae50924fe9d8b440044c7bfb3232a51eb912786a235cc70fb2e2911e6291" +CANDIDATE_SHA="c8e5a06387ff21ec0e048f8bb02dc1b4c31b364625e18ec641900356f60ce503" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index eb388abc..ab798ebd 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "ba68fd4ab7581d86886dce25ed53df8c6be9df52eb2e6dc4c6925dac8e4a4635" +ROOT_SHA = "de3ed6765dc8db307297e49736483d02d7471b4f3fe7a77408a4368d7a2515a3" USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index fc4a457d..88a81c81 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "ba68fd4ab7581d86886dce25ed53df8c6be9df52eb2e6dc4c6925dac8e4a4635" +PIN_ROOT = "de3ed6765dc8db307297e49736483d02d7471b4f3fe7a77408a4368d7a2515a3" PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index f5bc647f..f8ce4cb0 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" +SOURCE_SHA="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 3b2d6573..7b335ec5 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291" -PIN_ROOT="BA68FD4AB7581D86886DCE25ED53DF8C6BE9DF52EB2E6DC4C6925DAC8E4A4635" -PIN_GUARD="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" +PIN_PACKAGE="C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503" +PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" +PIN_GUARD="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index f2268dd5..eded21ef 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="BBBC800B719CF22A296E9A5EBCEDCA6B69DD4F0A94248B21FA23A3B5AD5431C5" +PIN_GUARD="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index daf1a922..a97df9fc 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="EAF1AE50924FE9D8B440044C7BFB3232A51EB912786A235CC70FB2E2911E6291" +ARCHIVE="C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r702_nfqws_reinjected_mark_contract.py b/tests/r702_nfqws_reinjected_mark_contract.py index cbec07ba..03b97019 100644 --- a/tests/r702_nfqws_reinjected_mark_contract.py +++ b/tests/r702_nfqws_reinjected_mark_contract.py @@ -23,7 +23,7 @@ assert helper.count(owned)==1 and helper.count(transient)==1 live=helper.split(owned,1)[1].split('\n}\n',1)[0] rollback=helper.split(transient,1)[1].split('\n}\n',1)[0] -assert 'expected_count = 11 if coexist else 10' in live +assert 'expected_count = 10 if coexist else 9' in live assert 'offset = 4' in live assert 'lines[3]' in live and 'lines[4]' in live assert 'offset = 5' in live diff --git a/tests/r711_tls_syn_fastpath_contract.py b/tests/r711_tls_syn_fastpath_contract.py index 4fdda987..8d76a7f6 100644 --- a/tests/r711_tls_syn_fastpath_contract.py +++ b/tests/r711_tls_syn_fastpath_contract.py @@ -1,45 +1,48 @@ #!/usr/bin/env python3 -"""R711 TCP443 original SYN packets bypass NFQUEUE before L7 nfqws queues. +"""R711 -> R742: original SYN must reach bounded NFQUEUE to seed nfqws conntrack. -Negative-first source contract, no root, real netfilter, network or user state. -Ensures the SYN optimization is narrowly limited to original WAN TCP443 and -the independent owner-only Stop/Recovery paths recognize only exact rules. +The old R711 optimization bypassed SYN, which upstream nfqws documents as +required for tracking TCP direction/sequence. Regression is source-only; +independent physical queue validation remains a separate release gate. """ from pathlib import Path import re -BASE=Path(__file__).resolve().parents[1] -h=(BASE/"linux/app/direct_method_helper.sh").read_text(encoding="utf-8") -SYN='tcp flags & (syn | ack) == syn return' -def verify(yes, name): - if not yes:raise AssertionError("R711_"+name) +ROOT=Path(__file__).resolve().parents[1] +h=(ROOT/"linux/app/direct_method_helper.sh").read_text(encoding="utf8") +def verify(value,name): + if not value:raise AssertionError("R742_"+name) def fragment(name): - match=re.findall(r'^'+re.escape(name)+r'\(\)\{\n.*?^\}\n', - h,flags=re.S|re.M) - verify(len(match)==1,"SHELL_FRAGMENT_NOT_EXACT_"+name) - return match[0] - -start=fragment("start_engine") if "start_engine(){" in h else h -print("R711_SOURCE_CURRENT_HEALTH_TARGET_PATH") -rule='nft "add rule inet $TABLE output oifname \\"$PHY\\" tcp dport 443 '+SYN+'"' -verify(h.count(rule)==1,"SYNSKIP_RULE_NOT_UNIQUE") -a=h.index('nft "add rule inet $TABLE output meta mark & 0x40000000 == 0x40000000 return"') -b=h.index('nft "add rule inet $TABLE output meta mark & 0xff0000 == 0x80000 return"') -c=h.index(rule) -queue=h.index('nft add rule inet "$TABLE" output oifname "$PHY" tcp dport 443 ct original packets 1-6 queue num "$QNUM" bypass') -verify(alen(patterns)' in trans and + 'not all(re.fullmatch(pattern,actual)' in trans, + "TRANSIENT_FOREIGN_RULE_FAIL_CLOSED") +verify('ROLLBACK_FOREIGN_NFT_PRESERVED' in h and + 'NFT_TABLE_OWNERSHIP_MISMATCH' in h,"SAFETY_REFUSES_FOREIGN_NFT") +for protocol in ('tcp dport 80','tcp dport 443','udp dport 443'): + verify(protocol+' ct original packets 1-6 queue num "$QNUM" bypass' in h, + "BOUND_AND_FAILOPEN_"+protocol) +print("R742_SYNS_SENT_IN_BOUNDED_NFQUEUE_STRICT_MARKED_TABLE_SOURCE_PASS") diff --git a/tests/r742_nfqws_conntrack_syn_first_packet_contract.py b/tests/r742_nfqws_conntrack_syn_first_packet_contract.py new file mode 100644 index 00000000..a77c08ed --- /dev/null +++ b/tests/r742_nfqws_conntrack_syn_first_packet_contract.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""R742 negative-first nfqws packet-0 conntrack and fail-closed nft ownership. + +Rootless: fakes nft output and validates exact production Bash ownership +function. Never touches real nftables/network, DNS, services or Tailscale. +""" +from __future__ import annotations +import json +import os +import pathlib +import re +import subprocess +import tempfile +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SOURCE=(ROOT/"linux/app/direct_method_helper.sh").read_text(encoding="utf-8") +OWNED_NAME="nft_table_owned" +TRANSIENT_NAME="nft_table_transient_owned" +SYN_FASTPATH='tcp flags & (syn | ack) == syn return' +MARK='meta mark & 0x40000000 == 0x40000000 return' +TS='meta mark & 0xff0000 == 0x80000 return' +QUEUE='tcp dport 443 ct original packets 1-6 queue num "$QNUM" bypass' +OWNED='nft_table_owned(){' +TRANSIENT='nft_table_transient_owned(){' + +def fragment(name): + matches=re.findall(r'^'+re.escape(name)+r'\(\)\{\n.*?^\}\n', + SOURCE,flags=re.M|re.S) + if len(matches)!=1: + raise AssertionError("R742_EXPECTED_UNIQUE_PRODUCTION_FUNCTION_"+name) + return matches[0] + +class ConntrackGuard(unittest.TestCase): + def test_01_no_pure_syn_exclusion_prior_to_nfqueue(self): + self.assertNotIn(SYN_FASTPATH,SOURCE, + "R742_SOURCE_BYPASSES_SYN_NFQWS_NEVER_CREATES_INTERNAL_CONNECTION") + self.assertIn('nft add rule inet "$TABLE" output oifname "$PHY" '+QUEUE, + SOURCE) + self.assertLess(SOURCE.index('meta mark & 0x40000000 == 0x40000000 return'), + SOURCE.index(QUEUE)) + self.assertLess(SOURCE.index('meta mark & 0xff0000 == 0x80000 return'), + SOURCE.index(QUEUE)) + + def test_02_stable_owned_full_nft_requires_same_exact_mark_order_and_counts(self): + own=fragment(OWNED_NAME) + self.assertIn('expected_count = 10 if coexist else 9',own) + self.assertNotIn('syn_rule',own) + self.assertNotIn('tcp flags',own) + self.assertIn('lines[offset + index]',own) + self.assertIn('len(lines) != expected_count',own) + + def test_03_transient_exact_owner_rejects_foreign_early_syn_rule(self): + fn=fragment(TRANSIENT_NAME) + self.assertNotIn('patterns.append(dev+r" tcp dport 443 tcp flags',fn) + self.assertIn('for proto,port in',fn) + self.assertIn('len(rules)>len(patterns)',fn) + + def test_04_output_has_no_unconditional_slowdown_or_tailscale_mutation(self): + self.assertIn('ct original packets 1-6 queue num "$QNUM" bypass',SOURCE) + self.assertIn('meta mark & 0xff0000 == 0x80000 return',SOURCE) + self.assertIn('meta mark & 0x40000000 == 0x40000000 return',SOURCE) + self.assertNotIn('ip route replace default',SOURCE) + self.assertIn('nft_table_owned "$phy"',SOURCE) + + def test_05_original_profile_and_user_whitelist_remain_targeted(self): + self.assertIn('--hostlist-domains=youtube.com,youtu.be,googlevideo.com',SOURCE) + self.assertIn('--dpi-desync=multisplit --dpi-desync-split-pos=1',SOURCE) + self.assertIn('TAILSCALE_ALL_SITES_UNSUPPORTED',SOURCE) + + def test_06_production_owner_semantics_actual_fake_full_and_bad_tables(self): + extract="\n".join(fragment(name) for name in + ('state_value','nft_table_owned','nft_table_transient_owned')) + full='''table inet directinternetmethod { +chain output { +type filter hook output priority mangle; policy accept; +meta mark & 0x40000000 == 0x40000000 return +meta mark & 0x00ff0000 == 0x00080000 return +oifname "enp1s0" tcp dport 80 ct original packets 1-6 queue flags bypass to 200 +oifname "enp1s0" tcp dport 443 ct original packets 1-6 queue flags bypass to 200 +oifname "enp1s0" udp dport 443 ct original packets 1-6 queue flags bypass to 200 +} +} +''' + tests=[ + ('owned',full,True,True), + ('old_fastpath_foreign',full.replace( + 'oifname "enp1s0" tcp dport 80', + 'oifname "enp1s0" tcp dport 443 '+SYN_FASTPATH+'\n' + 'oifname "enp1s0" tcp dport 80',1),False,False), + ('foreign_missing_injection_guard',full.replace( + 'meta mark & 0x40000000 == 0x40000000 return\n',''),False,False), + ('foreign_wrong_interface',full.replace( + 'oifname "enp1s0" tcp dport 443 ct', + 'oifname "wrong0" tcp dport 443 ct',1),False,False), + ('foreign_added_drop',full.replace('oifname "enp1s0" udp dport', + 'ip daddr 203.0.113.1 drop\n' + 'oifname "enp1s0" udp dport',1),False,False), + ] + with tempfile.TemporaryDirectory(prefix="r742-nfqws-owner-no-net-") as td: + base=pathlib.Path(td) + nft=base/"nft" + nft.write_text('#!/bin/sh\n[ "$*" = "list table inet directinternetmethod" ] || exit 12\n' + 'cat "$R742_MOCK_TABLE"\n',encoding="utf8") + nft.chmod(0o700) + table=base/"table" + state=base/"state.json" + state.write_text(json.dumps({"dnsMode":"system-preserved"}),encoding="utf8") + env=dict(os.environ,PATH=str(base)+os.pathsep+os.environ.get('PATH',''), + R742_MOCK_TABLE=str(table)) + for name,contents,owned,transient in tests: + table.write_text(contents,encoding="utf8") + code='set -u\nTABLE=directinternetmethod\nSTATE='+str(state)+'\n'+extract+''' +if nft_table_owned enp1s0;then echo OWNED=1;else echo OWNED=0;fi +if nft_table_transient_owned enp1s0 1;then echo TRANSIENT=1;else echo TRANSIENT=0;fi +''' + p=subprocess.run(['bash','-c',code],env=env, + text=True,capture_output=True,timeout=12) + self.assertEqual(p.returncode,0,(name,p.stdout,p.stderr)) + self.assertIn("OWNED="+str(int(owned)),p.stdout,(name,p.stdout)) + self.assertIn("TRANSIENT="+str(int(transient)),p.stdout,(name,p.stdout)) + +if __name__=="__main__": + unittest.main(verbosity=1) + print("R742_SYN_EARLY_NFQUEUE_AND_ROOTLESS_FAILCLOSED_OWNERSHIP_PASS") diff --git a/tests/tailscale_cleanup_ownership_contract.py b/tests/tailscale_cleanup_ownership_contract.py index e211d996..c3f9d0de 100644 --- a/tests/tailscale_cleanup_ownership_contract.py +++ b/tests/tailscale_cleanup_ownership_contract.py @@ -25,7 +25,6 @@ def f(name): type filter hook output priority mangle; policy accept; meta mark & 0x40000000 == 0x40000000 return %%MARK%% - oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return oifname "enp1s0" tcp dport 80 ct original packets 1-6 queue flags bypass to 200 oifname "enp1s0" tcp dport 443 ct original packets 1-6 queue flags bypass to 200 oifname "enp1s0" udp dport 443 ct original packets 1-6 queue flags bypass to 200 @@ -35,8 +34,8 @@ def f(name): mark="meta mark & 0x00ff0000 == 0x00080000 return" cases=[ ("split_coexistence_owns_marked_table", "system-preserved",mark,True,0,True), - ("split_coexistence_no_tcp443_syn_skip", "system-preserved",mark,True,81,False), - ("split_coexistence_bad_tcp443_syn_skip", "system-preserved",mark,True,81,False), + ("split_coexistence_unexpected_tcp443_syn_exemption", "system-preserved",mark,True,81,False), + ("split_coexistence_wrong_tcp443_syn_exemption", "system-preserved",mark,True,81,False), ("split_coexistence_missing_injected_mark", "system-preserved",mark,True,81,False), ("split_coexistence_wrong_injected_mark", "system-preserved",mark,True,81,False), ("split_coexistence_rejects_missing_mark", "system-preserved","",True,81,False), @@ -87,8 +86,8 @@ def f(name): "ctrldPid":0,"nfqwsPid":0}),encoding="utf-8") fixture=d/"nft.out" value=nft_output.replace("%%MARK%%",bypass) - if name=="split_coexistence_no_tcp443_syn_skip":value=value.replace('oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return',"",1) - if name=="split_coexistence_bad_tcp443_syn_skip":value=value.replace('oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return','oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == ack return',1) + if name=="split_coexistence_unexpected_tcp443_syn_exemption":value=value.replace('oifname "enp1s0" tcp dport 80', 'oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return\n oifname "enp1s0" tcp dport 80',1) + if name=="split_coexistence_wrong_tcp443_syn_exemption":value=value.replace('oifname "enp1s0" tcp dport 80', 'oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == ack return\n oifname "enp1s0" tcp dport 80',1) if name=="split_coexistence_missing_injected_mark":value=value.replace(inject_mark,"",1) if name=="split_coexistence_wrong_injected_mark":value=value.replace(inject_mark,'meta mark & 0x40000000 == 0x00000000 return',1) if name=="split_coexistence_rejects_late_mark": diff --git a/tests/tailscale_nft_ownership_contract.py b/tests/tailscale_nft_ownership_contract.py index 0e434877..0841d4dd 100644 --- a/tests/tailscale_nft_ownership_contract.py +++ b/tests/tailscale_nft_ownership_contract.py @@ -20,7 +20,6 @@ def function(name): type filter hook output priority mangle; policy accept; meta mark & 0x40000000 == 0x40000000 return %%MARK%% - oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return oifname "enp1s0" tcp dport 80 ct original packets 1-6 queue flags bypass to 200 oifname "enp1s0" tcp dport 443 ct original packets 1-6 queue flags bypass to 200 oifname "enp1s0" udp dport 443 ct original packets 1-6 queue flags bypass to 200 @@ -31,8 +30,8 @@ def function(name): MARK='meta mark & 0x00ff0000 == 0x00080000 return' CASES=[ ("coexist_mark_and_interface", "system-preserved", MARK, "enp1s0", True), - ("coexist_missing_tcp443_syn_exemption", "system-preserved", MARK, "enp1s0", False), - ("coexist_bad_tcp443_syn_exemption", "system-preserved", MARK, "enp1s0", False), + ("coexist_unexpected_tcp443_syn_exemption", "system-preserved", MARK, "enp1s0", False), + ("coexist_wrong_tcp443_syn_exemption", "system-preserved", MARK, "enp1s0", False), ("coexist_missing_injected_mark", "system-preserved", MARK, "enp1s0", False), ("coexist_bad_injected_mark", "system-preserved", MARK, "enp1s0", False), ("coexist_missing_bypass_mark", "system-preserved", "", "enp1s0", False), @@ -61,8 +60,8 @@ def function(name): env["MOCK_NFT_TABLE"]=str(table) for name,mode,mark,iface,expected in CASES: value=table_template.replace("%%MARK%%",mark) - if name=="coexist_missing_tcp443_syn_exemption":value=value.replace('oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return',"",1) - if name=="coexist_bad_tcp443_syn_exemption":value=value.replace('oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return','oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == ack return',1) + if name=="coexist_unexpected_tcp443_syn_exemption":value=value.replace('oifname "enp1s0" tcp dport 80', 'oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return\n oifname "enp1s0" tcp dport 80',1) + if name=="coexist_wrong_tcp443_syn_exemption":value=value.replace('oifname "enp1s0" tcp dport 80', 'oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == ack return\n oifname "enp1s0" tcp dport 80',1) if name=="coexist_missing_injected_mark":value=value.replace(INJECT_MARK,"",1) if name=="coexist_bad_injected_mark":value=value.replace(INJECT_MARK,'meta mark & 0x40000000 == 0x00000000 return',1) if name=="coexist_bypass_mark_after_first_queue": diff --git a/tests/tailscale_transient_rollback_ownership_contract.py b/tests/tailscale_transient_rollback_ownership_contract.py index 15e55e55..ca3d755e 100644 --- a/tests/tailscale_transient_rollback_ownership_contract.py +++ b/tests/tailscale_transient_rollback_ownership_contract.py @@ -26,15 +26,14 @@ def fragment(name,optional=False): 'oifname "enp1s0" tcp dport 443 ct original packets 1-6 queue flags bypass to 200', 'oifname "enp1s0" udp dport 443 ct original packets 1-6 queue flags bypass to 200', ] -synskip='oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return' -valid=[inject_mark,mark,synskip]+queue +valid=[inject_mark,mark]+queue cases=[ ("coexist_empty_table","table inet directinternetmethod {\n}\n","1",True), ("coexist_empty_chain",fixture.replace("%%RULES%%",""),"1",True), ("coexist_injected_mark_only",fixture.replace("%%RULES%%",inject_mark),"1",True), ("coexist_injected_and_ts_mark_only",fixture.replace("%%RULES%%",inject_mark+"\n"+mark),"1",True), - ("coexist_missing_syn_exemption",fixture.replace("%%RULES%%","\n".join([inject_mark,mark]+queue)),"1",False), - ("coexist_wrong_syn_exemption",fixture.replace("%%RULES%%","\n".join([inject_mark,mark,synskip.replace("== syn","== ack")]+queue)),"1",False), + ("coexist_foreign_old_syn_exemption",fixture.replace("%%RULES%%","\n".join([inject_mark,mark,'oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == syn return']+queue)),"1",False), + ("coexist_wrong_syn_exemption",fixture.replace("%%RULES%%","\n".join([inject_mark,mark,'oifname "enp1s0" tcp dport 443 tcp flags & (syn | ack) == ack return']+queue)),"1",False), ("coexist_missing_injected_mark",fixture.replace("%%RULES%%",mark),"1",False), ("coexist_wrong_injected_mark",fixture.replace("%%RULES%%",'meta mark & 0x40000000 == 0x00000000 return'+"\n"+mark),"1",False), ("coexist_partial_rules",fixture.replace("%%RULES%%","\n".join(valid[:2])),"1",True), @@ -44,7 +43,7 @@ def fragment(name,optional=False): ("coexist_wrong_order",fixture.replace("%%RULES%%","\n".join([queue[0],mark])),"1",False), ("standalone_foreign_mark",fixture.replace("%%RULES%%","\n".join(valid)),"0",False), ("standalone_injected_mark_only",fixture.replace("%%RULES%%",inject_mark),"0",True), - ("standalone_injected_and_queue",fixture.replace("%%RULES%%","\n".join([inject_mark,synskip]+queue)),"0",True), + ("standalone_injected_and_queue",fixture.replace("%%RULES%%","\n".join([inject_mark]+queue)),"0",True), ("coexist_foreign_chain",fixture.replace("%%RULES%%","\n".join(valid)).replace("\n}\n","\n chain input { type filter hook input priority filter; policy accept; }\n}\n"),"1",False), ] with tempfile.TemporaryDirectory(prefix="dim_transient_rollback_") as td: From a8072ce9a2f31cc733438c2ce0ff992173e8338f Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 17 Oct 2026 23:29:00 +0330 Subject: [PATCH 132/188] feat(linux): explicit selected-site local SOCKS opt-in with route guard --- .github/workflows/ci.yml | 26 +- build_linux_package.py | 1 + linux/app/direct_internet_method.py | 82 ++- linux/app/socket_fallback.py | 500 ++++++++++++++++++ linux/app/user_update_transaction.py | 3 +- tests/r519_versioned_package_contract.py | 10 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ...9_linux_update_cache_lifecycle_contract.py | 2 +- tests/r747_gui_local_socket_optin_contract.py | 147 +++++ ...47_optin_local_socket_fallback_contract.py | 354 +++++++++++++ 12 files changed, 1105 insertions(+), 26 deletions(-) create mode 100644 linux/app/socket_fallback.py create mode 100644 tests/r747_gui_local_socket_optin_contract.py create mode 100644 tests/r747_optin_local_socket_fallback_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a5731d3..7773d1ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,16 +39,16 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503" - assert p.is_file() and p.stat().st_size==8380692 + expected="697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648" + assert p.is_file() and p.stat().st_size==8405044 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" with zipfile.ZipFile(p) as z: root="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" - assert len(z.namelist())==29 and len(z.namelist())==len(set(z.namelist())) + assert len(z.namelist())==30 and len(z.namelist())==len(set(z.namelist())) j=json.loads(z.read(root+"PACKAGE_MANIFEST.json")) - assert j["version"]=="1.5.5-rc1" and len(j["files"])==27 + assert j["version"]=="1.5.5-rc1" and len(j["files"])==28 for x in j["files"]: data=z.read(root+x["file"]) assert len(data)==x["bytes"] @@ -89,6 +89,8 @@ jobs: python3 -B tests/r678_linux_gui_full_youtube_health_contract.py python3 -B tests/r735_linux_live_core_truth_contract.py python3 -B tests/r739_linux_update_cache_lifecycle_contract.py + python3 -B tests/r747_optin_local_socket_fallback_contract.py + python3 -B tests/r747_gui_local_socket_optin_contract.py python3 -B tests/r698_root_watchdog_firstsplit_integrity_contract.py python3 -B tests/r702_nfqws_reinjected_mark_contract.py python3 -B tests/r709_installer_no_bytecode_cache_contract.py @@ -165,7 +167,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -194,7 +196,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -227,7 +229,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -256,7 +258,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -285,7 +287,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -315,7 +317,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -360,7 +362,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -386,7 +388,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/build_linux_package.py b/build_linux_package.py index e775b0aa..5eb58b15 100644 --- a/build_linux_package.py +++ b/build_linux_package.py @@ -18,6 +18,7 @@ ("linux/system/upgrade_system.sh","system/upgrade_system.sh"), ("linux/system/uninstall_system.sh","system/uninstall_system.sh"), ("linux/app/direct_internet_method.py","app/direct_internet_method.py"), + ("linux/app/socket_fallback.py","app/socket_fallback.py"), ("linux/app/router_gateway.py","app/router_gateway.py"), ("linux/app/router_gateway_core.py","app/router_gateway_core.py"), ("linux/app/user_update_transaction.py","app/user_update_transaction.py"), diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index a86dbfba..0a906d2c 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -22,6 +22,7 @@ gi.require_version("Adw","1") from gi.repository import Gtk, Adw, GLib, Gdk from router_gateway import RouterGatewayWindow +from socket_fallback import LocalSocketFallback APP_ID="io.github.god13emad.DirectInternetMethod" VERSION="1.5.5-rc1" @@ -618,7 +619,7 @@ def download_and_install_update(info): prefix=archive_name.removesuffix(".zip")+"/" with zipfile.ZipFile(zip_path) as z: members=z.infolist() - if len(members)!=29 or len({m.filename for m in members})!=len(members): + if len(members)!=30 or len({m.filename for m in members})!=len(members): raise RuntimeError("Linux update archive has an unexpected file inventory.") names={} total=0 @@ -647,7 +648,7 @@ def download_and_install_update(info): manifest.get("version")!=release_version or manifest.get("platform")!="linux-x86_64" or not isinstance(manifest.get("files"),list) or - len(manifest["files"])!=27): + len(manifest["files"])!=28): raise RuntimeError("Linux update package identity does not match release.") referenced=set() expected_lines=[] @@ -698,10 +699,12 @@ def download_and_install_update(info): class Window(Adw.ApplicationWindow): def __init__(self,app): super().__init__(application=app,title="Direct Internet Method") - self.set_default_size(900,590) - self.set_size_request(780,540) + self.set_default_size(900,685) + self.set_size_request(780,615) self.set_resizable(True) self.available_update=None + # R747: local socket alternative exists only for this GUI lifetime. + self.local_socket_bridge=None self.adult_check_enabled=load_adult_site_check() if self.adult_check_enabled: try: save_adult_site_check(True) @@ -761,6 +764,29 @@ def __init__(self,app): self.adult_live=Gtk.Label(label="Adult coverage: "+("Checking…" if self.adult_check_enabled else "Disabled"),xalign=0);self.adult_live.add_css_class("dim-label");live.append(self.adult_live) box.append(live) + # R747: independent, explicit, session-only SOCKS5 opt-in. + # Never edits the user's Firefox/Brave/system proxy configuration. + local_card=Gtk.Box(orientation=Gtk.Orientation.VERTICAL,spacing=7) + local_card.add_css_class("card") + local_heading=Gtk.Label(label="Optional selected-site local SOCKS5 (manual app opt-in)",xalign=0) + local_heading.add_css_class("heading") + local_card.append(local_heading) + self.local_socket_status=Gtk.Label( + label="OFF — only approved public YouTube-related sites and three service controls; " + "not system-wide access.",xalign=0,wrap=True) + self.local_socket_status.add_css_class("dim-label") + local_card.append(self.local_socket_status) + local_buttons=Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,spacing=8) + self.local_socket_start_button=Gtk.Button(label="Start local SOCKS5 (session only)") + self.local_socket_stop_button=Gtk.Button(label="Stop local SOCKS5") + self.local_socket_stop_button.set_sensitive(False) + self.local_socket_start_button.connect("clicked",self.on_local_socket_start) + self.local_socket_stop_button.connect("clicked",self.on_local_socket_stop) + local_buttons.append(self.local_socket_start_button) + local_buttons.append(self.local_socket_stop_button) + local_card.append(local_buttons) + box.append(local_card) + buttons=Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,spacing=8);buttons.set_homogeneous(True) self.buttons={} for text_,action,css in [ @@ -780,10 +806,55 @@ def __init__(self,app): # Observe route/process changes without doing periodic web requests. self._status_timer_id=GLib.timeout_add_seconds(15,self.periodic_state_tick) self.connect("close-request",self.stop_status_timer) + self.connect("close-request",self.close_local_socket) if self.adult_check_enabled: threading.Thread(target=self.adult_sync_worker,args=(False,),daemon=True).start() threading.Thread(target=self.update_check_worker,args=(False,),daemon=True).start() + def on_local_socket_start(self,*_args): + # The exact user click is the only enabling action. No persistent + # configuration, no Root service and no changes to browser/DNS/proxy. + if self.local_socket_bridge is not None: + return + try: + if STATE.is_symlink() or STATE.exists(): + raise RuntimeError("Stop the Direct Method engine first.") + if _external_tunnel_active(): + raise RuntimeError("An unsupported external tunnel is active.") + candidate=LocalSocketFallback() + try: + port=candidate.start() + except BaseException: + candidate.stop() + raise + self.local_socket_bridge=candidate + except Exception as exc: + self.local_socket_status.set_text("Local SOCKS5 OFF — "+str(exc)) + return + self.local_socket_start_button.set_sensitive(False) + self.local_socket_stop_button.set_sensitive(True) + self.local_socket_status.set_text( + f"ON — SOCKS5 127.0.0.1:{port}. Configure ONLY your chosen app " + "with remote DNS (socks5h). Only allowlisted HTTPS destinations; " + "no system/global proxy. Your app validates TLS certificates. " + "Stop here or close this GUI to disable." + ) + + def on_local_socket_stop(self,*_args): + self.close_local_socket() + self.local_socket_status.set_text( + "OFF — no localhost SOCKS5 process, no system proxy changes." + ) + + def close_local_socket(self,*_args): + bridge=self.local_socket_bridge + self.local_socket_bridge=None + if bridge is not None: + bridge.stop() + self.local_socket_start_button.set_sensitive(True) + self.local_socket_stop_button.set_sensitive(False) + return False + def toggle_maximize(self,*_args): if self.is_maximized(): self.unmaximize() @@ -1063,6 +1134,9 @@ def do_save(*_): def on_action(self,_button,action): ui_test_log("ACTION:"+action) + if action=="start" and self.local_socket_bridge is not None: + self.result.set_text("Stop the optional local SOCKS5 before starting system DPI.") + return if action=="refresh": self.refresh();self.result.set_text("");return if action=="router": diff --git a/linux/app/socket_fallback.py b/linux/app/socket_fallback.py new file mode 100644 index 00000000..859f68de --- /dev/null +++ b/linux/app/socket_fallback.py @@ -0,0 +1,500 @@ +#!/usr/bin/env python3 +"""Opt-in, per-GUI-session, bounded localhost SOCKS5 fallback for selected sites. + +R747: no third-party tpws binary, no Root, no transparent network interception, +no changes to system/browser proxies, DNS, routes or Tailscale. This is NOT +the normal Direct Method Start/Stop/Recovery backend and not global internet. + +Only CONNECT/domain/443 to a small static public-site allowlist is allowed. +YouTube-family ClientHello is split in two write() calls after the first byte; +other allowed hosts are passed through intact. TLS is never decrypted; +the client MUST still validate the destination certificate. +""" +from __future__ import annotations + +import ipaddress +import pathlib +import re +import select +import socket +import socketserver +import subprocess +import threading +import time +from dataclasses import dataclass + +# Deliberately no wildcard goog(le).com or private/LAN traffic. +SEGMENT_SUFFIXES = ( + "youtube.com", "youtu.be", "googlevideo.com", "ytimg.com", + "youtube-nocookie.com", +) +CONTROL_HOSTS = frozenset(("github.com", "api.openai.com", "gemini.google.com")) +MAX_CLIENT_HELLO = 18432 +MAX_SESSIONS = 12 +MAX_IDLE_SECONDS = 300 + + +@dataclass(frozen=True) +class PhysicalRoute: + iface: str + source_ip: str + + +def current_physical_route() -> PhysicalRoute: + """Select a unique lowest-metric physical IPv4 route and verify OS policy. + + A connected Wi-Fi and Ethernet often create two default routes. This is + normal, not an ownership conflict. A tied best metric, a tunnel winning, + or an effective route different from the selected physical link is unsafe. + No packets are sent by the ip route get lookup. + """ + ip_cmd = ("/usr/sbin/ip" if pathlib.Path("/usr/sbin/ip").is_file() + else "/usr/bin/ip") + proc = subprocess.run( + (ip_cmd, "-o", "-4", "route", "show", "default"), + text=True, capture_output=True, timeout=3, check=False, + ) + if proc.returncode: + raise RuntimeError("SOCKET_ROUTE_LOOKUP_FAILED") + candidates = [] + for line in proc.stdout.splitlines(): + fields = line.split() + if not fields: + continue + if (fields[0] != "default" or fields.count("dev") != 1 or + fields.count("src") != 1 or fields.count("metric") > 1): + raise RuntimeError("SOCKET_PHYSICAL_DEFAULT_ROUTE_UNVERIFIED") + try: + iface = fields[fields.index("dev") + 1] + src = fields[fields.index("src") + 1] + metric = (int(fields[fields.index("metric") + 1]) + if "metric" in fields else 0) + if metric < 0: + raise ValueError("negative metric") + except (IndexError, ValueError) as exc: + raise RuntimeError("SOCKET_PHYSICAL_DEFAULT_ROUTE_UNVERIFIED") from exc + candidates.append((metric, iface, src)) + if not candidates: + raise RuntimeError("SOCKET_PHYSICAL_DEFAULT_ROUTE_UNAVAILABLE") + best_metric = min(m for m, _, _ in candidates) + winners = [(iface, src) for m, iface, src in candidates if m == best_metric] + if len(winners) != 1: + raise RuntimeError("SOCKET_PHYSICAL_DEFAULT_ROUTE_AMBIGUOUS") + iface, src = winners[0] + if not re.fullmatch(r"[A-Za-z0-9_.-]{1,32}", iface): + raise RuntimeError("SOCKET_BAD_INTERFACE_NAME") + if iface.startswith(("tailscale", "wg", "tun", "tap", "zt", "warp", "dim")): + raise RuntimeError("SOCKET_VPN_OR_TUNNEL_AS_DEFAULT_ROUTE") + try: + ip = ipaddress.IPv4Address(src) + except ValueError as exc: + raise RuntimeError("SOCKET_BAD_PHYSICAL_SOURCE_IPV4") from exc + if ip.is_loopback or ip.is_multicast or ip.is_unspecified or ip.is_link_local: + raise RuntimeError("SOCKET_INVALID_PHYSICAL_IP") + + # Main-table metrics alone are insufficient under policy routing/Tailscale + # exit nodes. Check a public destination's actual unmarked kernel lookup. + effective = subprocess.run( + (ip_cmd, "-4", "route", "get", "1.1.1.1"), + text=True, capture_output=True, timeout=3, check=False, + ) + if effective.returncode: + raise RuntimeError("SOCKET_EFFECTIVE_POLICY_ROUTE_UNAVAILABLE") + actual = [row.split() for row in effective.stdout.splitlines() + if "dev" in row.split()] + if len(actual) != 1: + raise RuntimeError("SOCKET_EFFECTIVE_POLICY_ROUTE_AMBIGUOUS") + fields = actual[0] + try: + if (fields.count("dev") != 1 or fields.count("src") != 1 or + fields[fields.index("dev") + 1] != iface or + fields[fields.index("src") + 1] != str(ip)): + raise RuntimeError("SOCKET_EFFECTIVE_POLICY_ROUTE_MISMATCH") + except IndexError as exc: + raise RuntimeError("SOCKET_EFFECTIVE_POLICY_ROUTE_UNVERIFIED") from exc + return PhysicalRoute(iface, str(ip)) + + + +def verify_target_physical_route(ip: str, route: PhysicalRoute) -> bool: + """Fail closed on policy routes that steer a specific destination away. + + Checking 1.1.1.1 alone cannot detect Tailscale/VPN split routes to the + actual resolved target. ip route get DEST from SOURCE ipproto tcp dport + 443 is a read-only kernel lookup, not a mutation or packet transmission. + """ + try: + target = ipaddress.IPv4Address(ip) + if not target.is_global or not isinstance(route, PhysicalRoute): + return False + if (not re.fullmatch(r"[A-Za-z0-9_.-]{1,32}", route.iface) or + route.iface.startswith(("tailscale", "wg", "tun", "tap", "zt", "warp", "dim"))): + return False + source = ipaddress.IPv4Address(route.source_ip) + if source.is_unspecified or source.is_multicast: + return False + command = ("/usr/sbin/ip" if pathlib.Path("/usr/sbin/ip").is_file() + else "/usr/bin/ip") + check = subprocess.run( + (command, "-o", "-4", "route", "get", str(target), "from", + str(source), "ipproto", "tcp", "dport", "443"), + text=True, capture_output=True, timeout=3, check=False, + ) + if check.returncode != 0: + return False + # Linux emits an optional second "cache" row with no "dev". + candidates = [line.split() for line in check.stdout.splitlines() + if "dev" in line.split()] + if len(candidates) != 1: + return False + fields = candidates[0] + if fields[0] != str(target) or fields.count("dev") != 1: + return False + if fields[fields.index("dev") + 1] != route.iface: + return False + # With from SOURCE iproute2 may print from SOURCE rather than src. + sources = [fields[i + 1] for i, token in enumerate(fields[:-1]) + if token in ("from", "src")] + return bool(sources) and all(value == str(source) for value in sources) + except (OSError, ValueError, IndexError, subprocess.TimeoutExpired): + return False + + +def normalize_allowed_host(host: str) -> tuple[str, bool]: + """(host, split) or fail-closed. A substring of an allowed host is NOT OK.""" + if not isinstance(host, str) or not 1 <= len(host) <= 253: + raise ValueError("SOCKET_HOST_LENGTH_INVALID") + try: + host.encode("ascii", "strict") + except UnicodeError as exc: + raise ValueError("SOCKET_HOST_NON_ASCII") from exc + lower = host.lower() + if lower != host.strip().lower() or lower.endswith("."): + raise ValueError("SOCKET_HOST_CANONICALIZATION_REJECTED") + labels = lower.split(".") + if (len(labels) < 2 or + not all(1 <= len(x) <= 63 and + re.fullmatch(r"[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", x) + for x in labels)): + raise ValueError("SOCKET_HOST_LABELS_INVALID") + is_video = any(lower == suffix or lower.endswith("." + suffix) + for suffix in SEGMENT_SUFFIXES) + if not is_video and lower not in CONTROL_HOSTS: + raise ValueError("SOCKET_HOST_NOT_IN_EXPLICIT_ALLOWLIST") + return lower, is_video + + +def validate_public_addresses(addresses: list[str]) -> list[str]: + """DNS rebinding/private-network hard stop, before attempting any TCP.""" + if not addresses or len(addresses) > 32: + raise ValueError("SOCKET_DNS_NO_BOUNDED_IPV4_RESULTS") + unique = [] + for raw in addresses: + try: + ip = ipaddress.IPv4Address(raw) + except ValueError as exc: + raise ValueError("SOCKET_DNS_INVALID_IPV4") from exc + if (not ip.is_global or ip.is_multicast or ip.is_reserved or + ip.is_unspecified or ip.is_link_local): + raise ValueError("SOCKET_DNS_PRIVATE_OR_RESERVED_IPV4_REJECTED") + if str(ip) not in unique: + unique.append(str(ip)) + return unique + + +def resolve_public_ipv4(host: str) -> list[str]: + answers = socket.getaddrinfo(host, 443, socket.AF_INET, + socket.SOCK_STREAM, socket.IPPROTO_TCP) + return validate_public_addresses([row[4][0] for row in answers]) + + +def tls_hello_sni(record: bytes) -> str: + """Parse exactly the initial complete TLS ClientHello record, not TLS keys.""" + if (len(record) < 52 or len(record) > MAX_CLIENT_HELLO or record[0] != 22 or + record[1] != 3 or record[2] not in (0, 1, 2, 3, 4) or + int.from_bytes(record[3:5], "big") != len(record) - 5): + raise ValueError("SOCKET_TLS_RECORD_INVALID") + body = memoryview(record)[5:] + if body[0] != 1 or int.from_bytes(body[1:4], "big") > len(body) - 4: + raise ValueError("SOCKET_TLS_CLIENTHELLO_INCOMPLETE_OR_INVALID") + # Reject multi-handshake records and fragmented ClientHello instead of + # altering an unknown stream or forwarding it without user-intended split. + if int.from_bytes(body[1:4], "big") != len(body) - 4: + raise ValueError("SOCKET_TLS_CLIENTHELLO_RECORD_NOT_EXCLUSIVE") + p = 4 + 2 + 32 + def take(size: int) -> bytes: + nonlocal p + if size < 0 or p + size > len(body): + raise ValueError("SOCKET_TLS_HELLO_TRUNCATED") + raw = bytes(body[p:p + size]) + p += size + return raw + take(take(1)[0]) + take(int.from_bytes(take(2), "big")) # cipher suites + take(take(1)[0]) # compression methods + extensions_len = int.from_bytes(take(2), "big") + end = p + extensions_len + if end != len(body): + raise ValueError("SOCKET_TLS_EXTENSION_LENGTH_INVALID") + found = None + while p < end: + kind = int.from_bytes(take(2), "big") + length = int.from_bytes(take(2), "big") + value = take(length) + if kind != 0: + continue + if found is not None or len(value) < 5: + raise ValueError("SOCKET_TLS_DUPLICATE_OR_BAD_SNI") + count = int.from_bytes(value[:2], "big") + if count != len(value) - 2 or value[2] != 0: + raise ValueError("SOCKET_TLS_SNI_LIST_INVALID") + n = int.from_bytes(value[3:5], "big") + if n == 0 or n != len(value) - 5: + raise ValueError("SOCKET_TLS_SNI_NAME_LENGTH_INVALID") + try: + found = value[5:].decode("ascii", "strict") + except UnicodeError as exc: + raise ValueError("SOCKET_TLS_SNI_NON_ASCII") from exc + if found is None: + raise ValueError("SOCKET_TLS_SNI_REQUIRED") + return normalize_allowed_host(found)[0] + + +def send_initial_tls(upstream, record: bytes, *, split: bool) -> None: + """Only the first TLS record is intentionally segmented; no sleeps.""" + if not record or record[0] != 22: + raise ValueError("SOCKET_INITIAL_TLS_RECORD_INVALID") + if split: + upstream.sendall(record[:1]) + upstream.sendall(record[1:]) + else: + upstream.sendall(record) + + +def _read_exact(sock: socket.socket, count: int) -> bytes: + if not 0 <= count <= MAX_CLIENT_HELLO: + raise ValueError("SOCKET_FRAME_LENGTH_INVALID") + output = bytearray() + while len(output) < count: + data = sock.recv(count - len(output)) + if not data: + raise ConnectionError("SOCKET_UNEXPECTED_CLIENT_EOF") + output.extend(data) + return bytes(output) + + +def _reply(client: socket.socket, code: int) -> None: + # RFC 1928, bind address/port = 0 (no misleading external IP disclosure). + client.sendall(b"\x05" + bytes([code]) + + b"\x00\x01\x00\x00\x00\x00\x00\x00") + + +def dial_public_ipv4(ip: str, port: int, source_ip: str) -> socket.socket: + """Connect to a previously validated public address using physical source.""" + remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + remote.settimeout(4) + remote.bind((source_ip, 0)) + remote.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + remote.connect((ip, port)) + return remote + except BaseException: + remote.close() + raise + + +def _handle_request(bridge: "LocalSocketFallback", client: socket.socket) -> None: + client.settimeout(8) + header = _read_exact(client, 2) + if header[0] != 5 or not 1 <= header[1] <= 8: + return + methods = _read_exact(client, header[1]) + if b"\x00" not in methods: + client.sendall(b"\x05\xff") + return + client.sendall(b"\x05\x00") + ver, operation, reserved, atyp = _read_exact(client, 4) + if (ver, operation, reserved, atyp) != (5, 1, 0, 3): + _reply(client, 7 if operation != 1 else 8) + return + hostname_length = _read_exact(client, 1)[0] + if not 1 <= hostname_length <= 253: + _reply(client, 8) + return + try: + raw = _read_exact(client, hostname_length).decode("ascii", "strict") + host, split = normalize_allowed_host(raw) + except (ValueError, UnicodeError): + _reply(client, 2) + return + port = int.from_bytes(_read_exact(client, 2), "big") + if port != 443: + _reply(client, 2) + return + try: + # A new connection must not quietly migrate to another interface, + # VPN default route or physical source after this GUI session started. + if bridge._route_getter() != bridge.route: + raise RuntimeError("SOCKET_PHYSICAL_ROUTE_CHANGED") + addresses = validate_public_addresses(bridge._resolver(host)) + # Validate every DNS answer before dialing ANY destination. A single + # VPN-specific route makes this session fail closed, not a fallback. + if not all(bridge._destination_route_getter(ip, bridge.route) is True + for ip in addresses): + raise RuntimeError("SOCKET_DESTINATION_POLICY_ROUTE_MISMATCH") + except (OSError, ValueError, RuntimeError): + _reply(client, 2) + return + remote = None + for ip in addresses: + if bridge._stopping.is_set(): + return + try: + remote = bridge._dialer(ip, port, bridge.route.source_ip) + break + except OSError: + continue + if remote is None: + _reply(client, 4) + return + bridge._track(remote) + try: + _reply(client, 0) + first = _read_exact(client, 5) + length = int.from_bytes(first[3:5], "big") + if length < 48 or length + 5 > MAX_CLIENT_HELLO: + raise ValueError("SOCKET_TLS_FIRST_RECORD_LENGTH_UNSAFE") + record = first + _read_exact(client, length) + if tls_hello_sni(record) != host: + raise ValueError("SOCKET_TLS_SNI_CONNECT_HOST_MISMATCH") + send_initial_tls(remote, record, split=split) + client.settimeout(15) + remote.settimeout(15) + last_activity = time.monotonic() + next_route_check = last_activity + 15 + while not bridge._stopping.is_set(): + now = time.monotonic() + if now - last_activity > MAX_IDLE_SECONDS: + return + if now >= next_route_check: + if bridge._route_getter() != bridge.route: + return + next_route_check = now + 15 + ready, _, _ = select.select((client, remote), (), (), 0.3) + for source in ready: + payload = source.recv(32768) + if not payload: + return + (remote if source is client else client).sendall(payload) + last_activity = time.monotonic() + finally: + bridge._untrack(remote) + try: + remote.shutdown(socket.SHUT_RDWR) + except OSError: + pass + remote.close() + + +class _LoopbackServer(socketserver.ThreadingTCPServer): + daemon_threads = True + block_on_close = False + request_queue_size = MAX_SESSIONS + allow_reuse_address = False + + def verify_request(self, request, client_address): + return client_address[0] == "127.0.0.1" + + +class _Handler(socketserver.BaseRequestHandler): + def handle(self) -> None: + bridge = self.server.bridge + if not bridge._slots.acquire(blocking=False): + try: + _reply(self.request, 1) + except OSError: + pass + return + bridge._track(self.request) + try: + _handle_request(bridge, self.request) + except (OSError, ValueError, ConnectionError, TimeoutError): + # Never log URL, hostname, DNS, private browsing data, or bytes. + pass + finally: + bridge._untrack(self.request) + bridge._slots.release() + + +class LocalSocketFallback: + """Start only on explicit opt-in; stop on explicit button or GUI close.""" + def __init__(self, *, route_getter=current_physical_route, + resolver=resolve_public_ipv4, dialer=dial_public_ipv4, + destination_route_getter=verify_target_physical_route): + self._route_getter = route_getter + self._resolver = resolver + self._dialer = dialer + self._destination_route_getter = destination_route_getter + self._slots = threading.BoundedSemaphore(MAX_SESSIONS) + self._sockets: set[socket.socket] = set() + self._lock = threading.Lock() + self._stopping = threading.Event() + self._server = None + self._thread = None + self.route = None + + @property + def port(self) -> int | None: + return (self._server.server_address[1] if self._server else None) + + def start(self) -> int: + if self._server: + raise RuntimeError("SOCKET_SESSION_ALREADY_RUNNING") + self.route = self._route_getter() + if not isinstance(self.route, PhysicalRoute): + raise RuntimeError("SOCKET_PHYSICAL_ROUTE_UNVERIFIED") + self._stopping.clear() + server = _LoopbackServer(("127.0.0.1", 0), _Handler, + bind_and_activate=True) + if server.server_address[0] != "127.0.0.1" or server.server_address[1] < 1024: + server.server_close() + raise RuntimeError("SOCKET_NOT_PRIVATE_DYNAMIC_LOOPBACK_PORT") + server.bridge = self + self._server = server + thread = threading.Thread(target=server.serve_forever, + kwargs={"poll_interval": 0.1}, + name="DIM-Local-Optin-SOCKS", + daemon=True) + self._thread = thread + thread.start() + return self.port + + def _track(self, sock: socket.socket) -> None: + with self._lock: + self._sockets.add(sock) + + def _untrack(self, sock: socket.socket) -> None: + with self._lock: + self._sockets.discard(sock) + + def stop(self) -> None: + self._stopping.set() + server, self._server = self._server, None + if server is None: + return + server.shutdown() + server.server_close() + with self._lock: + sockets = tuple(self._sockets) + for sock in sockets: + try: + sock.shutdown(socket.SHUT_RDWR) + except OSError: + pass + try: + sock.close() + except OSError: + pass + if self._thread and self._thread.is_alive(): + self._thread.join(timeout=2) + self._thread = None diff --git a/linux/app/user_update_transaction.py b/linux/app/user_update_transaction.py index 3d88ca50..14004440 100644 --- a/linux/app/user_update_transaction.py +++ b/linux/app/user_update_transaction.py @@ -23,6 +23,7 @@ FILES = ( ("app/direct_internet_method.py", "direct_internet_method.py", 0o755), + ("app/socket_fallback.py", "socket_fallback.py", 0o644), ("app/router_gateway.py", "router_gateway.py", 0o755), ("app/router_gateway_core.py", "router_gateway_core.py", 0o644), ("app/direct-internet-method.svg", "direct-internet-method.svg", 0o644), @@ -154,7 +155,7 @@ def source_files(root: pathlib.Path) -> dict: package_doc.get("version") == "1.5.5-rc1" and package_doc.get("platform") == "linux-x86_64" and isinstance(package_doc.get("files"), list) and - len(package_doc["files"]) == 27, "EXTRACTED_PACKAGE_IDENTITY_WRONG") + len(package_doc["files"]) == 28, "EXTRACTED_PACKAGE_IDENTITY_WRONG") seen = set() digest_lines = [] for item in package_doc["files"]: diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 1a966939..79d4bc9f 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":27,"sha256":"C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503"} +EXPECTED={"version":VERSION,"files":28,"sha256":"697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -82,14 +82,14 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual((prev["schema"],prev["version"],prev["platform"]),(1,VERSION,"linux-x86_64")) before={row["file"]:row for row in prev["files"]} after={row["file"]:row for row in self.rows} - self.assertEqual(set(after),set(before)|{"system/upgrade_guard.py"}) + self.assertEqual(set(after),set(before)|{"system/upgrade_guard.py","app/socket_fallback.py"}) self.assertEqual(len(before),26) changed={name for name in before if before[name]!=after[name]} self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh"}) self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "57B452CF570E8A1545340F13A140E9E6D86F4F2B6F282BA2AA3D992F64FE11AF") + "6CF031D2C7F18D7E7E4F1F9A37A1A0EBD3CA589F16F7E26274496F1F53E9317B") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', @@ -109,7 +109,7 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual(digest(old.read_bytes()), "47776640A1BB8B5D4490244565DDD47A6A817A999347566917CC73E21F41EC24") with zipfile.ZipFile(old) as z: - self.assertEqual(set(self.n),set(z.namelist())|{self.prefix+"system/upgrade_guard.py"}) + self.assertEqual(set(self.n),set(z.namelist())|{self.prefix+"system/upgrade_guard.py",self.prefix+"app/socket_fallback.py"}) self.assertEqual(z.read(self.prefix+"PACKAGE_MANIFEST.json"),old_manifest_bytes) self.assertEqual(z.read(self.prefix+"app/user_uninstall_preserve.py"),old_source.read_bytes()) self.assertEqual(z.testzip(),None) @@ -153,7 +153,7 @@ def test_gui_and_user_metadata_pins(self): self.assertIn('COEXIST_HELPER_SHA256="'+digest(HELPER.read_bytes()).lower()+'"',app) self.assertIn('installed.get("version") == "1.5.5-rc1"',app) updater=USER_TXN.read_text() - self.assertIn('len(package_doc["files"]) == 27',updater) + self.assertIn('len(package_doc["files"]) == 28',updater) self.assertIn('data.get("version") == "1.5.5-rc1"',updater) self.assertIn('"encrypted-dns-optin.txt"',updater) def test_rc_semver_prevents_implicit_downgrade(self): diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index f6dc975b..67ca97aa 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="c8e5a06387ff21ec0e048f8bb02dc1b4c31b364625e18ec641900356f60ce503" +CANDIDATE_SHA="697e1445ecc70148193227d6726aaeace7ab1dfb6a3b89650487e474f476f648" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 7b335ec5..1780f1ee 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503" +PIN_PACKAGE="697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" PIN_GUARD="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index a97df9fc..57a511d9 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="C8E5A06387FF21EC0E048F8BB02DC1B4C31B364625E18EC641900356F60CE503" +ARCHIVE="697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r739_linux_update_cache_lifecycle_contract.py b/tests/r739_linux_update_cache_lifecycle_contract.py index e064238c..657ca840 100644 --- a/tests/r739_linux_update_cache_lifecycle_contract.py +++ b/tests/r739_linux_update_cache_lifecycle_contract.py @@ -41,7 +41,7 @@ def build_zip(kind="good"): source = {"install.sh": b"#!/bin/bash\nexit 0\n"} source.update({f"app/fixture{i:02d}.bin": f"fixture-{i}\n".encode() - for i in range(26)}) + for i in range(27)}) if kind == "missing_installer": source["another_installer.sh"] = source.pop("install.sh") rows = [{"file": key, "bytes": len(raw), diff --git a/tests/r747_gui_local_socket_optin_contract.py b/tests/r747_gui_local_socket_optin_contract.py new file mode 100644 index 00000000..dfec2784 --- /dev/null +++ b/tests/r747_gui_local_socket_optin_contract.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""R747 GUI opt-in contract: real production Window methods, no GTK or network. + +The opt-in SOCKS process is never created by Application/Window constructor; +only a clicked Start action may create it. Close must dispose it and restore +control state. No system/root/Tailscale/browser proxy settings are touched. +""" +from __future__ import annotations + +import ast +import pathlib +import types +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SOURCE=(ROOT/"linux/app/direct_internet_method.py").read_text("utf-8") +APP=ast.parse(SOURCE) +WINDOW=next(n for n in APP.body if isinstance(n,ast.ClassDef) and n.name=="Window") +METHODS={n.name:n for n in WINDOW.body if isinstance(n,ast.FunctionDef)} +NEED=("on_local_socket_start","on_local_socket_stop","close_local_socket","on_action") +assert all(n in METHODS for n in NEED),"R747_REQUIRED_GUI_CONTROLS_MISSING" +BODY=ast.unparse(next(x for x in WINDOW.body if x.name=="__init__")) +assert "self.local_socket_bridge = None" in BODY +assert 'self.local_socket_start_button.connect(' in BODY and 'self.on_local_socket_start' in BODY +assert 'self.local_socket_stop_button.connect(' in BODY and 'self.on_local_socket_stop' in BODY +assert 'self.connect(' in BODY and 'self.close_local_socket' in BODY +assert "candidate.start()" not in BODY and "LocalSocketFallback()" not in BODY +assert "self.local_socket_bridge" in SOURCE +assert "SOCKS5 127.0.0.1" in SOURCE +assert "action == 'start'" in ast.unparse(METHODS["on_action"]) +assert "on_local_socket_start" in SOURCE + +class Button: + def __init__(self):self.enabled=None + def set_sensitive(self,v):self.enabled=bool(v) + +class Label: + def __init__(self):self.value="" + def set_text(self,s):self.value=str(s) + +class State: + symlink=False + present=False + def is_symlink(self):return self.symlink + def exists(self):return self.present + +class TestGUI(unittest.TestCase): + def setUp(self): + self.state=State() + self.tunnel=False + self.starts=0 + self.stops=0 + self.should_fail=False + testcase=self + class Local: + def __init__(self):pass + def start(self): + testcase.starts+=1 + if testcase.should_fail:raise RuntimeError("TEST_BIND_ERROR") + return 47321 + def stop(self):testcase.stops+=1 + env={"STATE":self.state,"_external_tunnel_active":lambda:self.tunnel, + "LocalSocketFallback":Local,"ui_test_log":lambda *args:None} + sub=ast.Module(body=[METHODS[n] for n in NEED],type_ignores=[]) + exec(compile(sub,"r747_actual_GUI_methods","exec"),env) + self.env=env + self.window=types.SimpleNamespace( + local_socket_bridge=None, + local_socket_start_button=Button(),local_socket_stop_button=Button(), + local_socket_status=Label(),result=Label(), + ) + for name in NEED: + setattr(self.window,name,types.MethodType(env[name],self.window)) + + def test_01_direct_user_click_is_only_activation(self): + self.assertEqual(self.starts,0) + self.window.on_local_socket_start() + self.assertEqual(self.starts,1) + self.assertIsNotNone(self.window.local_socket_bridge) + self.assertFalse(self.window.local_socket_start_button.enabled) + self.assertTrue(self.window.local_socket_stop_button.enabled) + self.assertIn("127.0.0.1:47321",self.window.local_socket_status.value) + self.assertIn("manual",SOURCE.lower()) + + def test_02_explicit_stop_disposes_single_instance(self): + self.window.on_local_socket_start() + self.window.on_local_socket_stop() + self.assertEqual(self.stops,1) + self.assertIsNone(self.window.local_socket_bridge) + self.assertTrue(self.window.local_socket_start_button.enabled) + self.assertFalse(self.window.local_socket_stop_button.enabled) + self.window.on_local_socket_stop() + self.assertEqual(self.stops,1) + + def test_03_close_disposes_session_without_admin(self): + self.window.on_local_socket_start() + self.assertIs(self.window.close_local_socket(),False) + self.assertEqual(self.stops,1) + self.assertIsNone(self.window.local_socket_bridge) + + def test_04_reentrant_start_does_not_create_duplicate_listener(self): + self.window.on_local_socket_start() + self.window.on_local_socket_start() + self.assertEqual(self.starts,1) + + def test_05_conflicting_main_engine_denies_fallback(self): + self.state.present=True + self.window.on_local_socket_start() + self.assertEqual(self.starts,0) + self.assertIn("OFF",self.window.local_socket_status.value) + + def test_06_symlink_engine_state_denies_fallback(self): + self.state.symlink=True + self.window.on_local_socket_start() + self.assertEqual(self.starts,0) + + def test_07_external_tunnel_denies_fallback(self): + self.tunnel=True + self.window.on_local_socket_start() + self.assertEqual(self.starts,0) + + def test_08_startup_error_does_not_leave_owned_listener(self): + self.should_fail=True + self.window.on_local_socket_start() + self.assertEqual(self.starts,1) + self.assertEqual(self.stops,1) + self.assertIsNone(self.window.local_socket_bridge) + self.assertIn("TEST_BIND_ERROR",self.window.local_socket_status.value) + + def test_09_normal_system_start_denied_while_local_proxy_active(self): + self.window.on_local_socket_start() + self.window.on_action(None,"start") + self.assertIn("Stop the optional local SOCKS5",self.window.result.value) + + def test_10_no_global_proxy_writes_or_automatic_third_party_bundle(self): + for bad in ("gsettings set org.gnome.system.proxy", + "network.proxy.socks_host =", + "xdg-settings set default-web-browser", + "pkexec /usr/bin","sudo /usr/bin", + "os.environ['ALL_PROXY']", + "os.environ['HTTPS_PROXY']"): + self.assertNotIn(bad,SOURCE) + self.assertIn("from socket_fallback import LocalSocketFallback",SOURCE) + +if __name__=="__main__": + unittest.main(verbosity=1) + print("R747_GUI_OPTIN_START_STOP_CLOSE_NO_SYSTEM_PROXY_10_TESTS_PASS") diff --git a/tests/r747_optin_local_socket_fallback_contract.py b/tests/r747_optin_local_socket_fallback_contract.py new file mode 100644 index 00000000..a98313dd --- /dev/null +++ b/tests/r747_optin_local_socket_fallback_contract.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python3 +"""R747 local SOCKS opt-in: negative-first security & lifecycle; no real web. + +All network operations use disposable 127.0.0.1 listeners. Production code +permanently rejects private DNS; fixture supplies public fake resolution and +a test-only dialer redirected to a separate private mock listener. Never starts +the real Direct Method service, alters DNS, installs binary or changes proxy. +""" +from __future__ import annotations + +import importlib.util +import os +import pathlib +import socket +import socketserver +import ssl +import struct +import threading +import time +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SOURCE=ROOT/"linux/app/socket_fallback.py" +SPEC=importlib.util.spec_from_file_location("dim_r747_socket_fallback",SOURCE) +sockmod=importlib.util.module_from_spec(SPEC) +import sys +sys.modules[SPEC.name]=sockmod +SPEC.loader.exec_module(sockmod) + + +def hello(host="www.youtube.com"): + context=ssl.create_default_context() + context.set_alpn_protocols(["http/1.1"]) + rx,tx=ssl.MemoryBIO(),ssl.MemoryBIO() + tls=context.wrap_bio(rx,tx,server_hostname=host) + try: + tls.do_handshake() + except ssl.SSLWantReadError: + pass + result=tx.read() + assert result and result[0]==22 and result[5]==1 + return result + + +def read_n(sock,n): + out=b"" + while len(out) Date: Sat, 17 Oct 2026 23:36:48 +0330 Subject: [PATCH 133/188] fix(linux): own optional SOCKS module in safe uninstall contract --- .github/workflows/ci.yml | 20 +++++++++---------- linux/app/user_uninstall_preserve.py | 3 ++- tests/r519_versioned_package_contract.py | 5 +++-- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7773d1ba..4647475f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648" - assert p.is_file() and p.stat().st_size==8405044 + expected="BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0" + assert p.is_file() and p.stat().st_size==8405070 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -167,7 +167,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -196,7 +196,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -229,7 +229,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -258,7 +258,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -287,7 +287,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -317,7 +317,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -362,7 +362,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -388,7 +388,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/app/user_uninstall_preserve.py b/linux/app/user_uninstall_preserve.py index e18ed872..f87b833e 100644 --- a/linux/app/user_uninstall_preserve.py +++ b/linux/app/user_uninstall_preserve.py @@ -16,7 +16,8 @@ import sys CODE_NAMES = frozenset({ - "direct_internet_method.py", "router_gateway.py", "router_gateway_core.py", + "direct_internet_method.py", "socket_fallback.py", + "router_gateway.py", "router_gateway_core.py", "direct-internet-method.svg", "uninstall.sh", "user_uninstall_preserve.py", "user_update_transaction.py", }) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 79d4bc9f..934ee8e4 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648"} +EXPECTED={"version":VERSION,"files":28,"sha256":"BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -61,7 +61,8 @@ def test_r519b_uninstaller_accepts_only_new_ownership_version(self): got=self.info.read(self.prefix+rel) declared=next(row for row in self.rows if row["file"]==rel) self.assertEqual(digest(got),declared["sha256"]) - self.assertEqual(declared["sha256"],"6691AA7262307F41DDAD1B5EB409E1C7AAC3B2171B5285156045849D969F6B8A") + self.assertEqual(declared["sha256"],"06825BCD25B237625DBC4FF60250763C38A27238A6273E0C97E847EAD385E64F") + self.assertIn('"socket_fallback.py"', got.decode()) self.assertIn('doc.get("version") == "1.5.5-rc1"',got.decode()) self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) self.assertEqual(digest((R/"linux/app/user_uninstall_preserve.py").read_bytes()),declared["sha256"]) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 67ca97aa..ab4a471a 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="697e1445ecc70148193227d6726aaeace7ab1dfb6a3b89650487e474f476f648" +CANDIDATE_SHA="bb702c9b104dc2d6168806604883f73202b9294dcd2f735d51645b2b11b41ca0" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 1780f1ee..1e7ae537 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,7 +28,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648" +PIN_PACKAGE="BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" PIN_GUARD="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 57a511d9..9a82f4f4 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="697E1445ECC70148193227D6726AAEACE7AB1DFB6A3B89650487E474F476F648" +ARCHIVE="BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 079d0c8ad65c1898b7de51e09d932d13cfc16dbc Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:18:04 +0330 Subject: [PATCH 134/188] test(linux): pin 28-member RC1 and root watchdog integrity --- .github/workflows/ci.yml | 18 +++++++++--------- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 17 ++++++++++++++++- .../r528_ephemeral_native_root_cli_contract.py | 6 +++--- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 4 ++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 9 files changed, 35 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4647475f..fcc54fc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0" + expected="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" assert p.is_file() and p.stat().st_size==8405070 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) @@ -167,7 +167,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -196,7 +196,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -229,7 +229,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -258,7 +258,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -287,7 +287,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -317,7 +317,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -362,7 +362,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -388,7 +388,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index be44698f..c725e6bd 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -119,7 +119,7 @@ def validate_source(source:Path): data=json.loads(manifest.read_text("utf8")) files=data.get("files") need(data.get("version")==VERSION and data.get("schema")==1 and - isinstance(files,list) and len(files)==27,"PACKAGE_MANIFEST") + isinstance(files,list) and len(files)==28,"PACKAGE_MANIFEST") pin={x.get("file"):x.get("sha256") for x in files if isinstance(x,dict)} for label,relative,want in ( ("ROOT_TX","system/protected_upgrade_transaction.py",PIN_ROOT), diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 032cd3c7..2bd761f7 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" +GUARD_SHA="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 934ee8e4..e2d3a549 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0"} +EXPECTED={"version":VERSION,"files":28,"sha256":"33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -142,6 +142,21 @@ def test_root_wrapper_pinned_identity(self): self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" stage',src) self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" apply',src) self.assertNotIn(' commit',src) + def test_guard_and_hosted_root_zip_inventory_track_live_manifest(self): + # R748 negative-first: source/unit guards must accept the same exact + # archive member count as the deterministic current ZIP (no bypass). + guard=(R/"linux/system/upgrade_guard.py").read_text(encoding="utf8") + wrapper=(R/"linux/system/upgrade_system.sh").read_text(encoding="utf8") + native=(R/"tests/r528_ephemeral_native_root_cli_contract.py").read_text(encoding="utf8") + expected=EXPECTED["files"] + self.assertIn(f"len(files)=={expected}",guard) + self.assertIn(f"archive_verify(z,CANDIDATE_PREFIX,{expected+2})",native) + self.assertIn(f'len(manifest.get("files",[]))=={expected}',native) + self.assertIn('GUARD_SHA="'+digest(guard.encode("utf8"))+'"',wrapper) + # Immutable original public Stable stays deliberately at 24 ZIP + # entries; only the unpublished RC1's inventory is extended. + self.assertIn('archive_verify(z,STABLE_PREFIX,24)',native) + def test_user_installer_never_pretends_committed(self): src=INSTALLER.read_text() self.assertIn("ROOT_UPGRADE_STAGED=1",src) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index ab4a471a..226216ce 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="bb702c9b104dc2d6168806604883f73202b9294dcd2f735d51645b2b11b41ca0" +CANDIDATE_SHA="33741c43d52ed8f23595ca6e7b2191431d8063e498e7ec94cddcbdfcd9473c78" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" @@ -121,9 +121,9 @@ def stage_exact_candidate(candidate_zip:Path,temp:Path)->Path: need(not extract.exists() and not extract.is_symlink(),"TEMP_SOURCE_COLLISION") extract.mkdir(mode=0o700) with zipfile.ZipFile(candidate_zip) as z: - archive_verify(z,CANDIDATE_PREFIX,29) + archive_verify(z,CANDIDATE_PREFIX,30) manifest=json.loads(z.read(CANDIDATE_PREFIX+"PACKAGE_MANIFEST.json")) - need(manifest.get("version")=="1.5.5-rc1" and len(manifest.get("files",[]))==27, + need(manifest.get("version")=="1.5.5-rc1" and len(manifest.get("files",[]))==28, "RC1_PACKAGE_VERSION_MANIFEST") for f in manifest["files"]: relative=f["file"] diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index f8ce4cb0..2bdc4e29 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" +SOURCE_SHA="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 1e7ae537..0b025f00 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0" +PIN_PACKAGE="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" -PIN_GUARD="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" +PIN_GUARD="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index eded21ef..9c1c345e 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="70D676AE7EBC71062C2F06B4506557DD3D5CC4FB43B9D771336DF5D32F3D35ED" +PIN_GUARD="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 9a82f4f4..f2618829 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="BB702C9B104DC2D6168806604883F73202B9294DCD2F735D51645B2B11B41CA0" +ARCHIVE="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 06f243cb771b33da127ea48c72fc7d68381005cf Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:23:39 +0330 Subject: [PATCH 135/188] test(linux): pin watchdog user transaction to RC1 source --- tests/r519_versioned_package_contract.py | 18 ++++++++++++++++++ tests/r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index e2d3a549..ddbd7f8d 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -157,6 +157,24 @@ def test_guard_and_hosted_root_zip_inventory_track_live_manifest(self): # entries; only the unpublished RC1's inventory is extended. self.assertIn('archive_verify(z,STABLE_PREFIX,24)',native) + def test_hosted_root_watchdog_user_sha_matches_current_candidate(self): + # R748: adding/removing a managed user module changes this source + # without changing Root. Both sides of the independent watchdog and + # the hosted guard tests must follow the exact current user hash. + expected=digest(USER_TXN.read_bytes()) + for filename,variable in ( + ("tests/r532_ephemeral_root_watchdog_contract.py","USER_SHA"), + ("tests/r532_root_watchdog_worker.py","PIN_USER"), + ("tests/r554_native_guard_ephemeral_contract.py","PIN_USER"), + ): + text=(R/filename).read_text(encoding="utf8") + matches=re.findall( + rf'(?m)^\s*{variable}\s*=\s*["\x27]([a-fA-F0-9]{{64}})["\x27]', + text, + ) + self.assertEqual(len(matches),1,filename) + self.assertEqual(matches[0].upper(),expected,filename) + def test_user_installer_never_pretends_committed(self): src=INSTALLER.read_text() self.assertIn("ROOT_UPGRADE_STAGED=1",src) diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index ab798ebd..ff92cf4d 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -37,7 +37,7 @@ TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" ROOT_SHA = "de3ed6765dc8db307297e49736483d02d7471b4f3fe7a77408a4368d7a2515a3" -USER_SHA = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" +USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: h=hashlib.sha256() diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 88a81c81..ad8d3d57 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -26,7 +26,7 @@ ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" PIN_ROOT = "de3ed6765dc8db307297e49736483d02d7471b4f3fe7a77408a4368d7a2515a3" -PIN_USER = "eac84cb951f520f0e6a05d1452ef355a1cc89d7c68deb73f91ba14c8e1c4bd66" +PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): h=hashlib.sha256() diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 0b025f00..19d10b7c 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -31,7 +31,7 @@ PIN_PACKAGE="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" PIN_GUARD="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" -PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" +PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") From 65d1534f65cdedb2cefa83a20de1489ad3ad6cb2 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:30:46 +0330 Subject: [PATCH 136/188] fix(linux): align root guard with current user transaction --- .github/workflows/ci.yml | 18 +++++++++--------- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 7 ++++++- .../r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 4 ++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 9 files changed, 24 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcc54fc4..c00ac291 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" + expected="C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387" assert p.is_file() and p.stat().st_size==8405070 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) @@ -167,7 +167,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -196,7 +196,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -229,7 +229,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -258,7 +258,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -287,7 +287,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -317,7 +317,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -362,7 +362,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -388,7 +388,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index c725e6bd..9cb7eac4 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -27,7 +27,7 @@ BASELINE="1.5.2" OLD_USER="1.5.4" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" -PIN_USER="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" +PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" RESULT=GUARD/"rollback-result.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 2bd761f7..149c4b86 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" +GUARD_SHA="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } @@ -84,7 +84,7 @@ try: assert data["oldUserMetaSha"]==sha(home/".local/share/DirectInternetMethod/INSTALL.json") assert data["rootTransactionSha"]==root_sha assert sha(guard/"root_tx.py")==root_sha - assert sha(guard/"user_tx.py")=="EAC84CB951F520F0E6A05D1452EF355A1CC89D7C68DEB73F91BA14C8E1C4BD66" + assert sha(guard/"user_tx.py")=="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" assert data["userTransactionSha"]==sha(guard/"user_tx.py") assert data["workerSha"]==sha(guard/"worker.py") pending=home/".local/state/DirectInternetMethod/update-pending" diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index ddbd7f8d..dbcb889b 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78"} +EXPECTED={"version":VERSION,"files":28,"sha256":"C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -153,6 +153,11 @@ def test_guard_and_hosted_root_zip_inventory_track_live_manifest(self): self.assertIn(f"archive_verify(z,CANDIDATE_PREFIX,{expected+2})",native) self.assertIn(f'len(manifest.get("files",[]))=={expected}',native) self.assertIn('GUARD_SHA="'+digest(guard.encode("utf8"))+'"',wrapper) + # The independent guard and system wrapper MUST agree with the + # installed user transaction, including future managed-file changes. + user_sha=digest(USER_TXN.read_bytes()) + self.assertIn('PIN_USER="'+user_sha+'"',guard) + self.assertIn('assert sha(guard/"user_tx.py")=="'+user_sha+'"',wrapper) # Immutable original public Stable stays deliberately at 24 ZIP # entries; only the unpublished RC1's inventory is extended. self.assertIn('archive_verify(z,STABLE_PREFIX,24)',native) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 226216ce..682436fe 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="33741c43d52ed8f23595ca6e7b2191431d8063e498e7ec94cddcbdfcd9473c78" +CANDIDATE_SHA="c436ff92a7a5ab7edad95ae0ec4cb28ff56c47fa85a91f322344354705810387" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 2bdc4e29..addf7bfd 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" +SOURCE_SHA="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 19d10b7c..9106b470 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" +PIN_PACKAGE="C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" -PIN_GUARD="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" +PIN_GUARD="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 9c1c345e..baadbc1c 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="A243C30D8C98B2249A2D44072DE51D147DABE483579C9B4E6127D6110212986E" +PIN_GUARD="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index f2618829..802a5f8a 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="33741C43D52ED8F23595CA6E7B2191431D8063E498E7EC94CDDCBDFCD9473C78" +ARCHIVE="C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 365a3f2b11a15987ba3e1d0140b6a0adc5cdb776 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 02:35:35 +0330 Subject: [PATCH 137/188] fix(linux): idempotently verify repeated root rollback receipt --- .github/workflows/ci.yml | 20 ++-- linux/system/upgrade_guard.py | 43 +++++++- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 99 ++++++++++++++++++- tests/r554_native_guard_ephemeral_contract.py | 4 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 9 files changed, 156 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c00ac291..d5820d31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387" - assert p.is_file() and p.stat().st_size==8405070 + expected="F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6" + assert p.is_file() and p.stat().st_size==8407422 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -167,7 +167,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -196,7 +196,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -229,7 +229,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -258,7 +258,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -287,7 +287,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -317,7 +317,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -362,7 +362,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -388,7 +388,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 9cb7eac4..93697cd7 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -343,7 +343,45 @@ def rollback_worker(): # engine is unsafe; require authorized local Recovery. need(not (home/USER_REL/"directmethod/state.json").exists(), "LIVE_ENGINE_REQUIRES_LOCAL_RECOVERY") - if phase=="NO_PENDING_UPGRADE" and not pending.exists(): + if phase=="ROLLED_BACK": + # R751: independent OnActiveSec/OnCalendar timer expressions may + # re-activate the service after the first successful rollback. + # Verify terminal Root/User bytes and the immutable original + # root-owned success receipt; NEVER roll back a second time, + # overwrite its evidence, or auto-retire the guard/slot here. + need(not pending.exists() and not pending.is_symlink(), + "ROLLEDBACK_USER_PENDING_REMAINS") + regular(RESULT,0,0o600) + need(RESULT.stat().st_nlink==1,"ROLLEDBACK_RECEIPT_HARDLINK") + receipt_bytes=RESULT.read_bytes() + prior=json.loads(receipt_bytes) + need(receipt_bytes==canonical(prior),"ROLLEDBACK_RECEIPT_NONCANONICAL") + need(prior.get("revision")=="R552" and + prior.get("status")=="PASS_AUTONOMOUS_TWO_SIDED_RESTORATION" and + prior.get("rootRollback")=="PASS" and + prior.get("userRollback")=="PASS" and + prior.get("recoveryAttempted") is True and + prior.get("networkMutationAttempted") is False and + prior.get("uid")==0, + "ROLLEDBACK_PRIOR_RECEIPT_NOT_SUCCESSFUL") + m=tx.manifest() + need(m.get("phase")=="ROLLED_BACK" and + m.get("fromVersion")==BASELINE and + m.get("toVersion")==VERSION and + m.get("uid")==j["uid"] and + m.get("home")==j["home"] and + m.get("oldMetadataSHA")==j["oldRootMetaSha"], + "ROLLEDBACK_FOREIGN_ROOT_TRANSACTION") + tx._validate_slot(m) + states,metadata=tx._target_states(m) + need(set(states)==set(m["files"]) and + all(states[name]==m["files"][name]["old"] for name in states) and + metadata==m["oldMetadataSHA"], + "ROLLEDBACK_ORIGINAL_ROOT_BYTES_CHANGED") + verify_installed_after_restore(j) + report["status"]="NO_CHANGE_ALREADY_ROLLED_BACK" + report["restoredReceiptSha256"]=sha_file(RESULT) + elif phase=="NO_PENDING_UPGRADE" and not pending.exists(): report["status"]="NO_CHANGE_NO_PENDING" else: need(phase in ("STAGED","APPLIED","DEGRADED","NO_PENDING_UPGRADE"), @@ -372,7 +410,8 @@ def rollback_worker(): return 0 if report["status"] in ( "PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT", "NO_ROLLBACK_ALREADY_COMMITTED","NO_CHANGE_NO_PENDING", - "PASS_AUTONOMOUS_TWO_SIDED_RESTORATION") else 90 + "PASS_AUTONOMOUS_TWO_SIDED_RESTORATION", + "NO_CHANGE_ALREADY_ROLLED_BACK") else 90 def _exact_owned_inventory(root:Path,files:set[str],dirs:set[str],mode:int): """Allowlist every file/dir; no symlinks, foreign nodes or hardlinks.""" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 149c4b86..2ffca7ec 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" +GUARD_SHA="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index dbcb889b..362a1bc5 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387"} +EXPECTED={"version":VERSION,"files":28,"sha256":"F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 682436fe..15266669 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="c436ff92a7a5ab7edad95ae0ec4cb28ff56c47fa85a91f322344354705810387" +CANDIDATE_SHA="f10e23085d2677fa6522809c4da3d41d3b8ebe930aee3c6d4d3cba0b8b0145c6" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index addf7bfd..285ecb62 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" +SOURCE_SHA="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) @@ -195,6 +195,103 @@ def test_18_active_engine_allows_only_accepted_backup_cleanup(self): self.assertEqual(json.loads(self.result.read_text())["status"], "PASS_RESUMED_ACCEPTED_ROOT_USER_COMMIT") + def _terminal_rolledback_fixture(self): + """Synthetic exact root owner/perms, never real host Root/files.""" + self.fake["uid"]=os.getuid() + self.fake["oldRootMetaSha"]="ORIG_ROOT" + prior=g.regular + self.addCleanup(lambda:setattr(g,"regular",prior)) + def fake_regular(path,uid=None,mode=None): + if path==g.RESULT: + if not path.is_file() or path.is_symlink() or mode!=0o600: + raise RuntimeError("R751_TEST_RESULT_NOT_REGULAR") + return path.stat() + return prior(path,uid,mode) + g.regular=fake_regular + receipt={ + "revision":"R552", + "status":"PASS_AUTONOMOUS_TWO_SIDED_RESTORATION", + "uid":0, + "rootPhaseBefore":"APPLIED", + "rootRollback":"PASS", + "userRollback":"PASS", + "recoveryAttempted":True, + "networkMutationAttempted":False, + "whenUtc":"2026-10-17T22:39:13Z", + "durationSeconds":0.119, + } + self.result.write_bytes(g.canonical(receipt)) + self.result.chmod(0o600) + root_home=str(self.home) + uid=self.fake["uid"] + class RolledBack: + native=True + def __init__(self):self.calls=[] + def status(self): + self.calls.append("status") + return {"status":"ROLLED_BACK"} + def manifest(self): + self.calls.append("manifest") + return {"phase":"ROLLED_BACK","fromVersion":g.BASELINE, + "toVersion":g.VERSION,"uid":uid,"home":root_home, + "files":{"helper":{"old":"ORIGINAL"}},"oldMetadataSHA":"ORIG_ROOT"} + def _validate_slot(self,manifest): + self.calls.append("validate") + def _target_states(self,manifest): + self.calls.append("target_states") + return {"helper":"ORIGINAL"},"ORIG_ROOT" + def rollback(self): + raise AssertionError("ALREADY_ROLLED_BACK_MUST_NOT_ROLLBACK_AGAIN") + tx=RolledBack() + g.imported_root=lambda:tx + return tx,receipt + + def test_19_terminal_rolledback_second_timer_is_idempotent_and_retained(self): + tx,receipt=self._terminal_rolledback_fixture() + original=self.result.read_bytes() + self.assertEqual(g.rollback_worker(),0) + self.assertEqual(self.result.read_bytes(),original) + self.assertIn("validate_restored",self.events) + self.assertNotIn("user_command",self.events) + self.assertEqual(tx.calls,["status","manifest","validate","target_states"]) + + def test_20_repeat_third_timer_keeps_original_receipt(self): + tx,_=self._terminal_rolledback_fixture() + original=self.result.read_bytes() + self.assertEqual(g.rollback_worker(),0) + self.assertEqual(g.rollback_worker(),0) + self.assertEqual(self.result.read_bytes(),original) + self.assertEqual(self.events,["validate_restored","validate_restored"]) + + def test_21_rolledback_wrong_receipt_must_fail_closed(self): + tx,receipt=self._terminal_rolledback_fixture() + receipt["userRollback"]="FAIL" + self.result.write_bytes(g.canonical(receipt)) + self.assertEqual(g.rollback_worker(),90) + self.assertEqual(self.result.read_bytes(),g.canonical(receipt)) + self.assertNotIn("validate_restored",self.events) + self.assertNotIn("user_command",self.events) + + def test_22_rolledback_missing_receipt_must_fail_closed(self): + tx,_=self._terminal_rolledback_fixture() + self.result.unlink() + self.assertEqual(g.rollback_worker(),90) + self.assertNotIn("validate_restored",self.events) + self.assertNotIn("user_command",self.events) + + def test_23_rolledback_root_original_hash_drift_must_fail_closed(self): + tx,_=self._terminal_rolledback_fixture() + tx._target_states=lambda m:({"helper":"FOREIGN"},"ORIG_ROOT") + self.assertEqual(g.rollback_worker(),90) + self.assertNotIn("validate_restored",self.events) + + def test_24_rolledback_user_pending_must_not_report_clean(self): + tx,_=self._terminal_rolledback_fixture() + pending=self.home/g.PENDING_REL + pending.mkdir(parents=True) + self.assertEqual(g.rollback_worker(),90) + self.assertNotIn("validate_restored",self.events) + def test_09_sources_are_not_host_install_commands(self): src=SRC.read_text() self.assertNotIn("sudo -S",src) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 9106b470..59e61c5b 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387" +PIN_PACKAGE="F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6" PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" -PIN_GUARD="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" +PIN_GUARD="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index baadbc1c..3eec2196 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="CDB853DAB914E73059C252746BAA5413F5DF27D1EFDEC183391404E4CA48BC17" +PIN_GUARD="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 802a5f8a..3ad6d45f 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="C436FF92A7A5AB7EDAD95AE0EC4CB28FF56C47FA85A91F322344354705810387" +ARCHIVE="F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 701a05653ea67470761d549b972ea5d0ea09bfdb Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 18:03:18 +0330 Subject: [PATCH 138/188] fix(windows): reject nonphysical curl probe interface addresses --- tests/probe_process_security/Program.cs | 16 ++++++++++++++++ windows/gui/ProbeProcessBuilder.cs | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/probe_process_security/Program.cs b/tests/probe_process_security/Program.cs index e29c369a..d37b0ecc 100644 --- a/tests/probe_process_security/Program.cs +++ b/tests/probe_process_security/Program.cs @@ -32,6 +32,22 @@ void Reject(string label, string url, string ip) const string valid = "https://www.youtube.com/generate_204"; const string physical = "192.168.8.42"; +// R759 NEGATIVE-FIRST: dangerous interface inputs must never fall back to +// wildcard/loopback/broadcast routing while probing supposedly physical egress. +Reject("nonphysical_unspecified", valid, "0.0.0.0"); +Reject("nonphysical_loopback", valid, "127.0.0.1"); +Reject("nonphysical_multicast", valid, "224.0.0.1"); +Reject("nonphysical_limited_broadcast", valid, "255.255.255.255"); +Reject("nonphysical_link_local", valid, "169.254.10.20"); +Reject("nonphysical_zero_net", valid, "0.1.2.3"); +Reject("nonphysical_loopback_other", valid, "127.4.5.6"); +Reject("nonphysical_reserved", valid, "240.0.0.1"); +Check("physical_usb_rfc1918_ipv4_accepted", () => + Need(ProbeProcessBuilder.Create(valid, "10.106.38.164").ArgumentList[2] == "10.106.38.164", + "Safe USB private IPv4 was rejected")); +Check("physical_carrier_grade_nat_ipv4_accepted", () => + Need(ProbeProcessBuilder.Create(valid, "100.100.64.2").ArgumentList[2] == "100.100.64.2", + "Safe CGNAT IPv4 was rejected")); Check("fixed_system32_curl_not_path_or_cwd", () => { diff --git a/windows/gui/ProbeProcessBuilder.cs b/windows/gui/ProbeProcessBuilder.cs index 48212b60..f66b61ba 100644 --- a/windows/gui/ProbeProcessBuilder.cs +++ b/windows/gui/ProbeProcessBuilder.cs @@ -26,6 +26,14 @@ internal static ProcessStartInfo Create(string url, string localIp) !string.Equals(ip.ToString(), ipText, StringComparison.Ordinal)) throw new ArgumentException("Probe requires one canonical physical IPv4 address.", nameof(localIp)); + // An unspecified source may select the system default route rather + // than the verified physical adapter. Loopback, link-local, + // multicast, and reserved IPv4 are never valid physical egress. + var octets = ip.GetAddressBytes(); + if (octets[0] == 0 || octets[0] == 127 || octets[0] >= 224 || + (octets[0] == 169 && octets[1] == 254)) + throw new ArgumentException("Probe requires a physical unicast IPv4 address.", nameof(localIp)); + if (!Uri.TryCreate(url, UriKind.Absolute, out var endpoint) || endpoint.Scheme != Uri.UriSchemeHttps || endpoint.Port != 443 || From df3cdb2240db8069c43011b3875659bb1ac0daf0 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 18:59:01 +0330 Subject: [PATCH 139/188] audit-only: pin root DNS helper binaries to trusted paths (R762, DO NOT MERGE) --- linux/system/encrypted_selective_dns.py | 24 ++++++- ...usted_dns_system_binary_origin_contract.py | 64 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/r762_trusted_dns_system_binary_origin_contract.py diff --git a/linux/system/encrypted_selective_dns.py b/linux/system/encrypted_selective_dns.py index b5f2b046..9281a1df 100644 --- a/linux/system/encrypted_selective_dns.py +++ b/linux/system/encrypted_selective_dns.py @@ -58,8 +58,30 @@ def need(condition: bool, why: str) -> None: raise RuntimeError("FAIL_CLOSED_" + why) +# Root action helpers must never resolve their executable from the caller's PATH. +# All call sites select one of these fixed, OS-owned executables. +TRUSTED_SYSTEM_BINARIES = { + "resolvectl": "/usr/bin/resolvectl", + "tailscale": "/usr/bin/tailscale", + "nmcli": "/usr/bin/nmcli", + "ip": "/usr/bin/ip", + "ss": "/usr/bin/ss", + "systemctl": "/usr/bin/systemctl", + "busctl": "/usr/bin/busctl", + "dig": "/usr/bin/dig", + "systemd-run": "/usr/bin/systemd-run", +} + + def call(*args: str, seconds: int = 10, ok: bool = True) -> subprocess.CompletedProcess: - p = subprocess.run(args, capture_output=True, text=True, timeout=seconds) + need(bool(args) and type(args[0]) is str and args[0] in TRUSTED_SYSTEM_BINARIES, + "UNTRUSTED_COMMAND") + executable = pathlib.Path(TRUSTED_SYSTEM_BINARIES[args[0]]) + need(executable.is_file() and not executable.is_symlink(), "TRUSTED_TOOL_MISSING") + mode = executable.stat() + need(mode.st_uid == 0 and mode.st_mode & 0o022 == 0, "TRUSTED_TOOL_OWNER") + p = subprocess.run((str(executable), *args[1:]), capture_output=True, text=True, + timeout=seconds) if ok and p.returncode: raise RuntimeError("CALL_FAILED_" + repr(args[:3]) + "_" + str(p.returncode) + "_" + p.stderr[-100:]) diff --git a/tests/r762_trusted_dns_system_binary_origin_contract.py b/tests/r762_trusted_dns_system_binary_origin_contract.py new file mode 100644 index 00000000..589d707d --- /dev/null +++ b/tests/r762_trusted_dns_system_binary_origin_contract.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Negative-first PATH-poisoning regression, only user-local subprocess fixtures.""" +import importlib.util +import pathlib +import os +import tempfile +import hashlib +import stat +import subprocess +import unittest +from unittest.mock import patch +from types import SimpleNamespace + +ROOT=pathlib.Path(__file__).resolve().parents[1] +CODE=ROOT / "linux/system/encrypted_selective_dns.py" +SPEC=importlib.util.spec_from_file_location("dim_r762_dns", CODE) +MOD=importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MOD) + + +class R762TrustedSystemExec(unittest.TestCase): + def test_01_path_first_decoy_never_executes(self): + with tempfile.TemporaryDirectory(prefix="dim-r762-sec-") as td: + root=pathlib.Path(td) + marker=root / "executed" + fake=root / "resolvectl" + fake.write_text("#!/bin/sh\\nprintf 'POISON\\n' > '"+str(marker)+"'\\n") + fake.chmod(0o700) + with patch.dict(os.environ, {"PATH":td+":"+os.environ.get("PATH","")}): + result=MOD.call("resolvectl","--version",seconds=4) + self.assertEqual(result.returncode, 0) + self.assertFalse(marker.exists()) + self.assertNotIn("POISON", result.stdout) + + def test_02_unknown_command_name_is_rejected_before_any_spawn(self): + with patch.object(MOD.subprocess, "run", side_effect=AssertionError("spawned")): + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNTRUSTED_COMMAND"): + MOD.call("malicious", "--do-not-run") + + def test_03_exact_absolute_argv_and_bounded_timeout(self): + with patch.object(MOD.subprocess, "run", + return_value=SimpleNamespace(stdout="ok",stderr="",returncode=0)) as run: + result=MOD.call("ip","-4","route","show","default",seconds=5) + argv=run.call_args.args[0] + self.assertEqual(argv[0], "/usr/bin/ip") + self.assertEqual(argv[1:], ("-4","route","show","default")) + self.assertEqual(run.call_args.kwargs["timeout"],5) + self.assertNotIn("shell",run.call_args.kwargs) + self.assertEqual(result.stdout,"ok") + + def test_04_every_allowed_binary_is_absolute_root_owned_not_world_writable(self): + for label,name in MOD.TRUSTED_SYSTEM_BINARIES.items(): + path=pathlib.Path(name) + with self.subTest(label=label): + self.assertTrue(path.is_absolute()) + self.assertTrue(path.is_file()) + self.assertFalse(path.is_symlink()) + st=path.stat() + self.assertEqual(st.st_uid,0) + self.assertFalse(st.st_mode & 0o022) + + +if __name__ == "__main__": + unittest.main(verbosity=2) From 52ebb0edf22a3bf0c72d8192b9b3c363a71d681d Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Sun, 18 Oct 2026 23:22:40 +0330 Subject: [PATCH 140/188] audit-only: coherently repin RC1 Root Guard and package after R762 (DO NOT MERGE) --- .github/workflows/ci.yml | 21 ++++++++++--------- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 10 ++++++--- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ...usted_dns_system_binary_origin_contract.py | 17 ++++++++++++++- 13 files changed, 47 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcd98792..c65c077d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6" - assert p.is_file() and p.stat().st_size==8407422 + expected="4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C" + assert p.is_file() and p.stat().st_size==8408326 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -80,6 +80,7 @@ jobs: set -euo pipefail python3 -B tests/encrypted_selective_dns_policy_contract.py python3 -B tests/encrypted_selective_dns_controller_contract.py + python3 -B tests/r762_trusted_dns_system_binary_origin_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py @@ -167,7 +168,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -196,7 +197,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -229,7 +230,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -258,7 +259,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -287,7 +288,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -317,7 +318,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -362,7 +363,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -388,7 +389,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index e3d497ac..850df3e4 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -28,7 +28,7 @@ "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), ("encrypted_controller","system/encrypted_selective_dns.py","usr/lib/directinternetmethod/encrypted_selective_dns.py", - None,"6DA9BAB94E1F4AF5C9FBE0742F96B3E2B5A0392294B27EDA3F4012095D9993DA",0o755), + None,"6E12A906D0B3AD6FD910303B55F89F76EB56C2EF1AABF15549806641285E461F",0o755), ("encrypted_policy","system/encrypted_selective_policy.py","usr/lib/directinternetmethod/encrypted_selective_policy.py", None,"BCADC01C4A0DEDA0AB7C678155BE6F6960D3B68D9CFE1307DF85A94F18785663",0o644), ) diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 93697cd7..df0d704a 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" +PIN_ROOT="17376CC6EFBC37ACEA1DB2AB0219EFBF98A98FDF532CFD7AF8FCF94801099615" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 2ffca7ec..b735b10d 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" +TX_SHA="17376CC6EFBC37ACEA1DB2AB0219EFBF98A98FDF532CFD7AF8FCF94801099615" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" +GUARD_SHA="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 362a1bc5..8e509531 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6"} +EXPECTED={"version":VERSION,"files":28,"sha256":"4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -66,7 +66,7 @@ def test_r519b_uninstaller_accepts_only_new_ownership_version(self): self.assertIn('doc.get("version") == "1.5.5-rc1"',got.decode()) self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) self.assertEqual(digest((R/"linux/app/user_uninstall_preserve.py").read_bytes()),declared["sha256"]) - def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self): + def test_original_unreleased_candidate_preserved_and_current_source_delta(self): # CI does not commit an 8.3 MB obsolete candidate ZIP. Its original # manifest and changed single source member are pinned byte-for-byte # as compact historical fixtures (all 26 original file SHA entries). @@ -86,9 +86,13 @@ def test_original_unreleased_candidate_preserved_and_only_uninstaller_delta(self self.assertEqual(set(after),set(before)|{"system/upgrade_guard.py","app/socket_fallback.py"}) self.assertEqual(len(before),26) changed={name for name in before if before[name]!=after[name]} + # Preserve the immutable historical manifest and explicitly recognize + # the new R762 controller hardening in the current unreleased package. self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", - "system/upgrade_system.sh","install.sh"}) + "system/upgrade_system.sh","install.sh","system/encrypted_selective_dns.py"}) + self.assertEqual(after["system/encrypted_selective_dns.py"]["sha256"], + "6E12A906D0B3AD6FD910303B55F89F76EB56C2EF1AABF15549806641285E461F") self.assertEqual(after["app/direct_internet_method.py"]["sha256"], "6CF031D2C7F18D7E7E4F1F9A37A1A0EBD3CA589F16F7E26274496F1F53E9317B") self.assertIn("encrypted=encrypted_dns_optin_state()", diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 15266669..6ce38c1e 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="f10e23085d2677fa6522809c4da3d41d3b8ebe930aee3c6d4d3cba0b8b0145c6" +CANDIDATE_SHA="4b4a76c02873982de817380a4a23aaf399ec7b3ae9756ca12d39f1499ee86e2c" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index ff92cf4d..c52833da 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "de3ed6765dc8db307297e49736483d02d7471b4f3fe7a77408a4368d7a2515a3" +ROOT_SHA = "17376cc6efbc37acea1db2ab0219efbf98a98fdf532cfd7af8fcf94801099615" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index ad8d3d57..af562f8a 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "de3ed6765dc8db307297e49736483d02d7471b4f3fe7a77408a4368d7a2515a3" +PIN_ROOT = "17376cc6efbc37acea1db2ab0219efbf98a98fdf532cfd7af8fcf94801099615" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 285ecb62..3b1882a6 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" +SOURCE_SHA="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 59e61c5b..5f3460c1 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6" -PIN_ROOT="DE3ED6765DC8DB307297E49736483D02D7471B4F3FE7A77408A4368D7A2515A3" -PIN_GUARD="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" +PIN_PACKAGE="4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C" +PIN_ROOT="17376CC6EFBC37ACEA1DB2AB0219EFBF98A98FDF532CFD7AF8FCF94801099615" +PIN_GUARD="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 3eec2196..de2e5150 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="913DBE39499344BFF96BA41D33AB0B0A2EBEB662D604A0C45A528D18207EFEC2" +PIN_GUARD="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 3ad6d45f..fac916e1 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="F10E23085D2677FA6522809C4DA3D41D3B8EBE930AEE3C6D4D3CBA0B8B0145C6" +ARCHIVE="4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r762_trusted_dns_system_binary_origin_contract.py b/tests/r762_trusted_dns_system_binary_origin_contract.py index 589d707d..9eb815f3 100644 --- a/tests/r762_trusted_dns_system_binary_origin_contract.py +++ b/tests/r762_trusted_dns_system_binary_origin_contract.py @@ -48,16 +48,31 @@ def test_03_exact_absolute_argv_and_bounded_timeout(self): self.assertNotIn("shell",run.call_args.kwargs) self.assertEqual(result.stdout,"ok") - def test_04_every_allowed_binary_is_absolute_root_owned_not_world_writable(self): + def test_04_every_allowed_binary_is_absolute_root_owned_when_installed(self): + # Hosted CI may not have optional Tailscale/dig tools. Their absence + # must fail closed on invocation, not reject testing the whole source. + found=0 for label,name in MOD.TRUSTED_SYSTEM_BINARIES.items(): path=pathlib.Path(name) with self.subTest(label=label): self.assertTrue(path.is_absolute()) + self.assertEqual(str(path),"/usr/bin/"+label) + if not path.exists(): + continue self.assertTrue(path.is_file()) self.assertFalse(path.is_symlink()) st=path.stat() self.assertEqual(st.st_uid,0) self.assertFalse(st.st_mode & 0o022) + found+=1 + self.assertGreaterEqual(found,1) + + def test_05_missing_trusted_binary_fails_closed_before_spawn(self): + with patch.dict(MOD.TRUSTED_SYSTEM_BINARIES, + {"resolvectl":"/usr/bin/dim-r763-known-missing-tool"}): + with patch.object(MOD.subprocess,"run",side_effect=AssertionError("spawned")): + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_TRUSTED_TOOL_MISSING"): + MOD.call("resolvectl","--version") if __name__ == "__main__": From 076440137f038acef713108e5720ec6fdd0ae36d Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:16:37 +0330 Subject: [PATCH 141/188] audit-only: block symlink parent and untrusted root receipt (R765; DO NOT MERGE) --- .github/workflows/ci.yml | 19 +++--- linux/system/protected_upgrade_transaction.py | 52 +++++++++++++- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r764_parent_path_security_contract.py | 68 +++++++++++++++++++ 13 files changed, 141 insertions(+), 24 deletions(-) create mode 100644 tests/r764_parent_path_security_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c65c077d..e990093f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C" + expected="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" assert p.is_file() and p.stat().st_size==8408326 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) @@ -81,6 +81,7 @@ jobs: python3 -B tests/encrypted_selective_dns_policy_contract.py python3 -B tests/encrypted_selective_dns_controller_contract.py python3 -B tests/r762_trusted_dns_system_binary_origin_contract.py + python3 -B tests/r764_parent_path_security_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py @@ -168,7 +169,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -197,7 +198,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -230,7 +231,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -259,7 +260,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -288,7 +289,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -318,7 +319,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -363,7 +364,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -389,7 +390,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 850df3e4..1e31a74b 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -62,6 +62,53 @@ def no_symlink_regular(path,owner=True): st=path.stat() if owner:need(st.st_uid==0 and st.st_gid==0,"OWNER_"+path.name) return st +def guarded_file_bytes(path, *, native_receipt=False, limit=2**21): + """Read a bounded file through dirfds, refusing symlinks in EVERY parent. + + Package bytes may be user-owned; their exact SHA is separately pinned. + Final native acceptance is more privileged and must have an OS-owned, + non-group/world-writable file under safe OS-owned directories. A root-owned + sticky directory (for example /tmp) may contain a private root test slot. + """ + path=pathlib.Path(path) + need(path.is_absolute() and ".." not in path.parts and + len(path.parts)>=2, "UNSAFE_ABSOLUTE_FILE_PATH") + flags=os.O_RDONLY|os.O_CLOEXEC|os.O_NOFOLLOW + directory=os.open("/",flags|os.O_DIRECTORY) + opened=None + try: + for part in path.parts[1:-1]: + try: + child=os.open(part,flags|os.O_DIRECTORY,dir_fd=directory) + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_UNSAFE_PARENT_COMPONENT") from exc + os.close(directory);directory=child + if native_receipt: + entry=os.fstat(directory) + need(entry.st_uid==0 and + (not entry.st_mode & 0o022 or + (bool(entry.st_mode & stat.S_ISVTX) and + stat.S_ISDIR(entry.st_mode))), + "RECEIPT_PARENT_OWNER") + try: + opened=os.open(path.name,flags,dir_fd=directory) + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") from exc + info=os.fstat(opened) + need(stat.S_ISREG(info.st_mode) and 0<=info.st_size<=limit, + "UNSAFE_FILE_TYPE_OR_SIZE") + if native_receipt: + need(info.st_uid==0 and info.st_gid==0 and + not info.st_mode & 0o022, "UNTRUSTED_RECEIPT_OWNER") + with os.fdopen(os.dup(opened),"rb") as reader: + content=reader.read(limit+1) + need(len(content)<=limit, "FILE_SIZE_CHANGED_DURING_READ") + return content + finally: + if opened is not None:os.close(opened) + os.close(directory) + + def flush_dir(directory): f=os.open(directory,os.O_DIRECTORY) try:os.fsync(f) @@ -187,7 +234,7 @@ def stage(self,source): for name,rel,target,old,want,mode in FILES: file=src/rel no_symlink_regular(file,False) - blob=file.read_bytes() + blob=guarded_file_bytes(file) need(sha(blob)==want,"PACKAGE_SOURCE_SHA_"+name) data[name]=blob self.pending.mkdir(mode=0o700) @@ -323,7 +370,8 @@ def commit(self,acceptance): acceptance=pathlib.Path(acceptance) need(acceptance.is_file() and not acceptance.is_symlink(), "NATIVE_ACCEPTANCE_MISSING") - receipt=json.loads(acceptance.read_text("utf8")) + receipt=json.loads(guarded_file_bytes( + acceptance,native_receipt=self.native,limit=65536).decode("utf8")) need(receipt.get("revision")=="R519" and receipt.get("status")=="PASS_NATIVE_ROOT_UPGRADE" and receipt.get("sourceRootSHA")==sha(canonical(m["files"])) and diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index df0d704a..d720de40 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="17376CC6EFBC37ACEA1DB2AB0219EFBF98A98FDF532CFD7AF8FCF94801099615" +PIN_ROOT="2DC3FBDEB21C589DA8889F03B450A69204ECAD90D8F113B3C9DD5F6C9DCE533A" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index b735b10d..95b664f6 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="17376CC6EFBC37ACEA1DB2AB0219EFBF98A98FDF532CFD7AF8FCF94801099615" +TX_SHA="2DC3FBDEB21C589DA8889F03B450A69204ECAD90D8F113B3C9DD5F6C9DCE533A" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" +GUARD_SHA="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 8e509531..82b285f1 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C"} +EXPECTED={"version":VERSION,"files":28,"sha256":"5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 6ce38c1e..12b9dcec 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="4b4a76c02873982de817380a4a23aaf399ec7b3ae9756ca12d39f1499ee86e2c" +CANDIDATE_SHA="5d2e86cbaeb999eb404a03072387d772c33fe40233010a2f46ade5a9fa5d220a" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index c52833da..8d798ba8 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "17376cc6efbc37acea1db2ab0219efbf98a98fdf532cfd7af8fcf94801099615" +ROOT_SHA = "2dc3fbdeb21c589da8889f03b450a69204ecad90d8f113b3c9dd5f6c9dce533a" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index af562f8a..92f0fb87 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "17376cc6efbc37acea1db2ab0219efbf98a98fdf532cfd7af8fcf94801099615" +PIN_ROOT = "2dc3fbdeb21c589da8889f03b450a69204ecad90d8f113b3c9dd5f6c9dce533a" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 3b1882a6..9227a91c 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" +SOURCE_SHA="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 5f3460c1..d10dfab8 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -28,9 +28,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C" -PIN_ROOT="17376CC6EFBC37ACEA1DB2AB0219EFBF98A98FDF532CFD7AF8FCF94801099615" -PIN_GUARD="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" +PIN_PACKAGE="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" +PIN_ROOT="2DC3FBDEB21C589DA8889F03B450A69204ECAD90D8F113B3C9DD5F6C9DCE533A" +PIN_GUARD="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index de2e5150..24e5bdd6 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -21,7 +21,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="C0E9884F194604B1C20949C998E6B095C530DBEE1BC808DE7B0A1D1CC073B020" +PIN_GUARD="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index fac916e1..e1a493b4 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="4B4A76C02873982DE817380A4A23AAF399EC7B3AE9756CA12D39F1499EE86E2C" +ARCHIVE="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r764_parent_path_security_contract.py b/tests/r764_parent_path_security_contract.py new file mode 100644 index 00000000..c18620ee --- /dev/null +++ b/tests/r764_parent_path_security_contract.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""R764 nonprivileged negative-first parent-symlink path tests. +All stage/commit work uses R519's disposable fake filesystem under tests/. +""" +import importlib.util +import json +import pathlib +import unittest + +HERE=pathlib.Path(__file__).resolve().parent +spec=importlib.util.spec_from_file_location("r764_r519_fake_fixture", + HERE/"protected_upgrade_transaction_contract.py") +module=importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + +class R764PathParents(unittest.TestCase): + def setUp(self): + self.fake=module.FakeFixture("test_success_stage_apply_rollback") + self.fake.setUp() + def tearDown(self): + self.fake.tearDown() + + def test_symlink_parent_of_source_package_is_rejected_before_stage(self): + actual=self.fake.root/"real-source-parent" + actual.mkdir() + real_package=actual/"package" + self.fake.pkg.rename(real_package) + alias=self.fake.root/"source-parent-alias" + alias.symlink_to(actual,target_is_directory=True) + sourced=alias/"package" + self.assertTrue(sourced.is_dir()) + self.assertFalse(sourced.is_symlink()) + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_PARENT_COMPONENT"): + self.fake.tx.stage(sourced) + self.assertFalse(self.fake.tx.pending.exists()) + + def test_symlink_parent_of_root_acceptance_receipt_is_rejected(self): + self.fake.tx.stage(self.fake.pkg) + self.fake.tx.apply() + doc=self.fake.tx.manifest() + root=self.fake.root/"actual-acceptance-parent" + root.mkdir() + receipt=root/"acceptance.json" + data={"revision":"R519","status":"PASS_NATIVE_ROOT_UPGRADE", + "sourceRootSHA":module.m.sha(module.m.canonical(doc["files"])), + "protectedMetadataSHA":doc["newMetadataSHA"], + "nativeStartStopRecovery":True, + "tailscalePreserved":True,"physicalDNSPreserved":True} + receipt.write_text(json.dumps(data)+"\n") + alias=self.fake.root/"receipt-parent-alias" + alias.symlink_to(root,target_is_directory=True) + bypass=alias/"acceptance.json" + self.assertTrue(bypass.is_file()) + self.assertFalse(bypass.is_symlink()) + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_PARENT_COMPONENT"): + self.fake.tx.commit(bypass) + self.assertEqual(self.fake.tx.status()["status"],"APPLIED") + + def test_untrusted_native_acceptance_owner_is_rejected(self): + # No privilege requested: fake-owned receipt under a user-owned test + # directory must not become a native Root acceptance proof. + f=self.fake.root/"user-owned-test-receipt.json" + f.write_text("{}") + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_RECEIPT_PARENT_OWNER"): + module.m.guarded_file_bytes(f,native_receipt=True,limit=65536) + +if __name__=="__main__": + unittest.main(verbosity=2) From 73e71ffe02d3c92d1cf35c6fbf643f6f8846e92e Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:20:26 +0330 Subject: [PATCH 142/188] audit-only: repin exact R765 archive byte count in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e990093f..0a71088f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") expected="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" - assert p.is_file() and p.stat().st_size==8408326 + assert p.is_file() and p.stat().st_size==8410505 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" From 634d2c6324d8408a91d7e6c64ec2d99e662a07ad Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:25:23 +0330 Subject: [PATCH 143/188] audit-only: create synthetic Root acceptance in a private root-owned test dir --- tests/r554_native_guard_ephemeral_contract.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index d10dfab8..c2656505 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -20,6 +20,7 @@ import stat import subprocess import sys +import tempfile import time R=Path(__file__).resolve().parents[1] @@ -195,14 +196,25 @@ def run(): "tailscalePreserved":True, "physicalDNSPreserved":True } - acceptance=Path(temp)/"DIM_R562_TEST_ONLY_ACCEPTANCE.json" - need(not acceptance.exists() and not acceptance.is_symlink(), - "FOREIGN_TEST_ACCEPTANCE_EXISTS") - acceptance.write_text(json.dumps(fixture,sort_keys=True,indent=2)+"\n") - need(root_tx.commit(acceptance)["status"]=="COMMITTED", - "SYNTHETIC_ROOT_FINALIZATION") - need(root_tx.status()["status"]=="COMMITTED" and pending.is_dir(), - "ROOT_COMMITTED_USER_STILL_PENDING_WINDOW") + # Native Root now refuses receipts routed through user-owned parents. + # The old Path(temp) is deliberately runner-owned and must remain + # rejected. Generate this TEST-ONLY receipt in a private OS-root + # directory instead of weakening the production Root validation. + with tempfile.TemporaryDirectory(prefix="DIM_R562_ROOT_PRIVATE_RECEIPT_",dir="/tmp") as td: + private=Path(td) + st=private.stat() + need(st.st_uid==0 and stat.S_IMODE(st.st_mode)==0o700, + "TEST_RECEIPT_ROOT_PRIVATE_PARENT") + acceptance=private/"DIM_R562_TEST_ONLY_ACCEPTANCE.json" + need(not acceptance.exists() and not acceptance.is_symlink(), + "FOREIGN_TEST_ACCEPTANCE_EXISTS") + acceptance.write_text(json.dumps(fixture,sort_keys=True,indent=2)+"\n") + acceptance.chmod(0o600) + need(acceptance.stat().st_uid==0,"TEST_RECEIPT_ROOT_OWNER") + need(root_tx.commit(acceptance)["status"]=="COMMITTED", + "SYNTHETIC_ROOT_FINALIZATION") + need(root_tx.status()["status"]=="COMMITTED" and pending.is_dir(), + "ROOT_COMMITTED_USER_STILL_PENDING_WINDOW") if synthetic_reject: # The *only* deliberate user-data mutation happens on this disposable # GitHub runner AFTER synthetically accepting Root and BEFORE user commit. From 91b899a90f86080f8789adc3d596b6087c80f305 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:29:24 +0330 Subject: [PATCH 144/188] audit-only: preserve existing private hash map in native synthetic receipt fixture --- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index c2656505..57790cd2 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -201,11 +201,11 @@ def run(): # rejected. Generate this TEST-ONLY receipt in a private OS-root # directory instead of weakening the production Root validation. with tempfile.TemporaryDirectory(prefix="DIM_R562_ROOT_PRIVATE_RECEIPT_",dir="/tmp") as td: - private=Path(td) - st=private.stat() + acceptance_dir=Path(td) + st=acceptance_dir.stat() need(st.st_uid==0 and stat.S_IMODE(st.st_mode)==0o700, "TEST_RECEIPT_ROOT_PRIVATE_PARENT") - acceptance=private/"DIM_R562_TEST_ONLY_ACCEPTANCE.json" + acceptance=acceptance_dir/"DIM_R562_TEST_ONLY_ACCEPTANCE.json" need(not acceptance.exists() and not acceptance.is_symlink(), "FOREIGN_TEST_ACCEPTANCE_EXISTS") acceptance.write_text(json.dumps(fixture,sort_keys=True,indent=2)+"\n") From 6f008f3d9e0dd51b55c271ad4f4d305f534928d3 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:58:10 +0330 Subject: [PATCH 145/188] audit-only: pin R566 systemctl executable and reject untrusted arguments (R766; DO NOT MERGE) --- .github/workflows/ci.yml | 1 + .../r566_native_systemd_persistent_catchup.py | 30 +++++++++++- tests/r766_systemd_exec_origin_contract.py | 47 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tests/r766_systemd_exec_origin_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a71088f..375579e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,7 @@ jobs: python3 -B tests/encrypted_selective_dns_controller_contract.py python3 -B tests/r762_trusted_dns_system_binary_origin_contract.py python3 -B tests/r764_parent_path_security_contract.py + python3 -B tests/r766_systemd_exec_origin_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 24e5bdd6..2ccf088b 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -14,6 +14,8 @@ import json import os from pathlib import Path +import re +import stat import subprocess import sys import tempfile @@ -26,8 +28,34 @@ def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) +# This disposable Root systemd test must not become a generic command +# launcher. Only known unit operations, under the one absolute OS-owned +# systemctl executable, are permitted; all variable unit IDs are numeric. +UNIT_RE=re.compile(r"dim-r566-catchup-[0-9]{1,29}\.(?:timer|service)\Z") + + def run(*args,timeout=12): - p=subprocess.run(args,text=True,capture_output=True,timeout=timeout,check=False) + need(len(args)>=2 and all(type(item) is str for item in args) and + args[0]=="/usr/bin/systemctl","UNTRUSTED_COMMAND_ORIGIN") + program=Path("/usr/bin/systemctl") + need(program.is_file() and not program.is_symlink() and + program.stat().st_uid==0 and not program.stat().st_mode & 0o022, + "UNTRUSTED_SYSTEMCTL_BINARY") + action=args[1] + if action=="daemon-reload": + need(args==("/usr/bin/systemctl","daemon-reload"), + "INVALID_DAEMON_RELOAD_ARGUMENTS") + elif action in ("start","stop"): + need(len(args)==3 and bool(UNIT_RE.fullmatch(args[2])), + "INVALID_SYSTEMD_UNIT_ARGUMENTS") + elif action=="show": + need(len(args)==6 and bool(UNIT_RE.fullmatch(args[2])) and + args[3:]==("-p","ActiveState","--value"), + "INVALID_SYSTEMD_SHOW_ARGUMENTS") + else: + need(False,"UNTRUSTED_SYSTEMCTL_ACTION") + p=subprocess.run(("/usr/bin/systemctl",*args[1:]),text=True, + capture_output=True,timeout=timeout,check=False) need(p.returncode==0,"COMMAND_"+str(p.returncode)+"_"+p.stderr[-170:].replace(" ","_")) return p.stdout.strip() diff --git a/tests/r766_systemd_exec_origin_contract.py b/tests/r766_systemd_exec_origin_contract.py new file mode 100644 index 00000000..3f1922cc --- /dev/null +++ b/tests/r766_systemd_exec_origin_contract.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Unprivileged R766 hostile command-origin tests; never spawn systemctl.""" +import importlib.util +import pathlib +import unittest +from unittest.mock import patch +from types import SimpleNamespace + +TARGET=pathlib.Path(__file__).with_name("r566_native_systemd_persistent_catchup.py") +s=importlib.util.spec_from_file_location("dim_r766_sysctl",TARGET) +m=importlib.util.module_from_spec(s) +s.loader.exec_module(m) + +class TrustedSystemctlOnly(unittest.TestCase): + def _no_spawn(self,*args): + with patch.object(m.subprocess,"run",side_effect=AssertionError("UNEXPECTED_SUBPROCESS_EXECUTION")) as mocked: + with self.assertRaisesRegex(RuntimeError,"R566_SYSTEMD_FAIL_CLOSED_"): + m.run(*args) + mocked.assert_not_called() + + def test_non_systemctl_executable_never_spawns(self): + self._no_spawn("/bin/sh","-c","echo NOT_EXECUTED") + + def test_non_owned_target_unit_never_spawns(self): + self._no_spawn("/usr/bin/systemctl","start","../../other.service") + + def test_show_of_privileged_property_never_spawns(self): + self._no_spawn("/usr/bin/systemctl","show","dim-r566-catchup-123.timer", + "-p","ExecStart","--value") + + def test_valid_expected_commands_still_use_argv(self): + valid=[ + ("/usr/bin/systemctl","daemon-reload"), + ("/usr/bin/systemctl","start","dim-r566-catchup-123.timer"), + ("/usr/bin/systemctl","stop","dim-r566-catchup-123.timer"), + ("/usr/bin/systemctl","show","dim-r566-catchup-123.timer","-p","ActiveState","--value") + ] + with patch.object(m.subprocess,"run",return_value=SimpleNamespace(returncode=0,stdout="active\n",stderr="")) as mocked: + for args in valid: + self.assertEqual(m.run(*args),"active") + self.assertEqual(mocked.call_count,len(valid)) + for call,args in zip(mocked.call_args_list,valid): + self.assertEqual(call.args[0],args) + self.assertNotIn("shell",call.kwargs) + +if __name__=="__main__": + unittest.main(verbosity=2) From a0f6f64119a89af7c173f201d90429804dc1ff68 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 01:23:38 +0330 Subject: [PATCH 146/188] audit-only: forbid symlink-parent consent files in protected DNS (R767, DO NOT MERGE) --- .github/workflows/ci.yml | 1 + linux/system/encrypted_selective_dns.py | 82 +++++++++++++++---- .../r767_consent_parent_nofollow_contract.py | 30 +++++++ 3 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 tests/r767_consent_parent_nofollow_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 375579e8..e14ef73a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,7 @@ jobs: python3 -B tests/r762_trusted_dns_system_binary_origin_contract.py python3 -B tests/r764_parent_path_security_contract.py python3 -B tests/r766_systemd_exec_origin_contract.py + python3 -B tests/r767_consent_parent_nofollow_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py diff --git a/linux/system/encrypted_selective_dns.py b/linux/system/encrypted_selective_dns.py index 9281a1df..5da3df2d 100644 --- a/linux/system/encrypted_selective_dns.py +++ b/linux/system/encrypted_selective_dns.py @@ -20,6 +20,7 @@ import re import subprocess import sys +import stat import tempfile import time import uuid @@ -165,23 +166,74 @@ def read_state() -> dict | None: def require_consent(uid: int, home: str) -> None: + # This is a privileged opt-in check. Reading a legitimate leaf through + # a user-controlled symlink parent can otherwise select an unrelated + # token/profile. Traverse *all* directories by descriptor, no-follow. need(type(uid) is int and 0 < uid < 2**31, "UID") + need(type(home) is str and home.startswith("/home/"), "HOME_OWNER") p = pathlib.Path(home) - need(p.is_dir() and not p.is_symlink() and p.is_absolute() and - str(p).startswith("/home/") and p.stat().st_uid == uid,"HOME_OWNER") - app = p / ".local/share/DirectInternetMethod" - opt = app / NEW_OPTIN - old = app / OLD_OPTIN - need(not old.exists() and not old.is_symlink(), "LEGACY_UNENCRYPTED_OPTIN_CONFLICT") - need(opt.is_file() and not opt.is_symlink(), "EXPLICIT_ENCRYPTED_CONSENT_MISSING") - st = opt.stat() - need(st.st_uid == uid and st.st_mode & 0o077 == 0 and - st.st_size < 130 and opt.read_text().strip() == CONSENT, - "CONSENT_NOT_EXACT_OR_PRIVATE") - for n,expected in (("strategy.txt","balanced"),("scope.txt","targeted")): - pref=app/n - need(pref.is_file() and not pref.is_symlink() and - pref.read_text().strip() == expected,"INVALID_CONSENT_PROFILE_"+n) + need(p.is_absolute() and ".." not in p.parts and + len(p.parts) >= 3, "HOME_OWNER") + directory = os.open("/", os.O_RDONLY | os.O_DIRECTORY | + os.O_NOFOLLOW | os.O_CLOEXEC) + try: + home_parts = p.parts[1:] + names = home_parts + (".local", "share", "DirectInternetMethod") + for index, name in enumerate(names): + need(name not in (".", "..", "") and "/" not in name, + "CONSENT_PARENT_COMPONENT") + try: + child = os.open(name, os.O_RDONLY | os.O_DIRECTORY | + os.O_NOFOLLOW | os.O_CLOEXEC, + dir_fd=directory) + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_CONSENT_PARENT_COMPONENT") from exc + os.close(directory) + directory = child + info = os.fstat(directory) + if index == len(home_parts) - 1: + need(info.st_uid == uid, "HOME_OWNER") + if index >= len(home_parts): + need(info.st_uid == uid, "CONSENT_PARENT_OWNER") + + try: + os.stat(OLD_OPTIN, dir_fd=directory, follow_symlinks=False) + except FileNotFoundError: + pass + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_LEGACY_UNENCRYPTED_OPTIN_CONFLICT") from exc + else: + need(False, "LEGACY_UNENCRYPTED_OPTIN_CONFLICT") + + def token(name: str, missing: str, invalid: str, length: int, private: bool=False) -> str: + need("/" not in name and name not in (".", "..", ""), + "BAD_CONSENT_TOKEN_NAME") + try: + f = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | + os.O_CLOEXEC, dir_fd=directory) + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_" + missing) from exc + try: + meta = os.fstat(f) + need(stat.S_ISREG(meta.st_mode) and meta.st_uid == uid and + 0 <= meta.st_size <= length and + (not private or meta.st_mode & 0o077 == 0), invalid) + contents = os.read(f, length + 1) + need(len(contents) <= length, invalid) + return contents.decode("utf8").strip() + finally: + os.close(f) + + need(token(NEW_OPTIN, "EXPLICIT_ENCRYPTED_CONSENT_MISSING", + "CONSENT_NOT_EXACT_OR_PRIVATE",129, private=True) == CONSENT, + "CONSENT_NOT_EXACT_OR_PRIVATE") + for name, value in (("strategy.txt", "balanced"), + ("scope.txt", "targeted")): + need(token(name, "INVALID_CONSENT_PROFILE_" + name, + "INVALID_CONSENT_PROFILE_" + name,32) == value, + "INVALID_CONSENT_PROFILE_" + name) + finally: + os.close(directory) def link_state(iface: str) -> dict: diff --git a/tests/r767_consent_parent_nofollow_contract.py b/tests/r767_consent_parent_nofollow_contract.py new file mode 100644 index 00000000..ddb3c6a9 --- /dev/null +++ b/tests/r767_consent_parent_nofollow_contract.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +"""Isolated negative-first consent-parent symlink path contract, no root/network.""" +from pathlib import Path +import importlib.util +import os +import tempfile +import unittest + +ROOT=Path(__file__).resolve().parents[1] +s=importlib.util.spec_from_file_location("r767_dns_controller",ROOT/"linux/system/encrypted_selective_dns.py") +m=importlib.util.module_from_spec(s) +s.loader.exec_module(m) + +class ConsentPath(unittest.TestCase): + def test_user_home_private_consent_cannot_be_redirected_via_dotlocal_symlink(self): + with tempfile.TemporaryDirectory(prefix="r767-parent-",dir=str(ROOT)) as temp: + p=Path(temp);home=p/"fake-home";home.mkdir() + ext=p/"user-external-local";app=ext/"share/DirectInternetMethod" + app.mkdir(parents=True) + (home/".local").symlink_to(ext,target_is_directory=True) + (app/m.NEW_OPTIN).write_text(m.CONSENT+"\n") + (app/m.NEW_OPTIN).chmod(0o600) + (app/"scope.txt").write_text("targeted\n") + (app/"strategy.txt").write_text("balanced\n") + self.assertTrue((home/".local").is_symlink()) + self.assertFalse((home/".local/share/DirectInternetMethod"/m.NEW_OPTIN).is_symlink()) + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_CONSENT_PARENT_COMPONENT"): + m.require_consent(os.getuid(),str(home)) +if __name__=="__main__": + unittest.main(verbosity=2) From 10476722bb96fb048cd1a0be197bfe84de9ea952 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 01:42:43 +0330 Subject: [PATCH 147/188] audit-only: rebaseline trusted DNS consent Root and Guard SHA chain (R768, DO NOT MERGE) --- .github/workflows/ci.yml | 20 +++++++++---------- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 4 ++-- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 12 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e14ef73a..6003ccb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" - assert p.is_file() and p.stat().st_size==8410505 + expected="387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F" + assert p.is_file() and p.stat().st_size==8412857 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -171,7 +171,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -200,7 +200,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -233,7 +233,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -262,7 +262,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -291,7 +291,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -321,7 +321,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -366,7 +366,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -392,7 +392,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 1e31a74b..ac94544d 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -28,7 +28,7 @@ "29F9C04C07C01955DC67F491B417B89F57673FE782854025AC70E5D1B0EF7F31", "AB8E24FD468B7DEEBAC2F5CC4D7D5C309EDCB7E66B4F414BC9FB3FF97906F530",0o755), ("encrypted_controller","system/encrypted_selective_dns.py","usr/lib/directinternetmethod/encrypted_selective_dns.py", - None,"6E12A906D0B3AD6FD910303B55F89F76EB56C2EF1AABF15549806641285E461F",0o755), + None,"A8A8DD6155DDC4E2C5295A6CAA7B8809144C43985B47FD4664501ED683DB0AAE",0o755), ("encrypted_policy","system/encrypted_selective_policy.py","usr/lib/directinternetmethod/encrypted_selective_policy.py", None,"BCADC01C4A0DEDA0AB7C678155BE6F6960D3B68D9CFE1307DF85A94F18785663",0o644), ) diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index d720de40..dd5028a0 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="2DC3FBDEB21C589DA8889F03B450A69204ECAD90D8F113B3C9DD5F6C9DCE533A" +PIN_ROOT="5A50FDBF3D8CC207FEBA35B85274606E226508AB3D5059DF231789E0D947A616" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 95b664f6..f758175f 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="2DC3FBDEB21C589DA8889F03B450A69204ECAD90D8F113B3C9DD5F6C9DCE533A" +TX_SHA="5A50FDBF3D8CC207FEBA35B85274606E226508AB3D5059DF231789E0D947A616" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" +GUARD_SHA="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 82b285f1..6833d5b0 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A"} +EXPECTED={"version":VERSION,"files":28,"sha256":"387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -92,7 +92,7 @@ def test_original_unreleased_candidate_preserved_and_current_source_delta(self): "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", "system/upgrade_system.sh","install.sh","system/encrypted_selective_dns.py"}) self.assertEqual(after["system/encrypted_selective_dns.py"]["sha256"], - "6E12A906D0B3AD6FD910303B55F89F76EB56C2EF1AABF15549806641285E461F") + "A8A8DD6155DDC4E2C5295A6CAA7B8809144C43985B47FD4664501ED683DB0AAE") self.assertEqual(after["app/direct_internet_method.py"]["sha256"], "6CF031D2C7F18D7E7E4F1F9A37A1A0EBD3CA589F16F7E26274496F1F53E9317B") self.assertIn("encrypted=encrypted_dns_optin_state()", diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 12b9dcec..44bc8ba9 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="5d2e86cbaeb999eb404a03072387d772c33fe40233010a2f46ade5a9fa5d220a" +CANDIDATE_SHA="387216967dc1e0681d122b7ebada721983e7fc2c618ff0e1e7c1535db5ed497f" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 8d798ba8..05b4a7be 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "2dc3fbdeb21c589da8889f03b450a69204ecad90d8f113b3c9dd5f6c9dce533a" +ROOT_SHA = "5a50fdbf3d8cc207feba35b85274606e226508ab3d5059df231789e0d947a616" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 92f0fb87..e302f813 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "2dc3fbdeb21c589da8889f03b450a69204ecad90d8f113b3c9dd5f6c9dce533a" +PIN_ROOT = "5a50fdbf3d8cc207feba35b85274606e226508ab3d5059df231789e0d947a616" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 9227a91c..fef6fdca 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" +SOURCE_SHA="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 57790cd2..02c31f3c 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" -PIN_ROOT="2DC3FBDEB21C589DA8889F03B450A69204ECAD90D8F113B3C9DD5F6C9DCE533A" -PIN_GUARD="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" +PIN_PACKAGE="387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F" +PIN_ROOT="5A50FDBF3D8CC207FEBA35B85274606E226508AB3D5059DF231789E0D947A616" +PIN_GUARD="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 2ccf088b..404af1b6 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="7CA937E3589DC4F33C58658DBAD98E4A6308FCBC456B1D460F027FF499B3B09D" +PIN_GUARD="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index e1a493b4..9c3381ab 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="5D2E86CBAEB999EB404A03072387D772C33FE40233010A2F46ADE5A9FA5D220A" +ARCHIVE="387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From c598bb4f333c20a83e35785da1c5d8f5c7239674 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:14:15 +0330 Subject: [PATCH 148/188] audit-only: stream protected root files through no-follow descriptor chain (R769; DO NOT MERGE) --- .github/workflows/ci.yml | 1 + linux/system/protected_upgrade_transaction.py | 47 +++++++++++++--- ..._root_file_descriptor_boundary_contract.py | 53 +++++++++++++++++++ 3 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 tests/r769_root_file_descriptor_boundary_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6003ccb6..66cf88ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,6 +84,7 @@ jobs: python3 -B tests/r764_parent_path_security_contract.py python3 -B tests/r766_systemd_exec_origin_contract.py python3 -B tests/r767_consent_parent_nofollow_contract.py + python3 -B tests/r769_root_file_descriptor_boundary_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index ac94544d..14a25083 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -50,18 +50,53 @@ def need(ok,why): if not ok:raise RuntimeError("FAIL_CLOSED_"+why) def sha(data):return hashlib.sha256(data).hexdigest().upper() +@contextmanager +def verified_regular_fd(path): + """Pin a regular file by descriptor, never traversing a symlink parent. + + Unlike inspecting path.is_file() then opening later, both the metadata and + streamed bytes refer to the same opened inode. This does not modify files. + """ + path=pathlib.Path(path) + need(path.is_absolute() and ".." not in path.parts and + len(path.parts)>=2,"UNSAFE_ABSOLUTE_FILE_PATH") + flags=os.O_RDONLY|os.O_NOFOLLOW|os.O_CLOEXEC + parent=os.open("/",flags|os.O_DIRECTORY) + opened=None + try: + for part in path.parts[1:-1]: + try: + child=os.open(part,flags|os.O_DIRECTORY,dir_fd=parent) + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_UNSAFE_PARENT_COMPONENT") from exc + os.close(parent) + parent=child + try: + opened=os.open(path.name,flags,dir_fd=parent) + except OSError as exc: + raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") from exc + st=os.fstat(opened) + need(stat.S_ISREG(st.st_mode),"UNSAFE_FILE_TYPE_OR_SIZE") + yield opened,st + finally: + if opened is not None:os.close(opened) + os.close(parent) + def hash_file(path): h=hashlib.sha256() - with path.open("rb") as f: - for b in iter(lambda:f.read(1048576),b""):h.update(b) + with verified_regular_fd(path) as (fd,_): + with os.fdopen(os.dup(fd),"rb") as stream: + for block in iter(lambda:stream.read(1048576),b""): + h.update(block) return h.hexdigest().upper() def canonical(doc): return (json.dumps(doc,sort_keys=True,indent=2,ensure_ascii=False)+"\n").encode("utf8") def no_symlink_regular(path,owner=True): - need(path.is_file() and not path.is_symlink(),"MISSING_OR_SYMLINK_"+path.name) - st=path.stat() - if owner:need(st.st_uid==0 and st.st_gid==0,"OWNER_"+path.name) - return st + path=pathlib.Path(path) + need(not path.is_symlink(),"MISSING_OR_SYMLINK_"+path.name) + with verified_regular_fd(path) as (_,st): + if owner:need(st.st_uid==0 and st.st_gid==0,"OWNER_"+path.name) + return st def guarded_file_bytes(path, *, native_receipt=False, limit=2**21): """Read a bounded file through dirfds, refusing symlinks in EVERY parent. diff --git a/tests/r769_root_file_descriptor_boundary_contract.py b/tests/r769_root_file_descriptor_boundary_contract.py new file mode 100644 index 00000000..5e4334c6 --- /dev/null +++ b/tests/r769_root_file_descriptor_boundary_contract.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Rootless R769 path-taint regression: no real installation, no privilege, no network.""" +import hashlib +import importlib.util +import pathlib +import tempfile +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SOURCE=ROOT/"linux/system/protected_upgrade_transaction.py" +SPEC=importlib.util.spec_from_file_location("r769_root_tx",SOURCE) +MOD=importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MOD) + +class SecureFileAccess(unittest.TestCase): + def setUp(self): + self.temp=tempfile.TemporaryDirectory(prefix="r769-userowned-",dir=str(ROOT)) + self.root=pathlib.Path(self.temp.name) + self.trusted=self.root/"actual" + self.trusted.mkdir() + self.payload=self.trusted/"payload" + self.payload.write_bytes(b"FAKE_STATIC_ROOT_PAYLOAD") + self.alias=self.root/"redirected" + self.alias.symlink_to(self.trusted,target_is_directory=True) + self.target=self.alias/"payload" + self.assertTrue(self.target.is_file()) + self.assertFalse(self.target.is_symlink()) + + def tearDown(self): + self.temp.cleanup() + + def test_symlink_parent_hash_must_fail_closed(self): + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_PARENT_COMPONENT"): + MOD.hash_file(self.target) + + def test_symlink_parent_regular_stat_must_fail_closed(self): + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_PARENT_COMPONENT"): + MOD.no_symlink_regular(self.target,owner=False) + + def test_valid_private_file_preserves_hash_and_stat(self): + self.assertEqual(MOD.hash_file(self.payload),hashlib.sha256(self.payload.read_bytes()).hexdigest().upper()) + info=MOD.no_symlink_regular(self.payload,owner=False) + self.assertEqual(info.st_size,len(self.payload.read_bytes())) + + def test_large_root_static_binary_streams_safely(self): + # 8 MiB file exercises legitimate static 'ctrld' size without reading OS root. + large=self.root/"large-static" + with large.open("wb") as writer: + writer.write(b"X"*(8*1024*1024)) + self.assertEqual(MOD.hash_file(large),hashlib.sha256(large.read_bytes()).hexdigest().upper()) + +if __name__=="__main__": + unittest.main(verbosity=2) From 8b0fd6ff276b3fe89fd7bc636216140050876374 Mon Sep 17 00:00:00 2001 From: GOD13emad <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:21:51 +0330 Subject: [PATCH 149/188] audit-only: coherently pin R769 protected file descriptor guards and RC1 archive (DO NOT MERGE) --- .github/workflows/ci.yml | 20 +++++++++---------- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66cf88ca..5327c2ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F" - assert p.is_file() and p.stat().st_size==8412857 + expected="FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F" + assert p.is_file() and p.stat().st_size==8414235 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -172,7 +172,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -201,7 +201,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -234,7 +234,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -263,7 +263,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -292,7 +292,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -322,7 +322,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -367,7 +367,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -393,7 +393,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index dd5028a0..a7ef1f00 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="5A50FDBF3D8CC207FEBA35B85274606E226508AB3D5059DF231789E0D947A616" +PIN_ROOT="7E539F39CE89E4352655EC820730BA18EA6397B91A970943D8A3A37D739ACB3B" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index f758175f..4db83a97 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="5A50FDBF3D8CC207FEBA35B85274606E226508AB3D5059DF231789E0D947A616" +TX_SHA="7E539F39CE89E4352655EC820730BA18EA6397B91A970943D8A3A37D739ACB3B" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" +GUARD_SHA="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 6833d5b0..b21b6c1f 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F"} +EXPECTED={"version":VERSION,"files":28,"sha256":"FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 44bc8ba9..4a7e23c6 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="387216967dc1e0681d122b7ebada721983e7fc2c618ff0e1e7c1535db5ed497f" +CANDIDATE_SHA="fa99fcaa8e03c712c0d78eaee39c2cf102427a2ce9866fd97e1f76059775f14f" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 05b4a7be..ba3d1fec 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "5a50fdbf3d8cc207feba35b85274606e226508ab3d5059df231789e0d947a616" +ROOT_SHA = "7e539f39ce89e4352655ec820730ba18ea6397b91a970943d8a3a37d739acb3b" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index e302f813..f4e1c66c 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "5a50fdbf3d8cc207feba35b85274606e226508ab3d5059df231789e0d947a616" +PIN_ROOT = "7e539f39ce89e4352655ec820730ba18ea6397b91a970943d8a3a37d739acb3b" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index fef6fdca..c80a082a 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" +SOURCE_SHA="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 02c31f3c..8507f9c2 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F" -PIN_ROOT="5A50FDBF3D8CC207FEBA35B85274606E226508AB3D5059DF231789E0D947A616" -PIN_GUARD="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" +PIN_PACKAGE="FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F" +PIN_ROOT="7E539F39CE89E4352655EC820730BA18EA6397B91A970943D8A3A37D739ACB3B" +PIN_GUARD="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 404af1b6..30f48d0d 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="51DF6C262E409B5EF7FE78E09CA70AF679B3C5AD44FC8397309312C9706A52AD" +PIN_GUARD="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 9c3381ab..ee529532 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="387216967DC1E0681D122B7EBADA721983E7FC2C618FF0E1E7C1535DB5ED497F" +ARCHIVE="FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 67aefb8e0df8e3f5a269adcaadee21a7b89a5814 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:58:51 +0330 Subject: [PATCH 150/188] audit-only: reject user-owned FIFO before privileged file-read type check (R771, DO NOT MERGE) --- .github/workflows/ci.yml | 1 + linux/system/protected_upgrade_transaction.py | 6 +- ...1_fifo_special_file_failclosed_contract.py | 80 +++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/r771_fifo_special_file_failclosed_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5327c2ae..fc2e40f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,7 @@ jobs: python3 -B tests/r766_systemd_exec_origin_contract.py python3 -B tests/r767_consent_parent_nofollow_contract.py python3 -B tests/r769_root_file_descriptor_boundary_contract.py + python3 -B tests/r771_fifo_special_file_failclosed_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 14a25083..46576ab4 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -60,7 +60,8 @@ def verified_regular_fd(path): path=pathlib.Path(path) need(path.is_absolute() and ".." not in path.parts and len(path.parts)>=2,"UNSAFE_ABSOLUTE_FILE_PATH") - flags=os.O_RDONLY|os.O_NOFOLLOW|os.O_CLOEXEC + # O_NONBLOCK prevents a user-owned FIFO from blocking Root before fstat. + flags=os.O_RDONLY|os.O_NOFOLLOW|os.O_CLOEXEC|os.O_NONBLOCK parent=os.open("/",flags|os.O_DIRECTORY) opened=None try: @@ -108,7 +109,8 @@ def guarded_file_bytes(path, *, native_receipt=False, limit=2**21): path=pathlib.Path(path) need(path.is_absolute() and ".." not in path.parts and len(path.parts)>=2, "UNSAFE_ABSOLUTE_FILE_PATH") - flags=os.O_RDONLY|os.O_CLOEXEC|os.O_NOFOLLOW + # The source package is user-owned: never block on a FIFO before type check. + flags=os.O_RDONLY|os.O_CLOEXEC|os.O_NOFOLLOW|os.O_NONBLOCK directory=os.open("/",flags|os.O_DIRECTORY) opened=None try: diff --git a/tests/r771_fifo_special_file_failclosed_contract.py b/tests/r771_fifo_special_file_failclosed_contract.py new file mode 100644 index 00000000..2a3733de --- /dev/null +++ b/tests/r771_fifo_special_file_failclosed_contract.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""R771: user-owned special package file must fail closed without hanging Root. + +The test only invokes pure source helpers with disposable FIFO/regular files +under this checkout. Each potentially blocking call runs in a bounded child. +No root privileges, real system services or network are involved. +""" +import hashlib +import os +import pathlib +import subprocess +import sys +import tempfile +import unittest + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SOURCE=ROOT/"linux/system/protected_upgrade_transaction.py" +CHILD=r''' +import importlib.util,sys +spec=importlib.util.spec_from_file_location("dim_r771_root",sys.argv[1]) +m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m) +name,path=sys.argv[2:] +try: + if name=="no_symlink_regular": + m.no_symlink_regular(path,owner=False) + else: + getattr(m,name)(path) +except RuntimeError as exc: + if "FAIL_CLOSED_UNSAFE_FILE_TYPE_OR_SIZE" in str(exc): + print("R771_REJECTED_NONREGULAR") + sys.exit(0) + print("R771_UNEXPECTED_ERROR",type(exc).__name__,str(exc)) + sys.exit(91) +except Exception as exc: + print("R771_UNEXPECTED_EXCEPTION",type(exc).__name__,str(exc)) + sys.exit(92) +sys.exit(93) +''' + +class R771SpecialFileNoHang(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="r771-user-fifo-",dir=str(ROOT)) + self.base=pathlib.Path(self.tmp.name) + self.fifo=self.base/"untrusted-package-member" + os.mkfifo(self.fifo,0o600) + self.regular=self.base/"regular-payload" + self.regular.write_bytes(b"PINNED_REGULAR_FILE") + + def tearDown(self): + self.tmp.cleanup() + + def _must_reject_fifo(self,name): + command=[sys.executable,"-B","-c",CHILD,str(SOURCE),name,str(self.fifo)] + try: + response=subprocess.run(command,capture_output=True,text=True,timeout=2.0) + except subprocess.TimeoutExpired: + self.fail("R771_FIFO_OPEN_BLOCKED_WAITING_FOR_NONEXISTENT_WRITER") + self.assertEqual(response.returncode,0,(name,response.stdout,response.stderr)) + self.assertIn("R771_REJECTED_NONREGULAR",response.stdout) + + def test_01_guarded_source_bytes_reject_fifo_without_hang(self): + self._must_reject_fifo("guarded_file_bytes") + + def test_02_regular_file_validation_reject_fifo_without_hang(self): + self._must_reject_fifo("no_symlink_regular") + + def test_03_hash_file_reject_fifo_without_hang(self): + self._must_reject_fifo("hash_file") + + def test_04_valid_regular_files_remain_readable(self): + import importlib.util + spec=importlib.util.spec_from_file_location("dim_r771_inline",SOURCE) + module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module) + content=self.regular.read_bytes() + self.assertEqual(module.guarded_file_bytes(self.regular),content) + self.assertEqual(module.hash_file(self.regular),hashlib.sha256(content).hexdigest().upper()) + self.assertEqual(module.no_symlink_regular(self.regular,owner=False).st_size,len(content)) + +if __name__=="__main__": + unittest.main(verbosity=2) From 9ee02423aade799d8ecd7555f80f5094a5ecb72c Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 03:14:35 +0330 Subject: [PATCH 151/188] audit-only: coherently repin FIFO-safe Root/Guard and Linux RC1 artifact (R772; DO NOT MERGE) --- .github/workflows/ci.yml | 20 +++++++++---------- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc2e40f0..d8b84e32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F" - assert p.is_file() and p.stat().st_size==8414235 + expected="7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636" + assert p.is_file() and p.stat().st_size==8414421 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -173,7 +173,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -202,7 +202,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -235,7 +235,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -264,7 +264,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -293,7 +293,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -323,7 +323,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -368,7 +368,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -394,7 +394,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index a7ef1f00..5d944b67 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="7E539F39CE89E4352655EC820730BA18EA6397B91A970943D8A3A37D739ACB3B" +PIN_ROOT="A4261E4AFD63019BFC953030496BCB0B60949ECC5516C2CC639E081DE91641AE" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 4db83a97..dcaf1c1a 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="7E539F39CE89E4352655EC820730BA18EA6397B91A970943D8A3A37D739ACB3B" +TX_SHA="A4261E4AFD63019BFC953030496BCB0B60949ECC5516C2CC639E081DE91641AE" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" +GUARD_SHA="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index b21b6c1f..32193819 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F"} +EXPECTED={"version":VERSION,"files":28,"sha256":"7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 4a7e23c6..7eee2e68 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="fa99fcaa8e03c712c0d78eaee39c2cf102427a2ce9866fd97e1f76059775f14f" +CANDIDATE_SHA="7a15a380af1d4fa8ada9e9a55db09b8a988a597dec2b304acb59ce1b6bd53636" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index ba3d1fec..827c62fd 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "7e539f39ce89e4352655ec820730ba18ea6397b91a970943d8a3a37d739acb3b" +ROOT_SHA = "a4261e4afd63019bfc953030496bcb0b60949ecc5516c2cc639e081de91641ae" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index f4e1c66c..5ee5af47 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "7e539f39ce89e4352655ec820730ba18ea6397b91a970943d8a3a37d739acb3b" +PIN_ROOT = "a4261e4afd63019bfc953030496bcb0b60949ecc5516c2cc639e081de91641ae" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index c80a082a..027829cf 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" +SOURCE_SHA="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 8507f9c2..79ea5423 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F" -PIN_ROOT="7E539F39CE89E4352655EC820730BA18EA6397B91A970943D8A3A37D739ACB3B" -PIN_GUARD="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" +PIN_PACKAGE="7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636" +PIN_ROOT="A4261E4AFD63019BFC953030496BCB0B60949ECC5516C2CC639E081DE91641AE" +PIN_GUARD="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 30f48d0d..9da3a8b0 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="2A5125C190FE4251CD4D75A5BA1DA1B49595CB2CDA2AE20A875A106E3051947B" +PIN_GUARD="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index ee529532..bc6e647d 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="FA99FCAA8E03C712C0D78EAEE39C2CF102427A2CE9866FD97E1F76059775F14F" +ARCHIVE="7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 0a1369373ea29345692faa7f7059410c05b78c38 Mon Sep 17 00:00:00 2001 From: Project QA Automation Date: Mon, 19 Oct 2026 11:59:10 +0330 Subject: [PATCH 152/188] test(security): pin Root metadata and manifest reads to one fd (R776) --- .github/workflows/ci.yml | 21 +-- linux/system/protected_upgrade_transaction.py | 27 +++- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r776_checked_fd_swap_regression.py | 134 ++++++++++++++++++ 13 files changed, 178 insertions(+), 30 deletions(-) create mode 100644 tests/r776_checked_fd_swap_regression.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8b84e32..dcf83d27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636" - assert p.is_file() and p.stat().st_size==8414421 + expected="D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E" + assert p.is_file() and p.stat().st_size==8415103 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -86,6 +86,7 @@ jobs: python3 -B tests/r767_consent_parent_nofollow_contract.py python3 -B tests/r769_root_file_descriptor_boundary_contract.py python3 -B tests/r771_fifo_special_file_failclosed_contract.py + python3 -B tests/r776_checked_fd_swap_regression.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py @@ -173,7 +174,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -202,7 +203,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -235,7 +236,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -264,7 +265,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -293,7 +294,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -323,7 +324,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -368,7 +369,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -394,7 +395,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 46576ab4..b55b329d 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -90,6 +90,20 @@ def hash_file(path): for block in iter(lambda:stream.read(1048576),b""): h.update(block) return h.hexdigest().upper() +def checked_regular_bytes(path, *, owner=False, mode=None, max_bytes=2**20): + """Check identity, ownership, type and read from ONE pinned regular inode.""" + with verified_regular_fd(path) as (fd, st): + if owner: + need(st.st_uid==0 and st.st_gid==0, + "CHECKED_FILE_FOREIGN_OWNER") + if mode is not None: + need(stat.S_IMODE(st.st_mode)==mode, "CHECKED_FILE_MODE") + need(0<=st.st_size<=max_bytes, "CHECKED_FILE_TOO_LARGE") + with os.fdopen(os.dup(fd),"rb") as stream: + data=stream.read(max_bytes+1) + need(len(data)<=max_bytes, "CHECKED_FILE_SIZE_DRIFT") + return data + def canonical(doc): return (json.dumps(doc,sort_keys=True,indent=2,ensure_ascii=False)+"\n").encode("utf8") def no_symlink_regular(path,owner=True): @@ -177,9 +191,8 @@ def p(self,relative): ".." not in pathlib.PurePosixPath(relative).parts,"PATH_TRAVERSAL") return self.root/relative def original_metadata(self): - s=no_symlink_regular(self.meta,self.native) - need(stat.S_IMODE(s.st_mode)==0o644,"METADATA_MODE") - d=json.loads(self.meta.read_text("utf8")) + d=json.loads(checked_regular_bytes( + self.meta,owner=self.native,mode=0o644,max_bytes=65536).decode("utf8")) need(type(d)==dict and d.get("schema")==1 and d.get("version")==CURRENT_BACKEND_VERSION,"OLD_ROOT_VERSION") uid=d.get("uid") @@ -189,8 +202,8 @@ def original_metadata(self): need(isinstance(home,str) and home.startswith("/home/") and ".." not in pathlib.PurePosixPath(home).parts,"OWNER_HOME") idfile=self.p("etc/directinternetmethod.uid") - need(no_symlink_regular(idfile,self.native) and idfile.read_text().strip()==str(uid), - "UID_MARKER_CHANGED") + uid_marker=checked_regular_bytes(idfile,owner=self.native,max_bytes=128) + need(uid_marker.decode("utf8").strip()==str(uid),"UID_MARKER_CHANGED") return d def _baseline(self): before=self.original_metadata() @@ -248,8 +261,8 @@ def manifest(self): s=self.pending.stat() need(s.st_uid==0 and stat.S_IMODE(s.st_mode)==0o700,"SLOT_UNOWNED") p=self.pending/"manifest.json" - no_symlink_regular(p,self.native) - data=json.loads(p.read_text("utf8")) + data=json.loads(checked_regular_bytes( + p,owner=self.native,max_bytes=131072).decode("utf8")) need(data.get("schema")==SCHEMA and data.get("revision")=="R519" and data.get("fromVersion")==CURRENT_BACKEND_VERSION and data.get("toVersion")==TARGET_VERSION,"MANIFEST_VERSION") diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 5d944b67..478bd909 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="A4261E4AFD63019BFC953030496BCB0B60949ECC5516C2CC639E081DE91641AE" +PIN_ROOT="8A051957350E68E1938604BFA286FA4F528D8930798D385B0B1FB49FC6751682" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index dcaf1c1a..c0518204 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="A4261E4AFD63019BFC953030496BCB0B60949ECC5516C2CC639E081DE91641AE" +TX_SHA="8A051957350E68E1938604BFA286FA4F528D8930798D385B0B1FB49FC6751682" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" +GUARD_SHA="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 32193819..1a4cdcaf 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636"} +EXPECTED={"version":VERSION,"files":28,"sha256":"D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 7eee2e68..83a7f377 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="7a15a380af1d4fa8ada9e9a55db09b8a988a597dec2b304acb59ce1b6bd53636" +CANDIDATE_SHA="d0cbfed6d71e9a704a45484ad2f37992f931e7481142731f6a66fb0f20a9367e" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 827c62fd..b90f9fcd 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "a4261e4afd63019bfc953030496bcb0b60949ecc5516c2cc639e081de91641ae" +ROOT_SHA = "8a051957350e68e1938604bfa286fa4f528d8930798d385b0b1fb49fc6751682" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 5ee5af47..9ff24874 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "a4261e4afd63019bfc953030496bcb0b60949ecc5516c2cc639e081de91641ae" +PIN_ROOT = "8a051957350e68e1938604bfa286fa4f528d8930798d385b0b1fb49fc6751682" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 027829cf..6823ce1f 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" +SOURCE_SHA="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 79ea5423..c6f4d025 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636" -PIN_ROOT="A4261E4AFD63019BFC953030496BCB0B60949ECC5516C2CC639E081DE91641AE" -PIN_GUARD="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" +PIN_PACKAGE="D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E" +PIN_ROOT="8A051957350E68E1938604BFA286FA4F528D8930798D385B0B1FB49FC6751682" +PIN_GUARD="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 9da3a8b0..ef8e26c6 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="3353D1339096518819BD955DF4DB1CDAA6CC39BCFA0503328B77850AFE43B4B9" +PIN_GUARD="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index bc6e647d..3b51435e 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="7A15A380AF1D4FA8ADA9E9A55DB09B8A988A597DEC2B304ACB59CE1B6BD53636" +ARCHIVE="D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r776_checked_fd_swap_regression.py b/tests/r776_checked_fd_swap_regression.py new file mode 100644 index 00000000..4bf44e9c --- /dev/null +++ b/tests/r776_checked_fd_swap_regression.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""R776 negative-first deterministic metadata/manifest TOCTOU read regression. + +Scratch-owned fake root only; no elevated privileges, installed files or network. +A controlled file swap occurs immediately after an existing validity check. +""" +import importlib.util +from contextlib import contextmanager +import json +import pathlib +import tempfile +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location( + "dim_r776_toc", ROOT / "linux/system/protected_upgrade_transaction.py") +mod = importlib.util.module_from_spec(spec) +spec.loader.exec_module(mod) + +def write_json(p, data, mode=0o644): + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text(json.dumps(data, sort_keys=True) + "\n", encoding="utf-8") + p.chmod(mode) + +class SwappedPathAfterCheck(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory(prefix="r776-fakeroot-", dir=str(ROOT)) + self.home = pathlib.Path(self.tmp.name) + self.root = self.home / "fake" + self.tx = mod.RootTransaction(self.root) + self.meta = self.tx.meta + self.good = { + "schema": 1, "version": mod.CURRENT_BACKEND_VERSION, + "uid": 1000, "user": "trusted", "home": "/home/trusted" + } + self.evil = dict(self.good, user="attacker", home="/home/attacker") + write_json(self.meta, self.good) + self.malicious_meta = self.home / "user-controlled-meta.json" + write_json(self.malicious_meta, self.evil) + marker = self.root / "etc/directinternetmethod.uid" + marker.parent.mkdir(parents=True, exist_ok=True) + marker.write_text("1000\n", encoding="utf-8") + self.manifest = self.tx.pending / "manifest.json" + self.good_manifest = { + "schema": mod.SCHEMA, "revision": "R519", + "fromVersion": mod.CURRENT_BACKEND_VERSION, + "toVersion": mod.TARGET_VERSION, "phase": "STAGED", + "files": {name: {} for name,*_ in mod.FILES}, + } + self.evil_manifest = dict(self.good_manifest, phase="COMMITTED") + write_json(self.manifest, self.good_manifest, mode=0o600) + self.malicious_manifest = self.home / "user-controlled-manifest.json" + write_json(self.malicious_manifest, self.evil_manifest, mode=0o600) + + def tearDown(self): + self.tmp.cleanup() + + def swap_during_check(self, target, replacement): + original = mod.no_symlink_regular + called = [] + def check_and_swap(p, owner=True): + st = original(p, owner) + if pathlib.Path(p) == target: + called.append(True) + target.rename(target.with_name(target.name + ".original")) + target.symlink_to(replacement) + return st + mod.no_symlink_regular = check_and_swap + return original, called + + def test_valid_unmodified_metadata(self): + self.assertEqual(self.tx.original_metadata(), self.good) + + def test_valid_unmodified_manifest(self): + self.assertEqual(self.tx.manifest()["phase"], "STAGED") + + def test_metadata_swap_does_not_accept_unvalidated_path(self): + original, called = self.swap_during_check(self.meta, self.malicious_meta) + try: + try: + result = self.tx.original_metadata() + except RuntimeError: + return + self.assertNotEqual(result["home"], "/home/attacker", + "R776_TOCTOU_WRONG_METADATA_ACCEPTED") + finally: + mod.no_symlink_regular = original + + def test_manifest_swap_does_not_accept_unvalidated_path(self): + original, called = self.swap_during_check(self.manifest, self.malicious_manifest) + try: + try: + result = self.tx.manifest() + except RuntimeError: + return + self.assertNotEqual(result["phase"], "COMMITTED", + "R776_TOCTOU_WRONG_MANIFEST_ACCEPTED") + finally: + mod.no_symlink_regular = original + + def swap_after_inode_is_open(self, target, replacement): + """Model a rename/symlink race AFTER the source inode is pinned.""" + original = mod.verified_regular_fd + swapped = [] + @contextmanager + def open_and_swap(path): + with original(path) as (fd, st): + if pathlib.Path(path) == target: + swapped.append(True) + target.rename(target.with_name(target.name + ".pinned")) + target.symlink_to(replacement) + yield fd, st + mod.verified_regular_fd = open_and_swap + return original, swapped + + def test_original_metadata_read_remains_on_pinned_inode(self): + original, switched = self.swap_after_inode_is_open(self.meta, self.malicious_meta) + try: + self.assertEqual(self.tx.original_metadata(), self.good) + self.assertEqual(switched, [True]) + finally: + mod.verified_regular_fd = original + + def test_manifest_read_remains_on_pinned_inode(self): + original, switched = self.swap_after_inode_is_open( + self.manifest, self.malicious_manifest) + try: + self.assertEqual(self.tx.manifest()["phase"], "STAGED") + self.assertEqual(switched, [True]) + finally: + mod.verified_regular_fd = original + +if __name__ == "__main__": + unittest.main(verbosity=2) From e7901c5a3b8283c01778ebb12d2366718e8e3904 Mon Sep 17 00:00:00 2001 From: Project QA Automation Date: Mon, 19 Oct 2026 12:08:03 +0330 Subject: [PATCH 153/188] fix(security): retain native Root metadata-mode diagnostic (R777) --- .github/workflows/ci.yml | 20 +++++++++---------- linux/system/protected_upgrade_transaction.py | 2 +- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 ++-- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r776_checked_fd_swap_regression.py | 4 ++++ 13 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcf83d27..d6164ab3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E" - assert p.is_file() and p.stat().st_size==8415103 + expected="9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2" + assert p.is_file() and p.stat().st_size==8415099 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -174,7 +174,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -203,7 +203,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -236,7 +236,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -265,7 +265,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -294,7 +294,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -324,7 +324,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -369,7 +369,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -395,7 +395,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index b55b329d..a5df915c 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -97,7 +97,7 @@ def checked_regular_bytes(path, *, owner=False, mode=None, max_bytes=2**20): need(st.st_uid==0 and st.st_gid==0, "CHECKED_FILE_FOREIGN_OWNER") if mode is not None: - need(stat.S_IMODE(st.st_mode)==mode, "CHECKED_FILE_MODE") + need(stat.S_IMODE(st.st_mode)==mode, "METADATA_MODE") need(0<=st.st_size<=max_bytes, "CHECKED_FILE_TOO_LARGE") with os.fdopen(os.dup(fd),"rb") as stream: data=stream.read(max_bytes+1) diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 478bd909..85929e5b 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="8A051957350E68E1938604BFA286FA4F528D8930798D385B0B1FB49FC6751682" +PIN_ROOT="303A5A74FCB8610F86168370DA81083F4007D3B8FB5E7A2EA963389BC0D20859" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index c0518204..453703d6 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="8A051957350E68E1938604BFA286FA4F528D8930798D385B0B1FB49FC6751682" +TX_SHA="303A5A74FCB8610F86168370DA81083F4007D3B8FB5E7A2EA963389BC0D20859" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" +GUARD_SHA="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 1a4cdcaf..080b3f3c 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E"} +EXPECTED={"version":VERSION,"files":28,"sha256":"9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 83a7f377..87dd963c 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="d0cbfed6d71e9a704a45484ad2f37992f931e7481142731f6a66fb0f20a9367e" +CANDIDATE_SHA="9874007429d3a7337187b19bd04c79d74cc1a97d96d4b655257c183e65bdc7f2" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index b90f9fcd..67f91545 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "8a051957350e68e1938604bfa286fa4f528d8930798d385b0b1fb49fc6751682" +ROOT_SHA = "303a5a74fcb8610f86168370da81083f4007d3b8fb5e7a2ea963389bc0d20859" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 9ff24874..88cb182e 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "8a051957350e68e1938604bfa286fa4f528d8930798d385b0b1fb49fc6751682" +PIN_ROOT = "303a5a74fcb8610f86168370da81083f4007d3b8fb5e7a2ea963389bc0d20859" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 6823ce1f..56c30dc8 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" +SOURCE_SHA="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index c6f4d025..691c68f4 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E" -PIN_ROOT="8A051957350E68E1938604BFA286FA4F528D8930798D385B0B1FB49FC6751682" -PIN_GUARD="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" +PIN_PACKAGE="9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2" +PIN_ROOT="303A5A74FCB8610F86168370DA81083F4007D3B8FB5E7A2EA963389BC0D20859" +PIN_GUARD="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index ef8e26c6..8e66328e 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="7B3945033D7806D324D10A8CDEE0EE2B56D19F01ED0E1A18065595F4D1000C6E" +PIN_GUARD="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 3b51435e..79a192cd 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="D0CBFED6D71E9A704A45484AD2F37992F931E7481142731F6A66FB0F20A9367E" +ARCHIVE="9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r776_checked_fd_swap_regression.py b/tests/r776_checked_fd_swap_regression.py index 4bf44e9c..39d618b5 100644 --- a/tests/r776_checked_fd_swap_regression.py +++ b/tests/r776_checked_fd_swap_regression.py @@ -68,6 +68,10 @@ def check_and_swap(p, owner=True): mod.no_symlink_regular = check_and_swap return original, called + def test_legacy_metadata_mode_error_is_preserved(self): + self.meta.chmod(0o600) + with self.assertRaisesRegex(RuntimeError, "METADATA_MODE"): + self.tx.original_metadata() def test_valid_unmodified_metadata(self): self.assertEqual(self.tx.original_metadata(), self.good) From 93fce50c705dd20ada2240fb7025e13df45413c6 Mon Sep 17 00:00:00 2001 From: Project QA Automation Date: Mon, 19 Oct 2026 12:52:16 +0330 Subject: [PATCH 154/188] fix(linux): pin protected Stage baseline backups before slot creation (R778) --- .github/workflows/ci.yml | 21 +++--- linux/system/protected_upgrade_transaction.py | 19 +++++- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ...778_stage_backup_snapshot_race_contract.py | 67 +++++++++++++++++++ 13 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 tests/r778_stage_backup_snapshot_race_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6164ab3..cbfa9001 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2" - assert p.is_file() and p.stat().st_size==8415099 + expected="79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4" + assert p.is_file() and p.stat().st_size==8415972 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -87,6 +87,7 @@ jobs: python3 -B tests/r769_root_file_descriptor_boundary_contract.py python3 -B tests/r771_fifo_special_file_failclosed_contract.py python3 -B tests/r776_checked_fd_swap_regression.py + python3 -B tests/r778_stage_backup_snapshot_race_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py @@ -174,7 +175,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -203,7 +204,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -236,7 +237,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -265,7 +266,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -294,7 +295,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -324,7 +325,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -369,7 +370,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -395,7 +396,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index a5df915c..b68ef1b0 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -287,6 +287,22 @@ def stage(self,source): blob=guarded_file_bytes(file) need(sha(blob)==want,"PACKAGE_SOURCE_SHA_"+name) data[name]=blob + # R778: both protected baseline snapshots must be captured through + # the same pinned descriptor used to check identity and permissions. + # Fail before creating a Root pending slot if files changed after + # _baseline(), rather than retaining an untrusted backup. + originals={} + for name,rel,target,old,new,mode in FILES: + if old: + old_blob=checked_regular_bytes( + self.p(target),owner=self.native,mode=mode, + max_bytes=1048576) + need(sha(old_blob)==old,"BASELINE_BACKUP_CHANGED_"+name) + originals[name]=old_blob + orig=checked_regular_bytes( + self.meta,owner=self.native,mode=0o644,max_bytes=65536) + need(json.loads(orig.decode("utf8"))==before, + "BASELINE_METADATA_CHANGED_AFTER_PREFLIGHT") self.pending.mkdir(mode=0o700) (self.pending/"old").mkdir(mode=0o700) (self.pending/"new").mkdir(mode=0o700) @@ -295,9 +311,8 @@ def stage(self,source): for name,rel,target,old,new,mode in FILES: atomic_bytes(self.pending/"new"/name,data[name],0o600,self.native) if old: - atomic_bytes(self.pending/"old"/name,self.p(target).read_bytes(),0o600,self.native) + atomic_bytes(self.pending/"old"/name,originals[name],0o600,self.native) entries[name]={"target":target,"old":old,"new":new,"mode":mode} - orig=self.meta.read_bytes() atomic_bytes(self.pending/"old"/"metadata",orig,0o600,self.native) new_meta=dict(before,version=TARGET_VERSION) new_blob=canonical(new_meta) diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 85929e5b..3b9eaa51 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="303A5A74FCB8610F86168370DA81083F4007D3B8FB5E7A2EA963389BC0D20859" +PIN_ROOT="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 453703d6..d2a2dad2 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="303A5A74FCB8610F86168370DA81083F4007D3B8FB5E7A2EA963389BC0D20859" +TX_SHA="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" +GUARD_SHA="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 080b3f3c..33384a30 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2"} +EXPECTED={"version":VERSION,"files":28,"sha256":"79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 87dd963c..9d6fd075 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="9874007429d3a7337187b19bd04c79d74cc1a97d96d4b655257c183e65bdc7f2" +CANDIDATE_SHA="79a8f080bdfd4545fd75ed1eebe5ff588718b4966632a7212861f2b786cff9d4" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 67f91545..72ad60ab 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "303a5a74fcb8610f86168370da81083f4007d3b8fb5e7a2ea963389bc0d20859" +ROOT_SHA = "f89aba464f1c1b0a59300fa180cc061966c0b07db83c70fc06ac28f7a1f98b5d" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 88cb182e..13c08b7d 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "303a5a74fcb8610f86168370da81083f4007d3b8fb5e7a2ea963389bc0d20859" +PIN_ROOT = "f89aba464f1c1b0a59300fa180cc061966c0b07db83c70fc06ac28f7a1f98b5d" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 56c30dc8..9ea400df 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" +SOURCE_SHA="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 691c68f4..efab2341 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2" -PIN_ROOT="303A5A74FCB8610F86168370DA81083F4007D3B8FB5E7A2EA963389BC0D20859" -PIN_GUARD="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" +PIN_PACKAGE="79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4" +PIN_ROOT="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" +PIN_GUARD="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 8e66328e..6dc22246 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="35A7F5A0ABCA9C1ECA6389DF788036B5F801CB127D841198CDBBCEC28EDEFB82" +PIN_GUARD="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 79a192cd..667213ef 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="9874007429D3A7337187B19BD04C79D74CC1A97D96D4B655257C183E65BDC7F2" +ARCHIVE="79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r778_stage_backup_snapshot_race_contract.py b/tests/r778_stage_backup_snapshot_race_contract.py new file mode 100644 index 00000000..7ec70e31 --- /dev/null +++ b/tests/r778_stage_backup_snapshot_race_contract.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""R778 synthetic-fake-root negative-first stage snapshot race; never privileged. + +A controlled attacker-controlled symlink swap happens after accepted baseline +checks and before old backup bytes are captured. The intended security contract +is fail-closed before creating any pending transaction slot. +""" +import importlib.util +import json +import pathlib +import unittest + +BASE=pathlib.Path(__file__).resolve().parents[1] +spec=importlib.util.spec_from_file_location("r778_original_fixture", + BASE/"tests/protected_upgrade_transaction_contract.py") +fixture_module=importlib.util.module_from_spec(spec) +spec.loader.exec_module(fixture_module) + +class StageSnapshotRace(unittest.TestCase): + def setUp(self): + self.case=fixture_module.FakeFixture("test_success_stage_apply_rollback") + self.case.setUp() + + def tearDown(self): + self.case.tearDown() + + def _swap_after_baseline(self,target: pathlib.Path, content:bytes): + replacement=self.case.root/("untrusted-r778-"+target.name) + replacement.write_bytes(content) + old=self.case.tx._baseline + calls=[] + def switch(): + verified=old() + calls.append(True) + target.rename(target.with_name(target.name+".original-unchanged")) + target.symlink_to(replacement) + return verified + self.case.tx._baseline=switch + return calls,replacement + + def _must_reject_before_pending(self,calls): + with self.assertRaises((RuntimeError,OSError,ValueError)): + self.case.tx.stage(self.case.pkg) + self.assertEqual(calls,[True]) + self.assertFalse(self.case.tx.pending.exists(), + "R778_UNVERIFIED_PROTECTED_BACKUP_SLOT_CREATED") + + def test_01_valid_no_swap_stage_is_unmodified(self): + self.assertEqual(self.case.tx.stage(self.case.pkg)["status"],"STAGED") + self.case.all_old() + self.assertTrue(self.case.tx.pending.is_dir()) + + def test_02_old_protected_file_swapped_to_symlink_after_baseline(self): + row=next(x for x in self.case.files if x[3]) + target=self.case.root/row[2] + calls,_=self._swap_after_baseline(target,b"ATTACKER_CONTROLLED_UNPINNED_OLD_BYTES") + self._must_reject_before_pending(calls) + + def test_03_root_metadata_swapped_after_baseline(self): + meta=self.case.meta + attacker=dict(self.case.orig,home="/home/attacker") + data=(json.dumps(attacker,sort_keys=True)+"\n").encode() + calls,_=self._swap_after_baseline(meta,data) + self._must_reject_before_pending(calls) + +if __name__=="__main__": + unittest.main(verbosity=2) From c31783167ce22be2c30e379d2a99650056601f5a Mon Sep 17 00:00:00 2001 From: Project QA Automation Date: Mon, 19 Oct 2026 13:45:12 +0330 Subject: [PATCH 155/188] fix(linux): execute Root upgrade only from SHA-pinned protected copies (R779) --- .github/workflows/ci.yml | 21 +-- linux/install.sh | 3 +- linux/system/upgrade_guard.py | 52 ++++++- linux/system/upgrade_system.sh | 33 +++- tests/r519_versioned_package_contract.py | 10 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 4 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 4 +- ..._watchdog_firstsplit_integrity_contract.py | 2 +- ...oot_guard_source_copy_identity_contract.py | 143 ++++++++++++++++++ 12 files changed, 244 insertions(+), 34 deletions(-) create mode 100644 tests/r779_root_guard_source_copy_identity_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbfa9001..b8494617 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4" - assert p.is_file() and p.stat().st_size==8415972 + expected="04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386" + assert p.is_file() and p.stat().st_size==8419073 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -88,6 +88,7 @@ jobs: python3 -B tests/r771_fifo_special_file_failclosed_contract.py python3 -B tests/r776_checked_fd_swap_regression.py python3 -B tests/r778_stage_backup_snapshot_race_contract.py + python3 -B tests/r779_root_guard_source_copy_identity_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py @@ -175,7 +176,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -204,7 +205,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -237,7 +238,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -266,7 +267,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -295,7 +296,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -325,7 +326,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -370,7 +371,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -396,7 +397,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/linux/install.sh b/linux/install.sh index 0baa1001..89c5bd4a 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -12,7 +12,8 @@ ICONS="$HOME/.local/share/icons/hicolor/scalable/apps" USER_TRANSACTION="$HERE/app/user_update_transaction.py" TRANSACTION_ACTIVE=0 ROOT_UPGRADE_STAGED=0 -ROOT_TRANSACTION="$HERE/system/protected_upgrade_transaction.py" +# R779: a rollback must never execute user-writable package code as Root. +ROOT_TRANSACTION="/var/lib/directinternetmethod/upgrade-guard/root_tx.py" rollback_failed_install() { local code="$1" trap - ERR HUP INT TERM diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 3b9eaa51..66293afa 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -59,6 +59,40 @@ def regular(path:Path,uid=None,mode=None): def canonical(obj):return (json.dumps(obj,indent=2,sort_keys=True)+"\n").encode() +def pinned_package_bytes(source:Path,relative:str,want:str,limit:int=4*1024*1024)->bytes: + """Pin and SHA-verify package bytes from one opened regular inode. + + Both the user-owned package and its ancestor directories may change between + the initial guard preflight and the Root operation. No path-based reopen, + no symlink parent and no blocking open of FIFO/special source nodes. + """ + path=source/relative + need(path.is_absolute() and ".." not in path.parts and len(path.parts)>1, + "PINNED_PACKAGE_PATH") + flags=os.O_RDONLY|os.O_CLOEXEC|os.O_NOFOLLOW|os.O_NONBLOCK + directory=os.open("/",flags|os.O_DIRECTORY) + opened=None + try: + try: + for name in path.parts[1:-1]: + child=os.open(name,flags|os.O_DIRECTORY,dir_fd=directory) + os.close(directory);directory=child + opened=os.open(path.name,flags,dir_fd=directory) + except OSError as exc: + raise RuntimeError("R552_GUARD_FAIL_CLOSED_PINNED_SOURCE_OPEN") from exc + info=os.fstat(opened) + need(stat.S_ISREG(info.st_mode) and 0<=info.st_size<=limit, + "PINNED_SOURCE_REGULAR_AND_SIZE") + with os.fdopen(os.dup(opened),"rb") as reader: + payload=reader.read(limit+1) + need(len(payload)<=limit and + hashlib.sha256(payload).hexdigest().upper()==want.upper(), + "PINNED_SOURCE_SHA_CHANGED") + return payload + finally: + if opened is not None:os.close(opened) + os.close(directory) + def command(args,timeout=15): return subprocess.run(args,text=True,capture_output=True,timeout=timeout,check=False) @@ -124,10 +158,8 @@ def validate_source(source:Path): for label,relative,want in ( ("ROOT_TX","system/protected_upgrade_transaction.py",PIN_ROOT), ("USER_TX","app/user_update_transaction.py",PIN_USER)): - candidate=source/relative - regular(candidate) - need(sha_file(candidate)==want and str(pin.get(relative,"")).upper()==want, - "PACKAGE_"+label) + pinned_package_bytes(source,relative,want) + need(str(pin.get(relative,"")).upper()==want,"PACKAGE_"+label) return True def preflight(source:Path): @@ -145,10 +177,18 @@ def preflight(source:Path): "guardArmed":False,"networkUntouched":True} def guard_files(source:Path,uid:int,user:str,home:Path,um:Path,rm:Path): + # R779: re-open/verify the exact source bytes before any protected output. + # The initial preflight is not sufficient against a user-owned source race. + root_payload=pinned_package_bytes( + source,"system/protected_upgrade_transaction.py",PIN_ROOT) + user_payload=pinned_package_bytes( + source,"app/user_update_transaction.py",PIN_USER) GUARD.mkdir(mode=0o711) os.chown(GUARD,0,0);os.chmod(GUARD,0o711) - write_new(ROOT_TX,(source/"system/protected_upgrade_transaction.py").read_bytes(),0o500) - write_new(USER_TX,(source/"app/user_update_transaction.py").read_bytes(),0o644) + write_new(ROOT_TX,root_payload,0o500) + write_new(USER_TX,user_payload,0o644) + need(sha_file(ROOT_TX)==PIN_ROOT and sha_file(USER_TX)==PIN_USER, + "PINNED_GUARD_ROOT_USER_COPY_SHA") write_new(WORKER,Path(__file__).read_bytes(),0o500) dns=Path("/etc/resolv.conf") baseline={"schema":1,"revision":"R552","uid":uid,"user":user, diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index d2a2dad2..999c275e 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" +GUARD_SHA="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } @@ -48,7 +48,29 @@ GUARD_SHA="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" "$(printf '%s' "$GUARD_SHA" | tr '[:upper:]' '[:lower:]')" ] || { echo "R553_GUARD_SOURCE_SHA_CHANGED" >&2; exit 87; } -python3 "$SRC/system/upgrade_guard.py" arm --source "$SRC" --uid "$REQ_UID" +# R779: the user-owned source can change after its first SHA check. Copy +# into a private Root-only runtime directory, then hash the actual bytes that +# will be interpreted. This bootstrap is never a permanent second install. +GUARD_BOOTSTRAP_DIR="$(mktemp -d /run/directinternetmethod-guard-XXXXXXXX)" +[ -d "$GUARD_BOOTSTRAP_DIR" ] && [ ! -L "$GUARD_BOOTSTRAP_DIR" ] && +[ "$(stat -c %u -- "$GUARD_BOOTSTRAP_DIR")" = 0 ] && +[ "$(stat -c %a -- "$GUARD_BOOTSTRAP_DIR")" = 700 ] || { + echo 'R779_ROOT_BOOTSTRAP_DIR_UNSAFE' >&2; exit 87; +} +cleanup_guard_bootstrap() { + rm -f -- "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" + rmdir -- "$GUARD_BOOTSTRAP_DIR" +} +trap cleanup_guard_bootstrap EXIT +install -m 0500 -- "$SRC/system/upgrade_guard.py" "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" +[ -f "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" ] && +[ ! -L "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" ] && +[ "$(stat -c %u -- "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py")" = 0 ] && +[ "$(sha256sum "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" | cut -d' ' -f1)" = \ + "$(printf '%s' "$GUARD_SHA" | tr '[:upper:]' '[:lower:]')" ] || { + echo 'R779_BOOTSTRAP_COPY_SHA_CHANGED' >&2; exit 87; +} +python3 -B "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" arm --source "$SRC" --uid "$REQ_UID" # R553: Do not touch root-owned binaries/metadata without a separately # activated, root-owned two-sided timed rollback guard. A user-scoped backup # from install.sh is reversible; it is NOT a protected autonomous watchdog. @@ -113,8 +135,9 @@ except Exception as ex: print("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED") PY # Transactional stage + apply obtain native action lock; one rollback slot. -python3 "$SRC/system/protected_upgrade_transaction.py" stage --source "$SRC" -python3 "$SRC/system/protected_upgrade_transaction.py" apply -python3 "$SRC/system/protected_upgrade_transaction.py" status | +GUARD_ROOT_TX="/var/lib/directinternetmethod/upgrade-guard/root_tx.py" +python3 -B "$GUARD_ROOT_TX" stage --source "$SRC" +python3 -B "$GUARD_ROOT_TX" apply +python3 -B "$GUARD_ROOT_TX" status | python3 -c 'import json,sys;j=json.load(sys.stdin);assert j["status"]=="APPLIED" and j["version"]=="1.5.5-rc1"' echo '{"status":"ROOT_R519_APPLIED_UNCOMMITTED","needsRealNativeAcceptance":true}' diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 33384a30..0c32ee12 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4"} +EXPECTED={"version":VERSION,"files":28,"sha256":"04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -142,9 +142,11 @@ def test_root_wrapper_pinned_identity(self): self.assertIn("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED",src) self.assertIn("GUARD_SHA=",src) self.assertLess(src.index("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED"), - src.index('python3 "$SRC/system/protected_upgrade_transaction.py" stage')) - self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" stage',src) - self.assertIn('python3 "$SRC/system/protected_upgrade_transaction.py" apply',src) + src.index('python3 -B "$GUARD_ROOT_TX" stage')) + self.assertIn('python3 -B "$GUARD_ROOT_TX" stage',src) + self.assertIn('python3 -B "$GUARD_ROOT_TX" apply',src) + self.assertIn('R779_BOOTSTRAP_COPY_SHA_CHANGED',src) + self.assertNotIn('python3 "$SRC/system/upgrade_guard.py" arm',src) self.assertNotIn(' commit',src) def test_guard_and_hosted_root_zip_inventory_track_live_manifest(self): # R748 negative-first: source/unit guards must accept the same exact diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 9d6fd075..b4bde797 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="79a8f080bdfd4545fd75ed1eebe5ff588718b4966632a7212861f2b786cff9d4" +CANDIDATE_SHA="04061b34f45c7e667b54764982b60770ab585c78f3f7e12c8fa4410f01eb4386" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 9ea400df..a6ddaf3d 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" +SOURCE_SHA="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index efab2341..0c1385ca 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4" +PIN_PACKAGE="04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386" PIN_ROOT="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" -PIN_GUARD="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" +PIN_GUARD="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 6dc22246..bd11c881 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="9E31EFE3BA078523768206BE8CAEBC7F35DF476FDB7E506F01F08658EE4AFFD9" +PIN_GUARD="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 667213ef..d2f191a6 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="79A8F080BDFD4545FD75ED1EEBE5FF588718B4966632A7212861F2B786CFF9D4" +ARCHIVE="04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) @@ -105,7 +105,7 @@ def main(): "set -euo pipefail\n" "HERE="+subprocess.list2cmdline([str(extracted)])+"\n" "USER_TRANSACTION="+subprocess.list2cmdline([str(user_tx)])+"\n" - "ROOT_TRANSACTION="+subprocess.list2cmdline([str(extracted/"system/protected_upgrade_transaction.py")])+"\n" + "ROOT_TRANSACTION="+subprocess.list2cmdline(["/var/lib/directinternetmethod/upgrade-guard/root_tx.py"])+"\n" "ROOT_UPGRADE_STAGED=0\nTRANSACTION_ACTIVE=1\n" "pkexec() { printf 'R567_POLKIT_DENIED_BY_OWNER\\n' >&2; return 126; }\n" +rollback+"\n\n"+backend+"\n\n" diff --git a/tests/r698_root_watchdog_firstsplit_integrity_contract.py b/tests/r698_root_watchdog_firstsplit_integrity_contract.py index 58a5128e..1a4716de 100644 --- a/tests/r698_root_watchdog_firstsplit_integrity_contract.py +++ b/tests/r698_root_watchdog_firstsplit_integrity_contract.py @@ -67,7 +67,7 @@ def need(ok,reason): need(guard_sha in src(t),"INDEPENDENT_WATCHDOG_CONTRACT_PIN_STALE_"+pathlib.Path(t).stem) need('R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED' in wrapper,"INDEPENDENT_GUARD_REMOVED") need(wrapper.index('R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED')< - wrapper.index('python3 "$SRC/system/protected_upgrade_transaction.py" stage'), + wrapper.index('python3 -B "$GUARD_ROOT_TX" stage'), "ROOT_MUTATES_BEFORE_GUARD_ARMED") need('"$NFQWS" --dry-run "${NFQWS_ARGS[@]}"' in helper,"ENGINE_PARSER_VERIFICATION_REMOVED") diff --git a/tests/r779_root_guard_source_copy_identity_contract.py b/tests/r779_root_guard_source_copy_identity_contract.py new file mode 100644 index 00000000..2443c1be --- /dev/null +++ b/tests/r779_root_guard_source_copy_identity_contract.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +"""R779 Rootless negative-first source-to-root-guard byte identity regression. + +Only disposable user-owned fixture paths. Never calls pkexec, systemctl, Root +CLI, or host networking. The guard functions are imported, not invoked as CLI. +""" +from __future__ import annotations +import hashlib +import importlib.util +import json +import os +import subprocess +import sys +import pathlib +import shutil +import tempfile +import unittest +from unittest.mock import patch + +ROOT=pathlib.Path(__file__).resolve().parents[1] +SRC=ROOT/"linux/system/upgrade_guard.py" +spec=importlib.util.spec_from_file_location("r779_guard",SRC) +g=importlib.util.module_from_spec(spec) +spec.loader.exec_module(g) + +class GuardSourceIdentity(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="r779-owned-",dir=str(ROOT)) + self.base=pathlib.Path(self.tmp.name) + self.src=self.base/"DirectInternetMethod_1.5.5-rc1_Linux_x86_64" + (self.src/"system").mkdir(parents=True) + (self.src/"app").mkdir() + self.root_tx=self.src/"system/protected_upgrade_transaction.py" + self.user_tx=self.src/"app/user_update_transaction.py" + shutil.copyfile(ROOT/"linux/system/protected_upgrade_transaction.py",self.root_tx) + shutil.copyfile(ROOT/"linux/app/user_update_transaction.py",self.user_tx) + self.assertEqual(hashlib.sha256(self.root_tx.read_bytes()).hexdigest().upper(),g.PIN_ROOT) + self.assertEqual(hashlib.sha256(self.user_tx.read_bytes()).hexdigest().upper(),g.PIN_USER) + rows=[{"file":"system/protected_upgrade_transaction.py","sha256":g.PIN_ROOT}, + {"file":"app/user_update_transaction.py","sha256":g.PIN_USER}] + rows.extend({"file":f"unrelated-{i:02d}","sha256":"0"*64} for i in range(26)) + (self.src/"PACKAGE_MANIFEST.json").write_text( + json.dumps({"schema":1,"version":g.VERSION,"files":rows})+"\n") + self.guard_dir=self.base/"guard" + self.home=self.base/"home" + self.home.mkdir() + self.um=self.home/"INSTALL.json" + self.um.write_text('{"version":"1.5.4"}\n') + self.rm=self.base/"root-install.json" + self.rm.write_text('{"version":"1.5.2"}\n') + self.write_calls=[] + + def tearDown(self): + self.tmp.cleanup() + + def fake_write_new(self,path,data,mode): + path=pathlib.Path(path) + path.parent.mkdir(exist_ok=True,parents=True) + self.assertFalse(path.exists(),"No writes to existing fixture files") + path.write_bytes(data) + path.chmod(mode) + self.write_calls.append(str(path)) + + def arm_fixture_without_privileges(self): + with patch.object(g,"GUARD",self.guard_dir), \ + patch.object(g,"ROOT_TX",self.guard_dir/"root_tx.py"), \ + patch.object(g,"USER_TX",self.guard_dir/"user_tx.py"), \ + patch.object(g,"WORKER",self.guard_dir/"worker.py"), \ + patch.object(g,"BASELINE_FILE",self.guard_dir/"baseline.json"), \ + patch.object(g,"write_new",side_effect=self.fake_write_new), \ + patch.object(g.os,"chown",return_value=None): + return g.guard_files(self.src,1000,"fixture",self.home,self.um,self.rm) + + def test_01_correct_pinned_source_can_be_staged(self): + self.assertTrue(g.validate_source(self.src)) + doc=self.arm_fixture_without_privileges() + self.assertEqual(doc["rootTransactionSha"],g.PIN_ROOT) + self.assertEqual((self.guard_dir/"root_tx.py").read_bytes(),self.root_tx.read_bytes()) + self.assertEqual((self.guard_dir/"user_tx.py").read_bytes(),self.user_tx.read_bytes()) + self.assertEqual(len(self.write_calls),4) + + def test_02_changed_root_script_after_validation_never_installed_in_guard(self): + self.assertTrue(g.validate_source(self.src)) + self.root_tx.write_bytes(b"print('UNTRUSTED_ROOT_CODE')\n") + with self.assertRaises(RuntimeError): + self.arm_fixture_without_privileges() + self.assertFalse(self.guard_dir.exists(),"Changed root source must fail before guard creation") + + def test_03_changed_user_script_after_validation_never_installed_in_guard(self): + self.assertTrue(g.validate_source(self.src)) + self.user_tx.write_bytes(b"print('UNTRUSTED_USER_CODE')\n") + with self.assertRaises(RuntimeError): + self.arm_fixture_without_privileges() + self.assertFalse(self.guard_dir.exists(),"Changed user source must fail before guard creation") + + def test_04_fifo_source_never_blocks_root_before_sha_check(self): + self.root_tx.unlink() + os.mkfifo(self.root_tx,0o600) + code=""" +import importlib.util,sys +spec=importlib.util.spec_from_file_location("r779_child",sys.argv[1]) +m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m) +try: + m.pinned_package_bytes(__import__("pathlib").Path(sys.argv[2]), + "system/protected_upgrade_transaction.py",m.PIN_ROOT) +except RuntimeError: + sys.exit(0) +sys.exit(44) +""" + child=subprocess.run( + [sys.executable,"-B","-c",code,str(SRC),str(self.src)], + capture_output=True,text=True,timeout=3) + self.assertEqual(child.returncode,0,(child.stdout,child.stderr)) + + def test_05_parent_directory_symlink_cannot_redirect_pinned_source(self): + alias=self.base/"source-alias" + alias.symlink_to(self.src,target_is_directory=True) + with self.assertRaisesRegex(RuntimeError,"PINNED_SOURCE_OPEN"): + g.pinned_package_bytes(alias,"system/protected_upgrade_transaction.py",g.PIN_ROOT) + + def test_06_privileged_wrapper_executes_verified_root_owned_code_only(self): + script=(ROOT/"linux/system/upgrade_system.sh").read_text() + self.assertNotIn('python3 "$SRC/system/upgrade_guard.py" arm',script) + self.assertNotIn('python3 "$SRC/system/protected_upgrade_transaction.py" stage',script) + self.assertIn('mktemp -d /run/directinternetmethod-guard-XXXXXXXX',script) + self.assertIn('install -m 0500 -- "$SRC/system/upgrade_guard.py"',script) + self.assertIn('sha256sum "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py"',script) + launch='python3 -B "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py" arm' + self.assertIn(launch,script) + self.assertLess(script.index('sha256sum "$GUARD_BOOTSTRAP_DIR/upgrade_guard.py"'), + script.index(launch)) + self.assertIn('GUARD_ROOT_TX="/var/lib/directinternetmethod/upgrade-guard/root_tx.py"',script) + for verb in ("stage","apply","status"): + self.assertIn('python3 -B "$GUARD_ROOT_TX" '+verb,script) + + def test_07_installer_rollback_uses_immutable_owned_root_transaction(self): + src=(ROOT/"linux/install.sh").read_text() + self.assertNotIn('ROOT_TRANSACTION="$HERE/system/protected_upgrade_transaction.py"',src) + self.assertIn('ROOT_TRANSACTION="/var/lib/directinternetmethod/upgrade-guard/root_tx.py"',src) + self.assertIn('pkexec /usr/bin/python3 "$ROOT_TRANSACTION" rollback',src) + +if __name__=="__main__": + unittest.main(verbosity=2) From 1fff246d5892b2b88ccf52b08b2f0a8a8986186a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:20:16 +0330 Subject: [PATCH 156/188] fix(linux): add fixed Root-owned bootstrap and exact manifest admission (R781) --- .github/workflows/ci.yml | 21 +- docs/SECURITY_TRUSTED_ENTRY_R781.md | 42 +++ linux/install.sh | 19 +- tests/linux_contract_audit.py | 37 ++- tests/r519_versioned_package_contract.py | 7 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 11 +- ...09_installer_no_bytecode_cache_contract.py | 3 +- tests/r781_privileged_entry_contract.py | 173 ++++++++++++ tools/security/privileged_entry.py | 255 ++++++++++++++++++ 11 files changed, 542 insertions(+), 30 deletions(-) create mode 100644 docs/SECURITY_TRUSTED_ENTRY_R781.md create mode 100644 tests/r781_privileged_entry_contract.py create mode 100644 tools/security/privileged_entry.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8494617..1238f8c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386" - assert p.is_file() and p.stat().st_size==8419073 + expected="15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" + assert p.is_file() and p.stat().st_size==8419883 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -89,6 +89,7 @@ jobs: python3 -B tests/r776_checked_fd_swap_regression.py python3 -B tests/r778_stage_backup_snapshot_race_contract.py python3 -B tests/r779_root_guard_source_copy_identity_contract.py + python3 -B tests/r781_privileged_entry_contract.py python3 -B tests/encrypted_selective_dns_fake_lifecycle.py python3 -B tests/encrypted_selective_dns_gui_consent_contract.py python3 -B tests/r537_gui_consent_status_contract.py @@ -176,7 +177,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -205,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -238,7 +239,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -267,7 +268,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -296,7 +297,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -326,7 +327,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -371,7 +372,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -397,7 +398,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md new file mode 100644 index 00000000..d6cf4ea2 --- /dev/null +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -0,0 +1,42 @@ +# R781 — trusted Linux Root entrypoint (unreleased, review-only) + +## Purpose and authority + +The existing `pkexec /bin/bash "$HERE/system/upgrade_system.sh"` launched shell code directly from the owner's extracted package. Native Polkit confirmation authenticates the invoking user but does not attest this script. R781 replaces that initial privileged entry with a fixed administrator-provisioned program: + +`/usr/local/libexec/directinternetmethod/privileged_entry.py` + +This directory is deliberately **outside** the primary `/usr/lib/directinternetmethod` backend, so pre-provisioning does not accidentally make a truly fresh installation look like a partial backend. It is one trusted component of the same product, **not** a second full installation, a privileged wildcard broker, or permission to disable authentication. + +The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: + +`05B8AA5452CE0316CE4E815F268E06E223E3EAF7C825B27319268C9303E32620` + +It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: + +`07027AC1EDAA023B029CA772A5EC8BAA0AD53DB1072E3895A59B5E14F67349C8` + +The built Linux ZIP is **UNRELEASED**: SHA-256 `15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3` (8,419,883 bytes, 28 payload members). + +## Exact sequence and guardrails + +1. Independently review the exact source commit, its GitHub history and privilege boundary; obtain the trusted bootstrap from that immutable reviewed source. This is **not yet approved for installation**. +2. A legitimate interactive OS administrator, not this unprivileged installer, approves provisioning a new **Root-owned** file. Verify the source's SHA-256 independently; refuse existing foreign/symlink target. Create the dedicated root-owned ancestor with group/world nonwritable permissions, copy as root mode `0500`, then recompute and compare the installed file's SHA-256 before running any upgrade. Do not execute the package as Root to provision it. +3. User-side `linux/install.sh` verifies the trusted path's owner/group/mode/type/link count. If missing, fail closed, roll back its staged **user** transaction, and clearly request the separate OS-admin action. Never fall back to user-owned Bash. +4. Polkit authorizes `/usr/bin/python3 -I` on the fixed root-owned script with explicit `--action upgrade|install`, `--source`. The script demands actual `PKEXEC_UID`, verifies itself and all pathname ancestors as root-owned, and rejects non-root/caller identity mismatch. +5. Before ANY contained script runs under Root, the bootstrap verifies a fixed manifest hash, exactly 28 canonical allowlisted payloads, checks file types, sizes, symlinks/hardlinks, reads source bytes using a single pinned descriptor with no symlink traversal, and validates each member SHA. It freezes the entire accepted tree under a temporary `/run` directory owned by Root, mode `0700`. +6. Run only the copied, SHA-attested package script under a minimal sanitized environment. The existing independent, root-owned watchdog still protects protected `stage/apply`. The bootstrap removes only its isolated ephemeral copy. Any failure must leave user-owned configuration intact and must not hide an incomplete privileged transaction. + +## Security boundaries / remaining checks + +This SHA-pinned release-specific approach intentionally requires **another explicit administrator review and trusted-anchor update** for a future package with a different manifest. The project does not require arbitrary publisher digital signatures or silently trust an unverified GitHub release. + +R781 rootless tests only exercise preflight, source freezing, rejection and code provenance. They do **not** prove protected install, timer/rollback, UAC/Polkit consent, live TLS routing or product-wide security. Native ephemeral hosted Root tests, independent review of PR #62/#63, expert adjudication of all CodeQL High findings and owner-authorized physical acceptance are still separate gates. No trusted entry has been installed on either physical Linux host. + +## Revocation and recovery + +The installed file remains an explicit OS-admin-owned asset; no self-updater may overwrite it. If its installed checksum/ownership drifts or the corresponding package manifest changes, reject the privileged attempt. An administrator may disable/revoke this isolated entrypoint only after checking for active upgrade/rollback guard state and preserving the accepted installed backend and user settings. Do not remove any persistent watchdog or pending rollback evidence as part of bootstrap cache cleanup. + +## Provenance + +Original review-only source parent: `c31783167ce22be2c30e379d2a99650056601f5a` (PR #63). New test: `tests/r781_privileged_entry_contract.py`. Scope: source/prototype until native hosted and physical, approved privileged acceptance is demonstrated. diff --git a/linux/install.sh b/linux/install.sh index 89c5bd4a..4a85b12a 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -101,17 +101,32 @@ $HERE/licenses/LICENSE-zapret.txt|/usr/lib/directinternetmethod/licenses/LICENSE EOF } +# R781: A privileged entry must be an OS-provisioned, Root-owned fixed script. +# Neither Root Bash nor Python may execute source from this user-owned package. +TRUSTED_BOOTSTRAP="/usr/local/libexec/directinternetmethod/privileged_entry.py" +trusted_bootstrap_ready() { + [ -f "$TRUSTED_BOOTSTRAP" ] && + [ ! -L "$TRUSTED_BOOTSTRAP" ] && + [ "$(stat -c %u -- "$TRUSTED_BOOTSTRAP")" = 0 ] && + [ "$(stat -c %g -- "$TRUSTED_BOOTSTRAP")" = 0 ] && + [ "$(stat -c %a -- "$TRUSTED_BOOTSTRAP")" = 500 ] && + [ "$(stat -c %h -- "$TRUSTED_BOOTSTRAP")" = 1 ] +} # Administrative authorization is required only when the protected backend is absent or differs. if backend_matches_current; then echo "Protected backend already matches current package; keeping root-owned code unchanged." else + if ! trusted_bootstrap_ready; then + echo "R781_TRUSTED_BOOTSTRAP_NOT_PROVISIONED: OS administrator must approve an independently verified, Root-owned bootstrap. No package code ran as Root." >&2 + rollback_failed_install 87 + fi if [ -f /var/lib/directinternetmethod/install.json ] && [ -d /usr/lib/directinternetmethod ]; then ROOT_UPGRADE_STAGED=1 - pkexec /bin/bash "$HERE/system/upgrade_system.sh" "$HERE" "$(id -u)" + pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action upgrade --source "$HERE" elif [ ! -e /var/lib/directinternetmethod/install.json ] && [ ! -e /usr/lib/directinternetmethod ]; then - pkexec /bin/bash "$HERE/system/install_system.sh" "$HERE" "$(id -u)" + pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action install --source "$HERE" else echo 'Foreign/partial root backend; no mutation authorized.' >&2 rollback_failed_install 25 diff --git a/tests/linux_contract_audit.py b/tests/linux_contract_audit.py index c1e2dfec..70aa5d41 100644 --- a/tests/linux_contract_audit.py +++ b/tests/linux_contract_audit.py @@ -21,7 +21,12 @@ def check(name,cond): D["checks"][name]=bool(cond) if not cond:D["status"]="FAIL" -check("version_154_backend_compatible", 'VERSION="1.5.4"' in ui and '"version":"1.5.4"' in install and '"version":"1.5.4"' in sysinstall and 'd.get("version") in ("1.5.1","1.5.2","1.5.3","1.5.4")' in install) +check("version_155rc1_exact_update_with_accepted_stable_baseline", + 'VERSION="1.5.5-rc1"' in ui and + '"version":"1.5.5-rc1"' in sysinstall and + 'd.get("version")=="1.5.5-rc1"' in install and + 'CURRENT_BACKEND_VERSION="1.5.2"' in txt("system/protected_upgrade_transaction.py") and + 'OLD_USER="1.5.4"' in txt("system/upgrade_guard.py")) check("no_default_route_mutation", not re.search(r'ip\s+route\s+(add|del|replace).*default|nmcli\s+.*ipv4\.gateway',helper,re.I)) check("no_unconditional_physical_dns_mutation", "nmcli connection modify" not in helper and "resolvectl dns enp" not in helper and "resolvectl dns eth" not in helper and @@ -110,8 +115,11 @@ def check(name,cond): check("ui_live_verify_rollback", 'LIVE_VERIFY_FAILED_ROLLED_BACK' in ui and 'run_system_action("stop")' in ui) check("ui_fixed_systemd_actions", 'unit=f"directinternetmethod-{action}.service"' in ui and 'subprocess.run(["systemctl","start",unit]' in ui) check("ui_multiprotocol_label", "Encrypted DNS (DoH) + HTTP/TLS/QUIC DPI bypass" in ui) -check("ui_online_update_hash_gate", "SHA256SUMS.txt" in ui and "hashlib.sha256" in ui and "Unsafe update archive path." in ui) -check("ui_online_update_three_way_digest_gate", all(x in ui for x in ("GitHub release asset digest is missing/invalid","SHA256SUMS.txt must contain exactly one valid checksum","Linux update checksum disagrees with GitHub asset digest","three-way SHA-256 verification","expected_linux=f\"DirectInternetMethod_{release_version}_Linux_x86_64.zip\""))) +check("ui_online_update_hash_gate", all(x in ui for x in ( + "SHA256SUMS.txt", "hashlib.sha256", "Unsafe Linux update release version.", + "Update cache path contains a symlink.", + "Update cache is not owned by the current user."))) +check("ui_online_update_three_way_digest_gate", all(x in ui for x in ("GitHub release asset digest is missing/invalid","SHA256SUMS.txt must contain exactly one Linux update checksum","Linux update checksum disagrees with GitHub asset digest","three-way SHA-256 verification","expected_linux=f\"DirectInternetMethod_{release_version}_Linux_x86_64.zip\""))) check("target_hostlist_scope", "^gemini.google.com" in hostlist.splitlines() and all(x not in hostlist.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xnxx.com","xhamster.com")) and all(x in adult_fallback.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xvideos-cdn.com","xvcdn.com","xnxx.com","xnxx-cdn.com","xhamster.com","xhcdn.com","redtube.com","rdtcdn.com"))) check("critical_global_services_hostlist", all(x in hostlist.splitlines() for x in ("openai.com","chatgpt.com","oaistatic.com","oaiusercontent.com","github.com","githubusercontent.com","githubassets.com","reddit.com","redditstatic.com","redditmedia.com","redd.it"))) check("custom_site_hostlist", all(x in helper for x in ('CUSTOM_HOSTS="$STATE_BASE/custom-hosts.txt"','USER_HOSTLIST_SYMLINK_REJECTED','python3 - "$HOSTS" "$CUSTOM_HOSTS" "$ADULT_FALLBACK" "$ADULT_HOSTS" "$ADULT_ON" "$RUN_HOSTS"','"custom-hostlist"')) and all(x in ui for x in ('CUSTOM_HOSTS=APP_HOME/"custom-hosts.txt"','normalize_custom_site','save_custom_hosts_text','("Custom Sites","custom",None)','Maximum 256 entries'))) @@ -142,15 +150,24 @@ def check(name,cond): and "PROVIDER_PACKAGE_DIR_UNSAFE" in transaction and '"router_gateway/providers.json"' in build) check("linux_package_includes_router_core", '("linux/app/router_gateway_core.py","app/router_gateway_core.py")' in build) -check("install_one_time_admin", 'pkexec /bin/bash "$HERE/system/install_system.sh"' in install) +check("install_one_time_admin", 'pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action install' in install) +check("install_root_trusted_entry_only", 'pkexec /bin/bash "$HERE/system/' not in install and + 'R781_TRUSTED_BOOTSTRAP_NOT_PROVISIONED' in install) check("install_idempotent_backend_hash_gate", "backend_matches_current()" in install and 'sha256sum "$src"' in install and "keeping root-owned code unchanged" in install) check("installed_root_backend_mismatch_triggers_explicit_rollback_and_no_root_overwrite", - "Protected backend differs; update refused until root rollback is verified" in install - and "rollback_failed_install 25" in install - and "rollback_failed_install 22" in install - and "rollback_failed_install 24" in install - and "if [ -e /var/lib/directinternetmethod/install.json ]" in install) -check("linux_hash_compatible_prior_backend_no_repeat_admin", 'd.get("version") in ("1.5.1","1.5.2","1.5.3","1.5.4")' in install and 'sha256sum "$src"' in install and 'if backend_matches_current; then' in install and 'keeping root-owned code unchanged' in install) + all(x in install for x in ( + "rollback_failed_install 25", "rollback_failed_install 22", + "rollback_failed_install 24", "rollback_failed_install 87", + "ROOT_UPGRADE_STAGED=1", "backend_matches_current ||", + "if ! trusted_bootstrap_ready; then", + 'pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action upgrade', + "Foreign/partial root backend; no mutation authorized."))) +check("linux_exact_current_backend_no_repeat_admin", + 'd.get("version")=="1.5.5-rc1"' in install and + 'sha256sum "$src"' in install and + 'if backend_matches_current; then' in install and + 'keeping root-owned code unchanged' in install and + 'pkexec /bin/bash "$HERE/system/' not in install) check("linux_install_reports_backend_version_authoritatively", 'protectedBackendVersion' in install and 'protectedBackendByteCompatible' in install and '/var/lib/directinternetmethod/install.json' in install) check("linux_lifecycle_acceptance_old_backend_sha_gated", "COMPATIBLE_OLD_BACKEND_HASH_MISMATCH" in (R/"tests"/"linux_installed_152_lifecycle.py").read_text(encoding="utf-8") and "compatibleBackendHashChecks" in (R/"tests"/"linux_installed_152_lifecycle.py").read_text(encoding="utf-8")) check("linux_fresh_install_parent_permissions", 'install -d -m 0755 "$ROOT" "$ROOT/runtime" "$ROOT/runtime/usr" "$ROOT/runtime/usr/bin" "$ROOT/licenses"' in sysinstall and 'install -d -m 0755 /var/lib/directinternetmethod' in sysinstall and 'mkdir -p /var/lib/directinternetmethod' not in sysinstall) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 0c32ee12..0cdc5f40 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386"} +EXPECTED={"version":VERSION,"files":28,"sha256":"15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -190,7 +190,10 @@ def test_user_installer_never_pretends_committed(self): src=INSTALLER.read_text() self.assertIn("ROOT_UPGRADE_STAGED=1",src) self.assertIn('R519_CANDIDATE_PENDING_NATIVE_ROOT_USER_COMMIT',src) - self.assertIn('pkexec /bin/bash "$HERE/system/upgrade_system.sh"',src) + self.assertIn('pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action upgrade',src) + self.assertIn('pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action install',src) + self.assertNotIn('pkexec /bin/bash "$HERE/system/',src) + self.assertIn('R781_TRUSTED_BOOTSTRAP_NOT_PROVISIONED',src) self.assertIn('python3 "$USER_TRANSACTION" --rollback',src) def test_gui_and_user_metadata_pins(self): app=GUI.read_text() diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index b4bde797..348eeea2 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="04061b34f45c7e667b54764982b60770ab585c78f3f7e12c8fa4410f01eb4386" +CANDIDATE_SHA="15a88bcf4f1d8dcec34b62d662041750dfc2a3accc1f1efd554d92e0d6bec1e3" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 0c1385ca..dec84e88 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,7 +29,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386" +PIN_PACKAGE="15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" PIN_ROOT="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" PIN_GUARD="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index d2f191a6..3f617a18 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="04061B34F45C7E667B54764982B60770AB585C78F3F7E12C8FA4410F01EB4386" +ARCHIVE="15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) @@ -87,8 +87,9 @@ def main(): "\n\n# Administrative authorization is required only") auth=excerpt(source,"# Administrative authorization is required only", "\nbackend_matches_current || ") - require(auth.count("pkexec /bin/bash")==2 and - 'ROOT_UPGRADE_STAGED=1' in auth,"POLKIT_BRANCH_PROVENANCE") + require(auth.count('pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP"')==2 + and 'ROOT_UPGRADE_STAGED=1' in auth + and 'pkexec /bin/bash' not in auth,"POLKIT_BRANCH_PROVENANCE") require("pkexec /usr/bin/python3" in rollback and 'if [ "$ROOT_UPGRADE_STAGED" -eq 1 ]' in rollback, "ROLLBACK_PROVENANCE") @@ -107,6 +108,10 @@ def main(): "USER_TRANSACTION="+subprocess.list2cmdline([str(user_tx)])+"\n" "ROOT_TRANSACTION="+subprocess.list2cmdline(["/var/lib/directinternetmethod/upgrade-guard/root_tx.py"])+"\n" "ROOT_UPGRADE_STAGED=0\nTRANSACTION_ACTIVE=1\n" + # Scope R567 is auth DENIED *after* an independently installed + # bootstrap was validated. R781 tests separate missing-bootstrap case. + + "trusted_bootstrap_ready() { return 0; }\n" + + "TRUSTED_BOOTSTRAP=/usr/local/libexec/directinternetmethod/privileged_entry.py\n" "pkexec() { printf 'R567_POLKIT_DENIED_BY_OWNER\\n' >&2; return 126; }\n" +rollback+"\n\n"+backend+"\n\n" "trap 'rollback_failed_install \"$?\"' ERR\n" diff --git a/tests/r709_installer_no_bytecode_cache_contract.py b/tests/r709_installer_no_bytecode_cache_contract.py index b17984ff..226605b4 100644 --- a/tests/r709_installer_no_bytecode_cache_contract.py +++ b/tests/r709_installer_no_bytecode_cache_contract.py @@ -18,7 +18,8 @@ prefix=installer.split('HERE="$(cd "$(dirname "$0")" && pwd)"',1) assert len(prefix)==2,"INSTALLER_SOURCE_PREFIX_CHANGED" assert needle in prefix[0] and prefix[0].index(needle)>prefix[0].index("umask 077") -assert 'pkexec /bin/bash "$HERE/system/upgrade_system.sh"' in installer,"PROTECTED_ROOT_AUTH_REMOVED" +assert 'pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP" --action upgrade' in installer,"PROTECTED_ROOT_AUTH_REMOVED" +assert 'pkexec /bin/bash "$HERE/system/' not in installer,"ROOT_ENTRYPOINT_MUST_NOT_BE_USER_WRITABLE" assert 'R519_CANDIDATE_PENDING_NATIVE_ROOT_USER_COMMIT' in installer,"UNCOMMITTED_SAFETY_SEMANTICS_CHANGED" assert 'python3 "$USER_TRANSACTION" --rollback' in installer,"USER_ROLLBACK_REMOVED" if sys.platform.startswith("linux"): diff --git a/tests/r781_privileged_entry_contract.py b/tests/r781_privileged_entry_contract.py new file mode 100644 index 00000000..22cf7bdf --- /dev/null +++ b/tests/r781_privileged_entry_contract.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""R781 rootless negative-first trust bootstrap contracts; no Root or GUI use.""" +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest +from unittest.mock import patch +import zipfile + +ROOT=Path(__file__).resolve().parents[1] +SRC=ROOT/"tools/security/privileged_entry.py" +SPEC=importlib.util.spec_from_file_location("dim_r781",SRC) +m=importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(m) +PKG="DirectInternetMethod_1.5.5-rc1_Linux_x86_64" + +class RootlessTrustedEntry(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="r781-only-user-",dir=str(ROOT)) + self.base=Path(self.tmp.name) + self.src=self.base/"source"/PKG + self.src.parent.mkdir() + with zipfile.ZipFile(ROOT/"delivery"/(PKG+".zip")) as z: + names=z.namelist() + self.assertEqual(len(names),30) + for name in names: + self.assertTrue(name.startswith(PKG+"/")) + self.assertNotIn("..",Path(name).parts) + z.extractall(self.src.parent) + self.private=self.base/"private" + self.private.mkdir(mode=0o700) + self.user=os.geteuid() + self.assertNotEqual(self.user,0,"Do not run this rootless suite as Root.") + + def tearDown(self): + self.tmp.cleanup() + + def candidate(self): + return m.freeze_package(self.src,self.private,self.user) + + def test_01_valid_unchanged_exact_manifest(self): + fd=m.open_pinned_directory(self.src) + try: + data, rows=m.package_inventory(fd) + self.assertEqual(m.sha(data),m.MANIFEST_SHA256) + self.assertEqual(len(rows),28) + finally: + os.close(fd) + + def test_02_integrity_frozen_canonical_archive(self): + actual=self.candidate() + self.assertEqual(actual.name,PKG) + self.assertTrue(actual.is_dir()) + self.assertEqual(actual.stat().st_mode&0o777,0o500) + for path in ("system/upgrade_system.sh","system/install_system.sh", + "system/upgrade_guard.py","system/protected_upgrade_transaction.py", + "app/user_update_transaction.py","runtime/ctrld"): + before=self.src/path + after=actual/path + self.assertEqual(hashlib.sha256(before.read_bytes()).hexdigest(), + hashlib.sha256(after.read_bytes()).hexdigest()) + self.assertEqual(len([x for x in actual.rglob("*") if x.is_file()]),30) + + def test_03_modified_manifest_rejected(self): + p=self.src/"PACKAGE_MANIFEST.json" + p.write_bytes(p.read_bytes()+b" ") + with self.assertRaisesRegex(m.AdmissionError,"TRUSTED_MANIFEST_DIGEST"): + self.candidate() + self.assertFalse((self.private/PKG).exists()) + + def test_04_modified_payload_does_not_stage_valid_version(self): + p=self.src/"system/upgrade_system.sh" + p.write_bytes(b"print('untrusted')\n") + with self.assertRaisesRegex(m.AdmissionError,"PINNED_PAYLOAD_SHA"): + self.candidate() + + def test_05_manifest_duplicate_key_rejected(self): + with self.assertRaisesRegex(m.AdmissionError,"DUPLICATE_JSON_FIELD"): + m.strict_json(b'{"schema":1,"schema":1}') + + def test_06_linked_parent_is_not_followed(self): + alias=self.base/"bad_symlink" + alias.symlink_to(self.src.parent,target_is_directory=True) + with self.assertRaisesRegex(m.AdmissionError,"SOURCE_DIR_NOFOLLOW"): + m.freeze_package(alias/PKG,self.private,self.user) + + def test_07_payload_symlink_never_followed(self): + p=self.src/"system/upgrade_system.sh" + p.unlink();p.symlink_to(self.src/"system/install_system.sh") + with self.assertRaisesRegex(m.AdmissionError,"MEMBER_FILE_NOFOLLOW"): + self.candidate() + + def test_08_payload_fifo_never_blocks(self): + p=self.src/"system/upgrade_system.sh" + p.unlink();os.mkfifo(p,0o600) + program=""" +import importlib.util,sys,os +sp=importlib.util.spec_from_file_location("tested",sys.argv[1]) +m=importlib.util.module_from_spec(sp);sp.loader.exec_module(m) +fd=m.open_pinned_directory(__import__('pathlib').Path(sys.argv[2])) +try: + m.read_pinned(fd,"system/upgrade_system.sh",100000) +except m.AdmissionError: + sys.exit(0) +finally: + os.close(fd) +sys.exit(99) +""" + r=subprocess.run([sys.executable,"-B","-c",program,str(SRC),str(self.src)], + capture_output=True,text=True,timeout=4) + self.assertEqual(r.returncode,0,r.stderr) + + def test_09_midstage_replacement_detected(self): + original=m.package_inventory + p=self.src/"system/upgrade_system.sh" + def swap(fd): + info=original(fd) + p.rename(p.with_name(p.name+".pinned")) + p.symlink_to(self.src/"system/install_system.sh") + return info + with patch.object(m,"package_inventory",side_effect=swap): + with self.assertRaisesRegex(m.AdmissionError,"MEMBER_FILE_NOFOLLOW"): + self.candidate() + + def test_10_untrusted_cli_without_root_refuses(self): + r=subprocess.run([sys.executable,"-B",str(SRC),"--action","upgrade", + "--source",str(self.src)], + capture_output=True,text=True,timeout=4) + self.assertEqual(r.returncode,86,(r.stdout,r.stderr)) + self.assertIn("ROOT_REQUIRED",r.stderr) + self.assertFalse((self.private/PKG).exists()) + + def test_12_no_pkexec_user_owned_code_and_missing_anchor_fails_closed(self): + installer=(ROOT/"linux/install.sh").read_text() + self.assertNotIn('pkexec /bin/bash "$HERE/system/',installer) + self.assertEqual(installer.count( + 'pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP"'),2) + self.assertIn('R781_TRUSTED_BOOTSTRAP_NOT_PROVISIONED',installer) + self.assertIn('trusted_bootstrap_ready()',installer) + self.assertIn('rollback_failed_install 87',installer) + self.assertIn('TRUSTED_BOOTSTRAP="/usr/local/libexec/directinternetmethod/privileged_entry.py"',installer) + + def test_14_fresh_install_anchor_does_not_create_primary_backend(self): + installer=(ROOT/"linux/install.sh").read_text() + anchor="/usr/local/libexec/directinternetmethod/privileged_entry.py" + self.assertIn('TRUSTED_BOOTSTRAP="'+anchor+'"',installer) + self.assertNotIn('TRUSTED_BOOTSTRAP="/usr/lib/directinternetmethod/',installer) + self.assertNotIn("pkexec /bin/bash",installer) + self.assertIn('[ ! -e /usr/lib/directinternetmethod ]',installer) + self.assertIn("--action install --source",installer) + self.assertIn("--action upgrade --source",installer) + helper=(ROOT/"tools/security/privileged_entry.py").read_text() + self.assertIn('TRUSTED_ENTRY = Path("'+anchor+'")',helper) + self.assertIn('Path("/usr/local/libexec/directinternetmethod")',helper) + + def test_13_missing_root_provisioning_is_not_emulated(self): + # A root-owned helper may legitimately be present on a CI host. + # This user-only test verifies non-root invocation fails regardless. + with self.assertRaisesRegex(m.AdmissionError,"ROOT_REQUIRED"): + m.check_trusted_entry() + + def test_11_source_from_other_uid_rejected(self): + with self.assertRaisesRegex(m.AdmissionError,"PACKAGE_SOURCE_OWNER"): + m.freeze_package(self.src,self.private,self.user+1) + +if __name__=="__main__": + unittest.main(verbosity=2) diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py new file mode 100644 index 00000000..691b0ad2 --- /dev/null +++ b/tools/security/privileged_entry.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +"""Review-only R781 trusted Linux Root entry (NOT installed or authorized). + +An OS administrator must first provision this exact, audited source at +/usr/local/libexec/directinternetmethod/privileged_entry.py as a root-owned 0500 file. +Never call pkexec on package-provided bash/python. No implicit sudo policy. + +One frozen manifest SHA is an *authority from this root-owned file*, not +from untrusted argv or the downloaded package. A later release requires a +separately reviewed trust-anchor update; this is not a generic updater. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +import pwd +import shutil +import stat +import subprocess +import sys +import tempfile + +VERSION = "1.5.5-rc1" +PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" +MANIFEST_SHA256 = "07027AC1EDAA023B029CA772A5EC8BAA0AD53DB1072E3895A59B5E14F67349C8" +TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") +MAX_ITEM = 16 * 1024 * 1024 +MAX_ALL = 48 * 1024 * 1024 +DIR_FLAGS = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC +FILE_FLAGS = os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC | os.O_NONBLOCK + +class AdmissionError(RuntimeError): + pass + +def require(value:bool, code:str)->None: + if not value: + raise AdmissionError("R781_REJECT_"+code) + +def sha(data:bytes)->str: + return hashlib.sha256(data).hexdigest().upper() + +def _object_no_duplicates(pairs): + result = {} + for key,value in pairs: + require(key not in result,"DUPLICATE_JSON_FIELD") + result[key] = value + return result + +def strict_json(data:bytes): + return json.loads(data.decode("utf-8"), object_pairs_hook=_object_no_duplicates) + +def checked_parts(path:Path)->tuple[str,...]: + require(path.is_absolute(),"SOURCE_MUST_BE_ABSOLUTE") + parts = path.parts[1:] + require(all(x not in ("", ".", "..") for x in parts) and len(parts)>0, + "SOURCE_PATH_COMPONENTS") + return parts + +def open_pinned_directory(path:Path)->int: + parts=checked_parts(path) + parent=os.open("/",DIR_FLAGS) + try: + for comp in parts: + try: + child=os.open(comp,DIR_FLAGS,dir_fd=parent) + except OSError as exc: + raise AdmissionError("R781_REJECT_SOURCE_DIR_NOFOLLOW") from exc + os.close(parent) + parent=child + return parent + except BaseException: + os.close(parent) + raise + +def read_pinned(root_fd:int,relative:str,max_bytes:int)->bytes: + entry=PurePosixPath(relative) + pieces=entry.parts + require(0=0:os.close(fd) + +def freeze_package(source:Path,staging_root:Path,uid:int)->Path: + """Pure source->private copy, isolated user-local in tests, root-only in CLI.""" + source=Path(source) + require(source.name==PACKAGE and type(uid) is int and 0 Date: Mon, 19 Oct 2026 15:25:03 +0330 Subject: [PATCH 157/188] test(linux): preserve denied-Polkit fixture after trusted bootstrap split (R781) --- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 3f617a18..a27e7595 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -84,7 +84,7 @@ def main(): "INSTALLER_IDENTITY") rollback=excerpt(source,"rollback_failed_install() {","\n\nfor c in ") backend=excerpt(source,"backend_matches_current() {", - "\n\n# Administrative authorization is required only") + "\n\n# R781: A privileged entry must be") auth=excerpt(source,"# Administrative authorization is required only", "\nbackend_matches_current || ") require(auth.count('pkexec /usr/bin/python3 -I "$TRUSTED_BOOTSTRAP"')==2 From a18518688e96e06b551806b7e9752dfd8ac87af6 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:46:40 +0330 Subject: [PATCH 158/188] test(linux): verify trusted Root bootstrap on ephemeral GitHub VM (R782) --- .github/workflows/ci.yml | 26 +++ tests/r782_hosted_trusted_entry_ephemeral.py | 180 +++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 tests/r782_hosted_trusted_entry_ephemeral.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1238f8c8..810c5b67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -630,6 +630,32 @@ jobs: if-no-files-found: warn retention-days: 14 + linux-r782-root-owned-bootstrap-ephemeral: + name: linux-r782-root-owned-bootstrap-ephemeral + # Actual UID 0 on a fresh original-repository GitHub VM ONLY. + # Never run this job on a user's machine, a fork, or a tag release. + if: github.event_name == 'workflow_dispatch' && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/security/r776-pinned-root-read-qa' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + actions: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Rebuild frozen candidate and verify trusted manifest source bytes + shell: bash + run: | + set -euo pipefail + python3 -B build_linux_package.py + printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '05B8AA5452CE0316CE4E815F268E06E223E3EAF7C825B27319268C9303E32620' 'tools/security/privileged_entry.py' | sha256sum --check --status + - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed + shell: bash + run: | + set -euo pipefail + # Test only; PKEXEC_UID is simulated, and no product install runs. + sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" GITHUB_REF="$GITHUB_REF" GITHUB_SHA="$GITHUB_SHA" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r782_hosted_trusted_entry_ephemeral.py + windows-release-contract: name: windows-release-contract-unsigned if: startsWith(github.ref, 'refs/tags/v') diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py new file mode 100644 index 00000000..7e9f829b --- /dev/null +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +"""R782 real-UID0/bootstrap content test on disposable original GitHub runner ONLY. + +Never execute product install/upgrade/rollback, pkexec, systemctl, DNS or network +commands. PKEXEC_UID is simulated solely for ownership validation: this is +NOT native Polkit authentication acceptance. +""" +from __future__ import annotations +import hashlib +import importlib.util +import os +from pathlib import Path +import pwd +import shutil +import stat +import subprocess +import sys +import zipfile + +BASE = Path(__file__).resolve().parents[1] +PIN_ENTRY = "05B8AA5452CE0316CE4E815F268E06E223E3EAF7C825B27319268C9303E32620" +PIN_ZIP = "15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" +ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") +SOURCE = BASE / "tools/security/privileged_entry.py" +PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" +ARCHIVE = BASE / "delivery" / (PKG + ".zip") + +def need(yes: bool, code: str): + if not yes: + raise RuntimeError("R782_FAIL_CLOSED_" + code) + +def sha_file(p: Path): + return hashlib.sha256(p.read_bytes()).hexdigest().upper() + +def guard(): + e = os.environ + need(sys.platform == "linux" and os.geteuid() == 0, "REAL_ROOT_REQUIRED") + need(e.get("CI") == "true" and e.get("GITHUB_ACTIONS") == "true" and + e.get("GITHUB_REPOSITORY") == "GOD13emad/DirectInternetMethod" and + e.get("RUNNER_ENVIRONMENT") == "github-hosted", "ORIGINAL_HOSTED_ONLY") + need(e.get("GITHUB_REF") == "refs/heads/security/r776-pinned-root-read-qa" and + e.get("GITHUB_SHA") and len(e["GITHUB_SHA"]) == 40, + "REVIEW_ONLY_BRANCH_AND_EXACT_COMMIT") + work = e.get("GITHUB_WORKSPACE", "") + need(work and Path(work).resolve() == BASE.resolve(), "WORKSPACE_NOT_OWNED") + commit = subprocess.run(["/usr/bin/git", "-C", str(BASE), "rev-parse", "HEAD"], + check=True, capture_output=True, text=True).stdout.strip() + need(commit == e["GITHUB_SHA"], "CHECKED_OUT_COMMIT_DIFFERENT") + temp = Path(e.get("RUNNER_TEMP", "")) + need(temp.is_absolute() and temp.is_dir() and + temp not in (Path("/"), Path("/tmp")), "RUNNER_TEMP_INVALID") + runner = pwd.getpwnam("runner") + need(runner.pw_uid > 0 and runner.pw_dir == "/home/runner", "REAL_HOSTED_RUNNER") + need(not ENTRY.exists() and not ENTRY.is_symlink(), "FOREIGN_ENTRY_PRESENT") + need(not ENTRY.parent.exists() and not ENTRY.parent.is_symlink(), + "FOREIGN_TRUST_DIRECTORY") + for candidate in ("/usr/lib/directinternetmethod", + "/var/lib/directinternetmethod/install.json", + "/var/lib/directinternetmethod/upgrade-guard"): + need(not Path(candidate).exists(), "NOT_DISPOSABLE_FRESH_BACKEND") + need(SOURCE.is_file() and ARCHIVE.is_file() and + sha_file(SOURCE) == PIN_ENTRY and sha_file(ARCHIVE) == PIN_ZIP, + "PINNED_QA_SOURCE_DIFFERENT") + return temp, runner.pw_uid + +def trusted_provision(): + # Only on a guarded disposable GitHub VM, not a personal Linux host. + upper = ENTRY.parent.parent + created_upper = False + if not upper.exists(): + upper.mkdir(mode=0o755) + created_upper = True + for part in (Path("/usr"), Path("/usr/local"), upper): + st = part.lstat() + need(stat.S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0 and + not (st.st_mode & 0o022), "UNTRUSTED_HOSTED_ANCESTOR") + ENTRY.parent.mkdir(mode=0o755) + fd = os.open(ENTRY, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o500) + with os.fdopen(fd, "wb") as stream: + stream.write(SOURCE.read_bytes()) + stream.flush() + os.fsync(stream.fileno()) + os.fchmod(stream.fileno(), 0o500) + st = ENTRY.lstat() + need(stat.S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0 and + st.st_nlink == 1 and stat.S_IMODE(st.st_mode) == 0o500 and + sha_file(ENTRY) == PIN_ENTRY, "NATIVE_OWNED_EXECUTABLE_IDENTITY") + return created_upper + +def package_fixture(temp: Path, uid: int): + d = temp / "dim-r782-exact-reviewed-fixture" + need(not d.exists() and not d.is_symlink(), "EXISTING_TEMP_FIXTURE") + d.mkdir(mode=0o700) + with zipfile.ZipFile(ARCHIVE) as z: + records = z.infolist() + need(len(records) == 30, "ARCHIVE_INVENTORY") + for item in records: + parts = item.filename.split("/") + need(parts[0] == PKG and ".." not in parts and + not stat.S_ISLNK(item.external_attr >> 16), "ARCHIVE_MEMBER_UNSAFE") + z.extractall(d) + package = d / PKG + for path in package.rglob("*"): + os.chown(path, uid, -1) + os.chown(package, uid, -1) + need(package.stat().st_uid == uid, "REAL_PACKAGE_USER_OWNER") + return d, package + +def native_root_snapshot(helper, package: Path, uid: int, temp: Path): + owned = temp / "dim-r782-frozen-root-only" + need(not owned.exists() and not owned.is_symlink(), "FOREIGN_ROOT_STAGING") + owned.mkdir(mode=0o700) + try: + freeze = helper.freeze_package(package, owned, uid) + need(freeze.stat().st_uid == uid and + stat.S_IMODE(freeze.stat().st_mode) == 0o500, "STAGED_LEAF_IDENTITY") + for name in ("system/upgrade_system.sh", "system/install_system.sh", + "system/upgrade_guard.py", "app/user_update_transaction.py"): + path = freeze / name + need(path.stat().st_uid == 0 and sha_file(path) == sha_file(package / name), + "STAGED_PRIVILEGED_CODE_BYTE_MISMATCH") + print("R782_ROOT_OWNED_PINNED_STAGING_PASS_NO_INSTALL") + finally: + shutil.rmtree(owned) + +def reject_drifted_manifest(package: Path, uid: int): + manifest = package / "PACKAGE_MANIFEST.json" + manifest.write_bytes(manifest.read_bytes() + b"\n") + old_dirs = set(Path("/run").glob("dim-trusted-root-*")) + env = {"PKEXEC_UID": str(uid), "PATH": "/usr/sbin:/usr/bin:/sbin:/bin", + "HOME": "/root", "LANG": "C", "PYTHONDONTWRITEBYTECODE": "1"} + outcome = subprocess.run(["/usr/bin/python3", "-I", str(ENTRY), + "--action", "upgrade", "--source", str(package)], + env=env, cwd="/", text=True, capture_output=True, + timeout=10, check=False) + need(outcome.returncode == 86 and + "R781_REJECT_TRUSTED_MANIFEST_DIGEST" in outcome.stderr, + "SIMULATED_AUTH_CORRUPTED_MANIFEST_FAILED_OPEN") + need(set(Path("/run").glob("dim-trusted-root-*")) == old_dirs, + "TEMP_ROOT_DIR_LEAKED") + need(not Path("/var/lib/directinternetmethod/install.json").exists(), + "REAL_BACKEND_TOUCHED") + print("R782_REAL_ROOT_ENTRY_CORRUPTED_MANIFEST_FAIL_CLOSED") + +def main(): + temp, uid = guard() + scratch = None + created_upper = False + parent_created = False + try: + created_upper = trusted_provision() + parent_created = True + os.environ["PKEXEC_UID"] = str(uid) # SIMULATION; no native auth conclusion + spec = importlib.util.spec_from_file_location("reviewed_root_entry", ENTRY) + assert spec and spec.loader + helper = importlib.util.module_from_spec(spec) + spec.loader.exec_module(helper) + helper.check_trusted_entry() + scratch, package = package_fixture(temp, uid) + native_root_snapshot(helper, package, uid, temp) + reject_drifted_manifest(package, uid) + print("R782_EPHEMERAL_ROOT_OWNERSHIP_AND_MANIFEST_GATE_PASS") + finally: + if scratch is not None: + need(scratch.parent == temp and scratch.name == "dim-r782-exact-reviewed-fixture", + "SCRATCH_SCOPE") + shutil.rmtree(scratch) + if parent_created: + need(ENTRY.is_file() and ENTRY.lstat().st_uid == 0 and + sha_file(ENTRY) == PIN_ENTRY, "TRUSTED_ENTRY_CLEANUP_IDENTITY") + ENTRY.unlink() + ENTRY.parent.rmdir() + if created_upper: + ENTRY.parent.parent.rmdir() + need(not ENTRY.exists() and + not Path("/var/lib/directinternetmethod/install.json").exists(), + "POST_CLEANUP_NO_PRODUCT_MUTATION") + +if __name__ == "__main__": + main() From a543819331b4036bf3a9c5b5a06fff11bbd44042 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:54:23 +0330 Subject: [PATCH 159/188] test(security): scope native Root fixture to fixed owned /run path (R782) --- tests/r782_hosted_trusted_entry_ephemeral.py | 31 ++++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index 7e9f829b..280c44a9 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -24,6 +24,10 @@ SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" ARCHIVE = BASE / "delivery" / (PKG + ".zip") +# This is a disposable original-repository GitHub VM, not an arbitrary path +# derived from RUNNER_TEMP or another user-provided environment variable. +EXPECTED_WORKSPACE = Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod") +OWNED_SCRATCH = Path("/run/dim-r782-one-time-trusted-root-fixture") def need(yes: bool, code: str): if not yes: @@ -41,14 +45,17 @@ def guard(): need(e.get("GITHUB_REF") == "refs/heads/security/r776-pinned-root-read-qa" and e.get("GITHUB_SHA") and len(e["GITHUB_SHA"]) == 40, "REVIEW_ONLY_BRANCH_AND_EXACT_COMMIT") - work = e.get("GITHUB_WORKSPACE", "") - need(work and Path(work).resolve() == BASE.resolve(), "WORKSPACE_NOT_OWNED") + need(e.get("GITHUB_WORKSPACE") == str(EXPECTED_WORKSPACE) and + BASE == EXPECTED_WORKSPACE and BASE.is_dir(), + "WORKSPACE_NOT_OWNED") commit = subprocess.run(["/usr/bin/git", "-C", str(BASE), "rev-parse", "HEAD"], check=True, capture_output=True, text=True).stdout.strip() need(commit == e["GITHUB_SHA"], "CHECKED_OUT_COMMIT_DIFFERENT") - temp = Path(e.get("RUNNER_TEMP", "")) - need(temp.is_absolute() and temp.is_dir() and - temp not in (Path("/"), Path("/tmp")), "RUNNER_TEMP_INVALID") + need(Path("/run").is_dir() and Path("/run").lstat().st_uid == 0 and + not (Path("/run").lstat().st_mode & 0o022), "ROOT_RUN_PARENT") + need(not OWNED_SCRATCH.exists() and not OWNED_SCRATCH.is_symlink(), + "NO_FOREIGN_SCRATCH") + temp = OWNED_SCRATCH runner = pwd.getpwnam("runner") need(runner.pw_uid > 0 and runner.pw_dir == "/home/runner", "REAL_HOSTED_RUNNER") need(not ENTRY.exists() and not ENTRY.is_symlink(), "FOREIGN_ENTRY_PRESENT") @@ -147,7 +154,13 @@ def main(): scratch = None created_upper = False parent_created = False + private_created = False try: + temp.mkdir(mode=0o700) + private_created = True + st = temp.lstat() + need(st.st_uid == 0 and stat.S_IMODE(st.st_mode) == 0o700 and + stat.S_ISDIR(st.st_mode), "OWNED_SCRATCH_IDENTITY") created_upper = trusted_provision() parent_created = True os.environ["PKEXEC_UID"] = str(uid) # SIMULATION; no native auth conclusion @@ -172,7 +185,13 @@ def main(): ENTRY.parent.rmdir() if created_upper: ENTRY.parent.parent.rmdir() - need(not ENTRY.exists() and + if private_created: + st = temp.lstat() + need(temp == OWNED_SCRATCH and st.st_uid == 0 and + stat.S_ISDIR(st.st_mode) and stat.S_IMODE(st.st_mode) == 0o700, + "CLEANUP_EXACT_OWNED_SCRATCH") + temp.rmdir() + need(not ENTRY.exists() and not temp.exists() and not Path("/var/lib/directinternetmethod/install.json").exists(), "POST_CLEANUP_NO_PRODUCT_MUTATION") From 4731ea9769262230f53d211e3e52f95b37d66651 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:31:09 +0330 Subject: [PATCH 160/188] fix(linux): pin source and acceptance paths to one validated file descriptor (R783) --- .github/workflows/ci.yml | 24 +++++++-------- docs/SECURITY_TRUSTED_ENTRY_R781.md | 6 ++-- linux/system/protected_upgrade_transaction.py | 25 ++++++++++++---- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +-- .../protected_upgrade_transaction_contract.py | 29 +++++++++++++++++++ tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 ++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r782_hosted_trusted_entry_ephemeral.py | 4 +-- tools/security/privileged_entry.py | 2 +- 16 files changed, 80 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 810c5b67..243c4bff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" - assert p.is_file() and p.stat().st_size==8419883 + expected="C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" + assert p.is_file() and p.stat().st_size==8420786 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -177,7 +177,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -206,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -239,7 +239,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -268,7 +268,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -297,7 +297,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -327,7 +327,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -372,7 +372,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -398,7 +398,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -647,8 +647,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' '15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' '05B8AA5452CE0316CE4E815F268E06E223E3EAF7C825B27319268C9303E32620' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md index d6cf4ea2..a1d4e49e 100644 --- a/docs/SECURITY_TRUSTED_ENTRY_R781.md +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -10,13 +10,13 @@ This directory is deliberately **outside** the primary `/usr/lib/directinternetm The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: -`05B8AA5452CE0316CE4E815F268E06E223E3EAF7C825B27319268C9303E32620` +`DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8` It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: -`07027AC1EDAA023B029CA772A5EC8BAA0AD53DB1072E3895A59B5E14F67349C8` +`2BBF8D5623C4F179A9D372AFE79C9D93339A06800DDE3E62AD578F204102C133` -The built Linux ZIP is **UNRELEASED**: SHA-256 `15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3` (8,419,883 bytes, 28 payload members). +The built Linux ZIP is **UNRELEASED**: SHA-256 `C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A` (8,420,786 bytes, 28 payload members). ## Exact sequence and guardrails diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index b68ef1b0..678f7654 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -277,7 +277,12 @@ def stage(self,source): need(not self.pending.exists() and not self.pending.is_symlink(),"SLOT_ALREADY_EXISTS") before=self._baseline() src=pathlib.Path(source) - need(src.is_dir() and not src.is_symlink(),"PACKAGE_SOURCE") + # R783: source member bytes are opened and SHA-pinned later through + # guarded_file_bytes() on the SAME verified inode. A separate + # is_dir()/is_symlink() pathname precheck cannot authenticate the + # later descriptor and adds an avoidable time-of-check race. + need(src.is_absolute() and ".." not in src.parts and + len(src.parts)>=2,"PACKAGE_SOURCE") # Source may be user-owned (pkexec), but its bytes are pinned to # the independently tested GitHub candidate; only mapped files read. data={} @@ -433,10 +438,20 @@ def commit(self,acceptance): "COMMIT_BACKEND_CHANGED") need(meta==m["newMetadataSHA"],"COMMIT_META_CHANGED") acceptance=pathlib.Path(acceptance) - need(acceptance.is_file() and not acceptance.is_symlink(), - "NATIVE_ACCEPTANCE_MISSING") - receipt=json.loads(guarded_file_bytes( - acceptance,native_receipt=self.native,limit=65536).decode("utf8")) + # R783: avoid pathname file/symlink preflight. guarded_file_bytes + # alone opens NOFOLLOW through pinned dirfds, fstats that exact + # inode, enforces root-owned receipt metadata in native mode and + # reads the bounded bytes. Preserve the prior rejection contract + # for missing or symlinked acceptance files. + try: + receipt_blob=guarded_file_bytes( + acceptance,native_receipt=self.native,limit=65536) + except RuntimeError as exc: + if str(exc)=="FAIL_CLOSED_UNSAFE_FILE_COMPONENT": + raise RuntimeError( + "FAIL_CLOSED_NATIVE_ACCEPTANCE_MISSING") from exc + raise + receipt=json.loads(receipt_blob.decode("utf8")) need(receipt.get("revision")=="R519" and receipt.get("status")=="PASS_NATIVE_ROOT_UPGRADE" and receipt.get("sourceRootSHA")==sha(canonical(m["files"])) and diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 66293afa..15f5a764 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" +PIN_ROOT="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 999c275e..a24ff35f 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" +TX_SHA="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" +GUARD_SHA="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/protected_upgrade_transaction_contract.py b/tests/protected_upgrade_transaction_contract.py index 1c405301..492b9e8b 100644 --- a/tests/protected_upgrade_transaction_contract.py +++ b/tests/protected_upgrade_transaction_contract.py @@ -152,6 +152,35 @@ def test_missing_acceptance_refuses_commit(self): self.tx.commit(self.root/"NO_SATISFACTORY_NATIVE_RUN.json") self.all_new() self.assertTrue(self.tx.pending.exists()) + def test_r783_relative_source_refuses_before_pending_creation(self): + # The actual read remains descriptor-relative to a validated absolute + # package source; a relative caller path must not be accepted. + relative=pathlib.Path(os.path.relpath(self.pkg,ROOT)) + with self.assertRaisesRegex(RuntimeError,"PACKAGE_SOURCE"): + self.tx.stage(relative) + self.assertFalse(self.tx.pending.exists()) + self.all_old() + + def test_r783_acceptance_symlink_fails_without_commit(self): + self.tx.stage(self.pkg) + self.tx.apply() + link=self.root/"r783_receipt_symlink.json" + link.symlink_to(self.meta) + with self.assertRaisesRegex(RuntimeError,"NATIVE_ACCEPTANCE_MISSING"): + self.tx.commit(link) + self.assertEqual(self.tx.status()["status"],"APPLIED") + self.all_new() + + def test_r783_acceptance_parent_symlink_fails_without_commit(self): + self.tx.stage(self.pkg) + self.tx.apply() + alias=self.root/"r783_parent_symlink" + alias.symlink_to(self.root,target_is_directory=True) + with self.assertRaisesRegex(RuntimeError,"UNSAFE_PARENT_COMPONENT"): + self.tx.commit(alias/"receipt.json") + self.assertEqual(self.tx.status()["status"],"APPLIED") + self.all_new() + def test_acceptance_requires_real_gates(self): self.tx.stage(self.pkg);self.tx.apply() meta=self.tx.manifest() diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 0cdc5f40..0ca5b5b4 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3"} +EXPECTED={"version":VERSION,"files":28,"sha256":"C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 348eeea2..0501033b 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="15a88bcf4f1d8dcec34b62d662041750dfc2a3accc1f1efd554d92e0d6bec1e3" +CANDIDATE_SHA="c02a7ceaed5d4764250c7416d8e51a4bb92be17fc4ca936ea349113ae733bd3a" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 72ad60ab..9eedac72 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "f89aba464f1c1b0a59300fa180cc061966c0b07db83c70fc06ac28f7a1f98b5d" +ROOT_SHA = "d003a699741cc1c069204833a1cb07743aa30fa2d829447c2683695f339149c2" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 13c08b7d..00a08689 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "f89aba464f1c1b0a59300fa180cc061966c0b07db83c70fc06ac28f7a1f98b5d" +PIN_ROOT = "d003a699741cc1c069204833a1cb07743aa30fa2d829447c2683695f339149c2" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index a6ddaf3d..f7ebdd74 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" +SOURCE_SHA="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index dec84e88..5913101a 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" -PIN_ROOT="F89ABA464F1C1B0A59300FA180CC061966C0B07DB83C70FC06AC28F7A1F98B5D" -PIN_GUARD="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" +PIN_PACKAGE="C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" +PIN_ROOT="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" +PIN_GUARD="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index bd11c881..16e345ba 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="327FDCD85A3AB328B52107AA07617549305DD01DBC518F4FC52D65AA90F781B1" +PIN_GUARD="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index a27e7595..cad90233 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" +ARCHIVE="C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index 280c44a9..a6f98c68 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,8 +18,8 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "05B8AA5452CE0316CE4E815F268E06E223E3EAF7C825B27319268C9303E32620" -PIN_ZIP = "15A88BCF4F1D8DCEC34B62D662041750DFC2A3ACCC1F1EFD554D92E0D6BEC1E3" +PIN_ENTRY = "DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8" +PIN_ZIP = "C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index 691b0ad2..ab31054b 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.5-rc1" PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "07027AC1EDAA023B029CA772A5EC8BAA0AD53DB1072E3895A59B5E14F67349C8" +MANIFEST_SHA256 = "2BBF8D5623C4F179A9D372AFE79C9D93339A06800DDE3E62AD578F204102C133" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From 5c8cc9ef3535584f478bdf0fabc7feb92ab45c87 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:35:00 +0330 Subject: [PATCH 161/188] test(linux): retain native fail-closed parent receipt guard (R783) --- tests/r527_privileged_root_transaction_contract.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/r527_privileged_root_transaction_contract.py b/tests/r527_privileged_root_transaction_contract.py index bbffc0d3..e997bd68 100644 --- a/tests/r527_privileged_root_transaction_contract.py +++ b/tests/r527_privileged_root_transaction_contract.py @@ -185,7 +185,12 @@ def test_11_reject_stale_second_stage(self): def test_12_commit_without_live_acceptance_forbidden(self): self.tx.stage(self.fx.pkg) self.tx.apply() - with self.assertRaisesRegex(RuntimeError, "NATIVE_ACCEPTANCE_MISSING"): + # The fake Root fixture lives under runner-owned GITHUB_WORKSPACE. + # R783's single-descriptor reader rejects its untrusted ancestor + # before it would check the missing receipt leaf. Both are narrowly + # valid fail-closed causes; neither may commit or mutate the baseline. + with self.assertRaisesRegex(RuntimeError, + "NATIVE_ACCEPTANCE_MISSING|RECEIPT_PARENT_OWNER"): self.tx.commit(self.root / "not-a-real-acceptance.json") self.assertEqual(self.tx.status()["status"], "APPLIED") self.assertEqual(self.tx.rollback()["status"], "ROLLED_BACK") From ce9973c4eaf6b4eba35759ed892aa7c1bdb90fce Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:39:50 +0330 Subject: [PATCH 162/188] test(linux): native fake receipt rejects unsafe parent before leaf (R783) --- tests/r528_ephemeral_native_root_cli_contract.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 0501033b..944ecf17 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -213,7 +213,12 @@ def run_native(): t.commit(temp/"NO_NATIVE_ACCEPTANCE_RECEIPT.json") raise RuntimeError("R528_BAD_ACCEPTANCE_WAS_ALLOWED") except RuntimeError as exc: - need("FAIL_CLOSED_NATIVE_ACCEPTANCE_MISSING" in str(exc), + # R783: the disposable native runner puts its absent receipt under a + # runner-owned temporary ancestor. The single-descriptor receipt read + # must fail closed before opening that leaf; an untrusted parent and + # a missing trusted leaf are both narrowly acceptable reject causes. + need(("FAIL_CLOSED_NATIVE_ACCEPTANCE_MISSING" in str(exc) or + "FAIL_CLOSED_RECEIPT_PARENT_OWNER" in str(exc)), "UNEXPECTED_ACCEPTANCE_REJECTION") need(t.rollback()["status"]=="ROLLED_BACK","ROOT_ROLLBACK") need(t.status()["status"]=="ROLLED_BACK","ROOT_ROLLBACK_STATE") From 03ae50be263305de517a03266c8d1ff40955a26d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:32:35 +0330 Subject: [PATCH 163/188] test(security): pin privileged CI fixture paths to owned GitHub-hosted locations (R784) --- tests/r527_privileged_root_transaction_contract.py | 8 ++++++-- tests/r528_ephemeral_native_root_cli_contract.py | 12 ++++++++---- tests/r532_ephemeral_root_watchdog_contract.py | 7 +++++-- tests/r554_native_guard_ephemeral_contract.py | 3 ++- tests/r566_native_systemd_persistent_catchup.py | 11 ++++++++--- tests/r567_hosted_denied_polkit_rollback.py | 3 ++- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/tests/r527_privileged_root_transaction_contract.py b/tests/r527_privileged_root_transaction_contract.py index e997bd68..0c6de44f 100644 --- a/tests/r527_privileged_root_transaction_contract.py +++ b/tests/r527_privileged_root_transaction_contract.py @@ -47,9 +47,13 @@ def assert_ephemeral_runner(): runner_temp = os.environ.get("RUNNER_TEMP") if not workspace or not runner_temp: raise RuntimeError("R527_RUNNER_PATHS_REQUIRED") - if Path(workspace).resolve() != BASE.resolve(): + # The runner directory is a *literal* repository-owned CI location, not + # an arbitrary root path derived from job-provided environment variables. + expected_workspace=Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod") + expected_temp=Path("/home/runner/work/_temp") + if workspace!=str(expected_workspace) or BASE!=expected_workspace: raise RuntimeError("R527_WORKSPACE_MISMATCH") - if not Path(runner_temp).is_dir(): + if runner_temp!=str(expected_temp) or not expected_temp.is_dir() or expected_temp.is_symlink(): raise RuntimeError("R527_RUNNER_TEMP_MISSING") if BASE == Path("/") or BASE == Path("/usr") or BASE == Path("/home"): raise RuntimeError("R527_REAL_ROOT_BLOCKED") diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 944ecf17..185d54db 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -29,6 +29,8 @@ STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" CANDIDATE_PREFIX="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" SOURCE=BASE/"linux/system/protected_upgrade_transaction.py" +HOSTED_WORKSPACE=Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod") +HOSTED_TEMP=Path("/home/runner/work/_temp") MAPPING={ "selective_dns.py":"system/selective_dns.py", "control.sh":"system/control.sh", @@ -73,11 +75,13 @@ def prerequisites(): "NOT_HOSTED_RUNNER") workspace=os.environ.get("GITHUB_WORKSPACE") runner_temp=os.environ.get("RUNNER_TEMP") - need(bool(workspace) and Path(workspace).resolve()==BASE.resolve(),"WORKSPACE_AUTHORITY") - need(bool(runner_temp) and Path(runner_temp).is_dir(),"RUNNER_TEMP_AUTHORITY") + need(workspace==str(HOSTED_WORKSPACE) and BASE==HOSTED_WORKSPACE, + "WORKSPACE_AUTHORITY") + need(runner_temp==str(HOSTED_TEMP) and HOSTED_TEMP.is_dir() and + not HOSTED_TEMP.is_symlink(),"RUNNER_TEMP_AUTHORITY") need(json.loads((BASE/"RELEASE.json").read_text())["version"]=="1.5.4", "STABLE_RELEASE_AUTHORITY") - stable=Path(runner_temp)/"DIM_R528_STABLE.zip" + stable=HOSTED_TEMP/"DIM_R528_STABLE.zip" candidate=BASE/"delivery"/CANDIDATE_ARCHIVE need(stable.is_file() and fsha(stable)==STABLE_SHA,"PINNED_PUBLIC_STABLE_ZIP") need(candidate.is_file() and fsha(candidate)==CANDIDATE_SHA, @@ -101,7 +105,7 @@ def prerequisites(): need(user.pw_uid>0 and user.pw_dir=="/home/runner","STANDARD_HOSTED_RUNNER_USER") need(not Path("/home/runner/.local/share/DirectInternetMethod/directmethod/state.json").exists(), "EXISTING_ACTIVE_USER_ENGINE") - return stable,candidate,Path(runner_temp),user + return stable,candidate,HOSTED_TEMP,user def archive_verify(z:zipfile.ZipFile,prefix:str,expected_count:int): names=z.namelist() diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 9eedac72..aa1d3d6c 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -75,8 +75,11 @@ def verify_host(): need(os.environ.get("RUNNER_ENVIRONMENT")=="github-hosted" and os.environ.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod", "EPHEMERAL_REPO_RUNNER_REQUIRED") - need(Path(os.environ.get("GITHUB_WORKSPACE","")).resolve()==BASE.resolve(),"EXACT_WORKSPACE") - need(Path(os.environ.get("RUNNER_TEMP","/does-not-exist")).is_dir(),"RUNNER_TEMP_REQUIRED") + need(os.environ.get("GITHUB_WORKSPACE")==str(BASE) and + BASE==Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod"), + "EXACT_WORKSPACE") + need(os.environ.get("RUNNER_TEMP")=="/home/runner/work/_temp" and + Path("/home/runner/work/_temp").is_dir(),"RUNNER_TEMP_REQUIRED") need(pwd.getpwnam("runner").pw_dir=="/home/runner","TEST_RUNNER_USER") need(Path("/proc/1/comm").read_text().strip()=="systemd","INDEPENDENT_SYSTEMD_PID1") need(WORKER_SOURCE.is_file() and not WORKER_SOURCE.is_symlink(),"GUARD_SOURCE_MISSING") diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 5913101a..8dfef188 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -62,7 +62,8 @@ def preflight_vm(): need(os.environ.get("GITHUB_ACTIONS")=="true" and os.environ.get("CI")=="true" and os.environ.get("RUNNER_ENVIRONMENT")=="github-hosted" and os.environ.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod" - and Path(os.environ.get("GITHUB_WORKSPACE","")).resolve()==R.resolve(), + and os.environ.get("GITHUB_WORKSPACE")==str(R) + and R==Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod"), "GITHUB_EPHEMERAL_ONLY") need(Path("/proc/1/comm").read_text().strip()=="systemd","SYSTEMD_PID1") runner=pwd.getpwnam("runner") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 16e345ba..ce4216d2 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -65,7 +65,9 @@ def preflight(): need(env.get("GITHUB_ACTIONS")=="true" and env.get("CI")=="true" and env.get("RUNNER_ENVIRONMENT")=="github-hosted" and env.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod" and - Path(env.get("GITHUB_WORKSPACE","")).resolve()==ROOT,"HOSTED_RUNNER_ONLY") + env.get("GITHUB_WORKSPACE")==str(ROOT) and + ROOT==Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod"), + "HOSTED_RUNNER_ONLY") need(Path("/proc/1/comm").read_text().strip()=="systemd","SYSTEMD_PID_ONE") need(hashlib.sha256(GUARD.read_bytes()).hexdigest().upper()==PIN_GUARD,"PINNED_GUARD") spec=importlib.util.spec_from_file_location("r566_exact_production_guard",GUARD) @@ -77,12 +79,15 @@ def main(): guard=preflight() identity=os.environ["GITHUB_RUN_ID"] need(identity.isdecimal() and len(identity)<30,"RUN_ID") - name="dim-r566-catchup-"+identity + # Fixed, fail-closed unit ID on a fresh disposable VM; the GitHub run + # identifier is checked for provenance but never enters a privileged + # filesystem pathname or systemd unit name. + name="dim-r566-catchup-784" service=Path("/etc/systemd/system")/(name+".service") timer=Path("/etc/systemd/system")/(name+".timer") for target in (service,timer): need(not target.exists() and not target.is_symlink(),"FOREIGN_SYSTEMD_UNIT") - with tempfile.TemporaryDirectory(prefix="dim-r566-"+identity+"-",dir="/run") as directory: + with tempfile.TemporaryDirectory(prefix="dim-r566-784-",dir="/run") as directory: root=Path(directory) os.chmod(root,0o700) marker=root/"fired.json" diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index cad90233..26bd7082 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -53,7 +53,8 @@ def hosted(): require(e.get("CI")=="true" and e.get("GITHUB_ACTIONS")=="true" and e.get("RUNNER_ENVIRONMENT")=="github-hosted" and e.get("GITHUB_REPOSITORY")=="GOD13emad/DirectInternetMethod" and - Path(e.get("GITHUB_WORKSPACE","")).resolve()==BASE, + e.get("GITHUB_WORKSPACE")==str(BASE) and + BASE==Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod"), "FRESH_GITHUB_VM_ONLY") runner=pwd.getpwnam("runner") require(runner.pw_dir=="/home/runner" and runner.pw_uid>0,"RUNNER") From 66ae0f17699cc216d09e4b65fa8850ffdff42206 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 20:47:39 +0330 Subject: [PATCH 164/188] fix(linux): verify exact managed-provider user stage in privileged guard --- .github/workflows/ci.yml | 24 ++++---- docs/SECURITY_TRUSTED_ENTRY_R781.md | 6 +- linux/system/upgrade_guard.py | 28 ++++++++- linux/system/upgrade_system.sh | 2 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 60 +++++++++++++++++-- tests/r554_native_guard_ephemeral_contract.py | 4 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r782_hosted_trusted_entry_ephemeral.py | 4 +- tools/security/privileged_entry.py | 2 +- 12 files changed, 106 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 243c4bff..c43d7a2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" - assert p.is_file() and p.stat().st_size==8420786 + expected="F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" + assert p.is_file() and p.stat().st_size==8422093 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -177,7 +177,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -206,7 +206,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -239,7 +239,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -268,7 +268,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -297,7 +297,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -327,7 +327,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -372,7 +372,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -398,7 +398,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -647,8 +647,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' 'C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' 'DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C81E9D6F1B517CCE19ADD72CBA6E442D18A0E88B435AAAAD3433158E48964E80' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md index a1d4e49e..c856a828 100644 --- a/docs/SECURITY_TRUSTED_ENTRY_R781.md +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -10,13 +10,13 @@ This directory is deliberately **outside** the primary `/usr/lib/directinternetm The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: -`DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8` +`C81E9D6F1B517CCE19ADD72CBA6E442D18A0E88B435AAAAD3433158E48964E80` It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: -`2BBF8D5623C4F179A9D372AFE79C9D93339A06800DDE3E62AD578F204102C133` +`5754B16D7C4916749691EC71012AAE2BAAFA28638293B10BA225962702381BF4` -The built Linux ZIP is **UNRELEASED**: SHA-256 `C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A` (8,420,786 bytes, 28 payload members). +The built Linux ZIP is **UNRELEASED**: SHA-256 `F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E` (8,422,093 bytes, 28 payload members). ## Exact sequence and guardrails diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 15f5a764..023cbad4 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -231,9 +231,31 @@ def staged_user_manifest(home:Path,uid:int,installed:Path): j.get("version")==VERSION and j.get("previousVersion")==OLD_USER, "USER_STAGE_IDENTITY") managed=j.get("managed");prior=j.get("old");mutable=j.get("mutable") - need(isinstance(managed,dict) and len(managed) in (7,8) and - isinstance(prior,dict) and set(prior)==set(managed)|{"INSTALL.json"} and - isinstance(mutable,dict) and len(mutable)==9, + # Exact package user-code file set; providers.json is managed only when + # the prior installed provider still matches its recorded managed SHA. + # R784 physical MMZ proved the old (7,8) cardinality wrongly rejected a + # legitimate 9-member stage with a managed provider. Never broaden this + # check to arbitrary ninth/foreign files. + user_files=frozenset(( + "direct_internet_method.py","socket_fallback.py","router_gateway.py", + "router_gateway_core.py","direct-internet-method.svg","uninstall.sh", + "user_uninstall_preserve.py","user_update_transaction.py")) + provider="router_gateway/providers.json" + mutable_files=frozenset(( + "settings.json","strategy.txt","scope.txt","custom-hosts.txt", + "adult-enabled.txt","adult-hosts.txt","adult-hosts.meta.json", + "encrypted-dns-optin.txt","selective-dns-optin.txt")) + managed_provider=j.get("providerManaged") + expected=user_files|({provider} if managed_provider is True else set()) + provider_sha=j.get("providerSha") + need(type(managed_provider) is bool and + isinstance(managed,dict) and set(managed)==expected and + isinstance(prior,dict) and set(prior)==expected|{"INSTALL.json"} and + isinstance(mutable,dict) and set(mutable)==mutable_files and + (provider_sha is None if managed_provider is False else + isinstance(provider_sha,str) and + isinstance(managed[provider],dict) and + managed[provider].get("sha")==provider_sha), "USER_STAGE_FILESET") meta=prior["INSTALL.json"] need(isinstance(meta,dict) and meta.get("exists") is True and diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index a24ff35f..5f6f280a 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" +GUARD_SHA="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 0ca5b5b4..8422b411 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A"} +EXPECTED={"version":VERSION,"files":28,"sha256":"F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 185d54db..1645c9ee 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="c02a7ceaed5d4764250c7416d8e51a4bb92be17fc4ca936ea349113ae733bd3a" +CANDIDATE_SHA="f60ab5672a91ea5a16f9264831c5cfb9d7a397535bd71cd1704ea7ac8c83566e" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index f7ebdd74..4cf21469 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" +SOURCE_SHA="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) @@ -306,12 +306,20 @@ def _stage_fixture(self): slot=self.home/g.PENDING_REL slot.mkdir(parents=True,mode=0o700) slot.chmod(0o700) - managed={f"managed-{i}":{"sha":"a"*64,"mode":0o755} for i in range(7)} + names=("direct_internet_method.py","socket_fallback.py", + "router_gateway.py","router_gateway_core.py", + "direct-internet-method.svg","uninstall.sh", + "user_uninstall_preserve.py","user_update_transaction.py") + managed={k:{"sha":"a"*64,"mode":0o755} for k in names} prior={k:{"exists":False,"sha":None} for k in managed} prior["INSTALL.json"]={"exists":True,"sha":g.sha_file(installed).lower()} + mutable=("settings.json","strategy.txt","scope.txt","custom-hosts.txt", + "adult-enabled.txt","adult-hosts.txt","adult-hosts.meta.json", + "encrypted-dns-optin.txt","selective-dns-optin.txt") row={"schema":1,"version":"1.5.5-rc1","previousVersion":"1.5.4", "managed":managed,"old":prior, - "mutable":{f"setting-{i}":None for i in range(9)}} + "providerManaged":False,"providerSha":None, + "mutable":{k:None for k in mutable}} manifest=slot/"manifest.json" manifest.write_text(json.dumps(row));manifest.chmod(0o600) return installed,manifest,row @@ -321,7 +329,51 @@ def test_11_real_user_stage_schema_not_invented_phase(self): self.assertNotIn("phase",row) accepted=g.staged_user_manifest(self.home,os.getuid(),installed) self.assertEqual(accepted["version"],"1.5.5-rc1") - self.assertEqual(len(accepted["managed"]),7) + self.assertEqual(len(accepted["managed"]),8) + + def test_25_real_managed_provider_nine_members_accepted(self): + installed,manifest,row=self._stage_fixture() + provider="router_gateway/providers.json" + digest="b"*64 + row["managed"][provider]={"sha":digest,"mode":0o644} + row["old"][provider]={"exists":True,"sha":digest} + row["providerManaged"]=True + row["providerSha"]=digest + manifest.write_text(json.dumps(row)) + result=g.staged_user_manifest(self.home,os.getuid(),installed) + self.assertEqual(len(result["managed"]),9) + self.assertEqual(result["providerSha"],digest) + + def test_26_managed_provider_flag_or_digest_drift_refused(self): + installed,manifest,row=self._stage_fixture() + provider="router_gateway/providers.json" + def refuse(): + manifest.write_text(json.dumps(row)) + with self.assertRaisesRegex(RuntimeError,"USER_STAGE_FILESET"): + g.staged_user_manifest(self.home,os.getuid(),installed) + row["providerManaged"]=True + refuse() # Missing managed provider entry. + row["managed"][provider]={"sha":"a"*64,"mode":0o644} + row["old"][provider]={"exists":True,"sha":"a"*64} + row["providerSha"]="0"*64 + refuse() # SHA not the staged provider's digest. + row["providerSha"]="a"*64 + row["providerManaged"]=False + refuse() # Unmanaged means provider file must NOT be staged. + + def test_27_foreign_extra_managed_and_mutable_paths_refused(self): + installed,manifest,row=self._stage_fixture() + def refuse(): + manifest.write_text(json.dumps(row)) + with self.assertRaisesRegex(RuntimeError,"USER_STAGE_FILESET"): + g.staged_user_manifest(self.home,os.getuid(),installed) + row["managed"]["foreign_plugin.sh"]={"sha":"c"*64,"mode":0o700} + row["old"]["foreign_plugin.sh"]={"exists":False,"sha":None} + refuse() + del row["managed"]["foreign_plugin.sh"] + del row["old"]["foreign_plugin.sh"] + row["mutable"]["arbitrary-foreign-user-data"]="00" + refuse() def test_12_stage_schema_and_metadata_drift_rejected(self): installed,manifest,row=self._stage_fixture() diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 8dfef188..09573f47 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" +PIN_PACKAGE="F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" PIN_ROOT="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" -PIN_GUARD="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" +PIN_GUARD="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index ce4216d2..74a5face 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="7769872F47112496AA1B0B3F0A0FFDBA5F8CF72226505462AA42FBBE0C8D70F0" +PIN_GUARD="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 26bd7082..aea711dd 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" +ARCHIVE="F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index a6f98c68..07027ef2 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,8 +18,8 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8" -PIN_ZIP = "C02A7CEAED5D4764250C7416D8E51A4BB92BE17FC4CA936EA349113AE733BD3A" +PIN_ENTRY = "C81E9D6F1B517CCE19ADD72CBA6E442D18A0E88B435AAAAD3433158E48964E80" +PIN_ZIP = "F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index ab31054b..101f597b 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.5-rc1" PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "2BBF8D5623C4F179A9D372AFE79C9D93339A06800DDE3E62AD578F204102C133" +MANIFEST_SHA256 = "5754B16D7C4916749691EC71012AAE2BAAFA28638293B10BA225962702381BF4" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From 1b3af38e5c8be90bd3874a7345b6fdaa9857f177 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:57:36 +0330 Subject: [PATCH 165/188] fix(security): bind Linux Root acceptance to fresh upgrade nonce (AI-assisted) Reject cross-attempt reuse of a previously valid Root upgrade acceptance receipt by requiring a 256-bit per-stage nonce in the protected manifest and attestation. Add a negative-first fake-root replay regression with fresh-receipt positive control, and carry exact source/guard/package/bootstrap SHA pin updates through reproducible Linux RC1 fixtures. Authorize exact original-repository security PR branch push CI for disposable native Root bootstrap and Windows R535 install/rollback QA only; keep protected main, public v1.5.4, physical hosts and publication unchanged. Local Emad Linux QA: 12/12 selected rootless suites PASS, including 28 Root transaction cases and 17 package cases. This is AI-assisted technical implementation, NOT independent human security review, CodeQL disposition, physical approval, or release acceptance. --- .github/workflows/ci.yml | 33 ++++----- docs/SECURITY_TRUSTED_ENTRY_R781.md | 8 +-- linux/system/protected_upgrade_transaction.py | 9 ++- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- .../protected_upgrade_transaction_contract.py | 1 + tests/r519_versioned_package_contract.py | 2 +- tests/r535_windows_candidate_hosted_build.ps1 | 2 +- tests/r535_windows_native_rollback.ps1 | 2 +- tests/r554_native_guard_ephemeral_contract.py | 7 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r764_parent_path_security_contract.py | 1 + tests/r776_checked_fd_swap_regression.py | 1 + tests/r782_hosted_trusted_entry_ephemeral.py | 4 +- tests/r789_receipt_replay_synthetic_probe.py | 72 +++++++++++++++++++ tests/release_metadata_audit.py | 3 +- tools/security/privileged_entry.py | 2 +- 17 files changed, 120 insertions(+), 35 deletions(-) create mode 100644 tests/r789_receipt_replay_synthetic_probe.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c43d7a2b..e00dfe9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: Source and Release Contracts on: push: - branches: [main, 'feature/**', 'hotfix/**'] + branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa'] tags: ['v*'] pull_request: branches: [main] @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" - assert p.is_file() and p.stat().st_size==8422093 + expected="D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" + assert p.is_file() and p.stat().st_size==8422557 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -62,6 +62,7 @@ jobs: python3 -B tests/r664_direct_update_monotonicity_contract.py python3 -B tests/r519_versioned_package_contract.py python3 -B tests/protected_upgrade_transaction_contract.py + python3 -B tests/r789_receipt_replay_synthetic_probe.py python3 -B tests/r519_user_update_transaction_contract.py - name: R526 Linux installer metadata activation (hermetic fake Root) shell: bash @@ -177,7 +178,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -206,7 +207,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -239,7 +240,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -268,7 +269,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -297,7 +298,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -327,7 +328,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -372,7 +373,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -398,7 +399,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -565,7 +566,7 @@ jobs: windows-private-r535-native: name: windows-private-r535-native - if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci') + if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci' || github.ref == 'refs/heads/security/r776-pinned-root-read-qa') needs: windows-source runs-on: windows-2025 timeout-minutes: 35 @@ -598,7 +599,7 @@ jobs: $installer=Join-Path $root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -cne 'github-hosted' -or - $env:GITHUB_REF -cne 'refs/heads/feature/r520-linux-155rc1-ci' -or + $env:GITHUB_REF -cnotin @('refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa') -or (git -C $root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R600_HOST_AUTHORITY'} if($report.status -cne 'PASS_ISOLATED_HOSTED_RC1_BUILD' -or $native.status -cne 'PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154' -or @@ -634,7 +635,7 @@ jobs: name: linux-r782-root-owned-bootstrap-ephemeral # Actual UID 0 on a fresh original-repository GitHub VM ONLY. # Never run this job on a user's machine, a fork, or a tag release. - if: github.event_name == 'workflow_dispatch' && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/security/r776-pinned-root-read-qa' + if: (github.event_name == 'workflow_dispatch' || github.event_name == 'push') && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/security/r776-pinned-root-read-qa' runs-on: ubuntu-latest timeout-minutes: 10 permissions: @@ -647,8 +648,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' 'F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' 'C81E9D6F1B517CCE19ADD72CBA6E442D18A0E88B435AAAAD3433158E48964E80' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '2783753829826E43C47779A90A38C941486A93F16F481E222AFC214F45B3DA78' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md index c856a828..88c5d2b5 100644 --- a/docs/SECURITY_TRUSTED_ENTRY_R781.md +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -10,13 +10,13 @@ This directory is deliberately **outside** the primary `/usr/lib/directinternetm The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: -`C81E9D6F1B517CCE19ADD72CBA6E442D18A0E88B435AAAAD3433158E48964E80` +`2783753829826E43C47779A90A38C941486A93F16F481E222AFC214F45B3DA78` It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: -`5754B16D7C4916749691EC71012AAE2BAAFA28638293B10BA225962702381BF4` +`7C0F7D85EC86C0C956B13CDEC101D2A1BEEA750B2EDD0F4AF8E87DA8824334D4` -The built Linux ZIP is **UNRELEASED**: SHA-256 `F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E` (8,422,093 bytes, 28 payload members). +The built Linux ZIP is **UNRELEASED**: SHA-256 `D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720` (8,422,557 bytes, 28 payload members). ## Exact sequence and guardrails @@ -31,7 +31,7 @@ The built Linux ZIP is **UNRELEASED**: SHA-256 `F60AB5672A91EA5A16F9264831C5CFB9 This SHA-pinned release-specific approach intentionally requires **another explicit administrator review and trusted-anchor update** for a future package with a different manifest. The project does not require arbitrary publisher digital signatures or silently trust an unverified GitHub release. -R781 rootless tests only exercise preflight, source freezing, rejection and code provenance. They do **not** prove protected install, timer/rollback, UAC/Polkit consent, live TLS routing or product-wide security. Native ephemeral hosted Root tests, independent review of PR #62/#63, expert adjudication of all CodeQL High findings and owner-authorized physical acceptance are still separate gates. No trusted entry has been installed on either physical Linux host. +R781 rootless tests only exercise preflight, source freezing, rejection and code provenance. They do **not** prove protected install, timer/rollback, UAC/Polkit consent, live TLS routing or product-wide security. Native ephemeral hosted Root tests, independent review of PR #62/#63, expert adjudication of all CodeQL High findings and owner-authorized physical acceptance are still separate gates. The current new digest of the trusted entry has NOT been installed on either physical Linux host. Read-only native SHA checks on 2026-10-19 found previous root-owned 0500 entry SHA256 `DD878D594830A3C1A85283E3399BF96B42B9FBC02AF68D993DE5507D605577D8` on both; replacing it requires fresh administrator consent after reviewed security acceptance. ## Revocation and recovery diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 678f7654..dba6f69b 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -268,6 +268,11 @@ def manifest(self): data.get("toVersion")==TARGET_VERSION,"MANIFEST_VERSION") need(data.get("phase") in ("STAGED","APPLIED","ROLLED_BACK","DEGRADED","COMMITTED"), "MANIFEST_PHASE") + # Bind each Root acceptance to this particular staged attempt, not an + # earlier successful receipt for the same deterministic payload. + need(type(data.get("transactionNonce")) is str and + re.fullmatch(r"[0-9a-f]{64}",data["transactionNonce"]) is not None, + "MANIFEST_NONCE") need(set(data.get("files",{}))=={row[0] for row in FILES},"MANIFEST_FILE_LIST") return data def write_manifest(self,data): @@ -325,7 +330,8 @@ def stage(self,source): payload={"schema":SCHEMA,"revision":"R519","phase":"STAGED", "fromVersion":CURRENT_BACKEND_VERSION,"toVersion":TARGET_VERSION, "files":entries,"oldMetadataSHA":sha(orig),"newMetadataSHA":sha(new_blob), - "uid":before["uid"],"home":before["home"],"user":before["user"]} + "uid":before["uid"],"home":before["home"],"user":before["user"], + "transactionNonce":os.urandom(32).hex()} self.write_manifest(payload) return {"status":"STAGED","files":len(FILES),"version":TARGET_VERSION} except BaseException: @@ -455,6 +461,7 @@ def commit(self,acceptance): need(receipt.get("revision")=="R519" and receipt.get("status")=="PASS_NATIVE_ROOT_UPGRADE" and receipt.get("sourceRootSHA")==sha(canonical(m["files"])) and + receipt.get("transactionNonce")==m["transactionNonce"] and receipt.get("protectedMetadataSHA")==meta and receipt.get("nativeStartStopRecovery") is True and receipt.get("tailscalePreserved") is True and diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 023cbad4..53c729ef 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" +PIN_ROOT="C6F4DF4084C44F6F67E9144F9C4F6FECFA339086039659A252A2C609F16EDA60" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index 5f6f280a..e2317779 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" +TX_SHA="C6F4DF4084C44F6F67E9144F9C4F6FECFA339086039659A252A2C609F16EDA60" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" +GUARD_SHA="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/protected_upgrade_transaction_contract.py b/tests/protected_upgrade_transaction_contract.py index 492b9e8b..741935cc 100644 --- a/tests/protected_upgrade_transaction_contract.py +++ b/tests/protected_upgrade_transaction_contract.py @@ -188,6 +188,7 @@ def test_acceptance_requires_real_gates(self): a={"revision":"R519","status":"PASS_NATIVE_ROOT_UPGRADE", "sourceRootSHA":m.sha(m.canonical(meta["files"])), "protectedMetadataSHA":meta["newMetadataSHA"], + "transactionNonce":meta["transactionNonce"], "nativeStartStopRecovery":False,"tailscalePreserved":True, "physicalDNSPreserved":True} f.write_text(json.dumps(a)) diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 8422b411..da797d02 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E"} +EXPECTED={"version":VERSION,"files":28,"sha256":"D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r535_windows_candidate_hosted_build.ps1 b/tests/r535_windows_candidate_hosted_build.ps1 index 8ccd3aa7..9d492a70 100644 --- a/tests/r535_windows_candidate_hosted_build.ps1 +++ b/tests/r535_windows_candidate_hosted_build.ps1 @@ -4,7 +4,7 @@ $ErrorActionPreference='Stop' Set-StrictMode -Version Latest $Root=$env:GITHUB_WORKSPACE if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_GITHUB_HOST_ONLY'} -if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} $r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json if($r.version -cne '1.5.5-rc1' -or $r.status -cne 'PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R535_RC1_DECLARATION'} $qa=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' diff --git a/tests/r535_windows_native_rollback.ps1 b/tests/r535_windows_native_rollback.ps1 index 16d13562..54c00f9a 100644 --- a/tests/r535_windows_native_rollback.ps1 +++ b/tests/r535_windows_native_rollback.ps1 @@ -20,7 +20,7 @@ $PublicSha='FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133' $PublicGui='BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F' $PublicSvc='2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_HOSTED_VM_ONLY'} -if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} $admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R535_ADMIN_TOKEN_REQUIRED'} if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R535_UNSAFE_REPLAY_OR_MISSING_BUILD'} diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 09573f47..5554bda2 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" -PIN_ROOT="D003A699741CC1C069204833A1CB07743AA30FA2D829447C2683695F339149C2" -PIN_GUARD="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" +PIN_PACKAGE="D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" +PIN_ROOT="C6F4DF4084C44F6F67E9144F9C4F6FECFA339086039659A252A2C609F16EDA60" +PIN_GUARD="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") @@ -193,6 +193,7 @@ def run(): "syntheticAcceptanceFixture":True, "sourceRootSHA":rootmod.sha(rootmod.canonical(m["files"])), "protectedMetadataSHA":m["newMetadataSHA"], + "transactionNonce":m["transactionNonce"], "nativeStartStopRecovery":True, "tailscalePreserved":True, "physicalDNSPreserved":True diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index aea711dd..31810ad6 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" +ARCHIVE="D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r764_parent_path_security_contract.py b/tests/r764_parent_path_security_contract.py index c18620ee..5d131bce 100644 --- a/tests/r764_parent_path_security_contract.py +++ b/tests/r764_parent_path_security_contract.py @@ -44,6 +44,7 @@ def test_symlink_parent_of_root_acceptance_receipt_is_rejected(self): data={"revision":"R519","status":"PASS_NATIVE_ROOT_UPGRADE", "sourceRootSHA":module.m.sha(module.m.canonical(doc["files"])), "protectedMetadataSHA":doc["newMetadataSHA"], + "transactionNonce":doc["transactionNonce"], "nativeStartStopRecovery":True, "tailscalePreserved":True,"physicalDNSPreserved":True} receipt.write_text(json.dumps(data)+"\n") diff --git a/tests/r776_checked_fd_swap_regression.py b/tests/r776_checked_fd_swap_regression.py index 39d618b5..68d4c91b 100644 --- a/tests/r776_checked_fd_swap_regression.py +++ b/tests/r776_checked_fd_swap_regression.py @@ -46,6 +46,7 @@ def setUp(self): "fromVersion": mod.CURRENT_BACKEND_VERSION, "toVersion": mod.TARGET_VERSION, "phase": "STAGED", "files": {name: {} for name,*_ in mod.FILES}, + "transactionNonce": "0"*64, } self.evil_manifest = dict(self.good_manifest, phase="COMMITTED") write_json(self.manifest, self.good_manifest, mode=0o600) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index 07027ef2..425432db 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,8 +18,8 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "C81E9D6F1B517CCE19ADD72CBA6E442D18A0E88B435AAAAD3433158E48964E80" -PIN_ZIP = "F60AB5672A91EA5A16F9264831C5CFB9D7A397535BD71CD1704EA7AC8C83566E" +PIN_ENTRY = "2783753829826E43C47779A90A38C941486A93F16F481E222AFC214F45B3DA78" +PIN_ZIP = "D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" diff --git a/tests/r789_receipt_replay_synthetic_probe.py b/tests/r789_receipt_replay_synthetic_probe.py new file mode 100644 index 00000000..1b9d0d4e --- /dev/null +++ b/tests/r789_receipt_replay_synthetic_probe.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Defensive read-only-source regression: acceptance receipt replay across synthetic upgrade attempts. + +Only R519 FakeFixture disposable user-owned roots; NEVER invokes pkexec, +real RootTransaction CLI, installed services or network. Evidence only. +""" +import importlib.util +import json +from pathlib import Path + +ROOT=Path(__file__).resolve().parents[1] +spec=importlib.util.spec_from_file_location("r789_fixture",ROOT/"tests/protected_upgrade_transaction_contract.py") +fixture=importlib.util.module_from_spec(spec) +spec.loader.exec_module(fixture) + +def staged_fixture(): + obj=fixture.FakeFixture("test_success_stage_apply_rollback") + obj.setUp() + assert obj.tx.stage(obj.pkg)["status"]=="STAGED" + assert obj.tx.apply()["status"]=="APPLIED" + return obj + +one=staged_fixture() +try: + one_m=one.tx.manifest() + prior={ + "revision":"R519", + "status":"PASS_NATIVE_ROOT_UPGRADE", + "sourceRootSHA":fixture.m.sha(fixture.m.canonical(one_m["files"])), + "protectedMetadataSHA":one_m["newMetadataSHA"], + "transactionNonce":one_m["transactionNonce"], + "nativeStartStopRecovery":True, + "tailscalePreserved":True, + "physicalDNSPreserved":True + } + first_file=one.root/"first_attempt_receipt.json" + first_file.write_text(json.dumps(prior)+"\n") + assert one.tx.commit(first_file)["status"]=="COMMITTED" +finally: + one.tearDown() + +two=staged_fixture() +try: + two_m=two.tx.manifest() + assert prior["sourceRootSHA"]==fixture.m.sha(fixture.m.canonical(two_m["files"])) + assert prior["protectedMetadataSHA"]==two_m["newMetadataSHA"] + replay=two.root/"STALE_previous_attempt.json" + replay.write_text(json.dumps(prior)+"\n") + # No fresh acceptance exists yet. Reusing an older success receipt + # from an identical baseline MUST be rejected. + assert prior["transactionNonce"] != two_m["transactionNonce"] + try: + two.tx.commit(replay) + except RuntimeError as exc: + assert "FAIL_CLOSED_NATIVE_ACCEPTANCE_NOT_PROVEN" in str(exc),str(exc) + else: + raise AssertionError("R789_STALE_ACCEPTANCE_REPLAYED") + assert two.tx.status()["status"] == "APPLIED" + # A genuine fresh transaction-specific receipt still permits commit. + fresh=dict(prior,transactionNonce=two_m["transactionNonce"]) + fresh_file=two.root/"current_attempt_receipt.json" + fresh_file.write_text(json.dumps(fresh)+"\n") + outcome=two.tx.commit(fresh_file) + assert outcome["status"] == "COMMITTED" + print(json.dumps({ + "schema":1,"scenario":"two_identical_synthetic_upgrade_attempts", + "stale_receipt_rejected":True, + "fresh_receipt_committed":True, + "scope":"FAKE_ROOT_ONLY; native owner/mode and Polkit not tested" + },sort_keys=True)) +finally: + two.tearDown() diff --git a/tests/release_metadata_audit.py b/tests/release_metadata_audit.py index 4d1ca7e7..0a7aa820 100644 --- a/tests/release_metadata_audit.py +++ b/tests/release_metadata_audit.py @@ -164,7 +164,8 @@ def require_public_stable_forward_update_from_rc155(version: str) -> None: and win_release.get('publication',{}).get('publishedAt') is None and (branch == 'feature/r535-windows-155rc1' or ref in ('refs/heads/feature/r535-windows-155rc1', - 'refs/heads/feature/r520-linux-155rc1-ci')) + 'refs/heads/feature/r520-linux-155rc1-ci', + 'refs/heads/security/r776-pinned-root-read-qa')) ) if win_version != version and not private_win_rc1: raise SystemExit('WINDOWS_PRIVATE_RC1_IDENTITY_NOT_AUTHORIZED') diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index 101f597b..ab881ad4 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.5-rc1" PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "5754B16D7C4916749691EC71012AAE2BAAFA28638293B10BA225962702381BF4" +MANIFEST_SHA256 = "7C0F7D85EC86C0C956B13CDEC101D2A1BEEA750B2EDD0F4AF8E87DA8824334D4" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From bbab84205dc09a16c28c0767bdc2bf50ac42f607 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:03:53 +0330 Subject: [PATCH 166/188] test(linux): refresh independent watchdog Root/Guard pins after nonce hardening Rootless CI 39311169443 exposed an independent R698 static guard failing because historical R532 worker and ephemeral watchdog fixture SHA pins still referenced previous protected Root source. Update exactly four existing test-only SHA declarations to the current Root and Guard hashes. No product binary, release metadata, native installation or acceptance policy changed. R698 native nfqws dry-run and source SHA chain PASS; R552 negative/guard fixture PASS on isolated Emad Linux checkout. This is a technical CI fixture repair, not security reviewer approval. --- tests/r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r566_native_systemd_persistent_catchup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index aa1d3d6c..e633ecaf 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "d003a699741cc1c069204833a1cb07743aa30fa2d829447c2683695f339149c2" +ROOT_SHA = "c6f4df4084c44f6f67e9144f9c4f6fecfa339086039659a252a2c609f16eda60" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 00a08689..bffdc8b6 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "d003a699741cc1c069204833a1cb07743aa30fa2d829447c2683695f339149c2" +PIN_ROOT = "c6f4df4084c44f6f67e9144f9c4f6fecfa339086039659a252a2c609f16eda60" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 4cf21469..306bce7c 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" +SOURCE_SHA="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 74a5face..472cc50c 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="B66A8B493C343E539EA9FC2FAAA26AD0AEBA067BE07ABCDAD3009502206A52FB" +PIN_GUARD="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) From 878e14c0d8f364fd5b908bb60464ea2ae8cabe4d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:06:46 +0330 Subject: [PATCH 167/188] test(linux): update R528 native Root candidate digest after nonce hardening Exact-head original hosted CI 39312048245 failed R528 native Root and R567 denied-Polkit guard before modifying privileged state because R528's private archive SHA still referenced the pre-nonce package. Update only that digest to the reproducible D34F779... candidate. Independent local digest fixture PASS and Linux source/Windows source/bootstrap QA green. Do not weaken runner identity, Root ownership or acceptance checks. Private QA only; no human approval or publication. --- tests/r528_ephemeral_native_root_cli_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 1645c9ee..1c944675 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="f60ab5672a91ea5a16f9264831c5cfb9d7a397535bd71cd1704ea7ac8c83566e" +CANDIDATE_SHA="d34f779322634bf35031b7a8594219b0ad1dd69296ef3637d23cbc97e7e99720" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" From e5726b0536f9d97e7194e7aafbe5bc5ed70dfeb4 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:57:29 +0330 Subject: [PATCH 168/188] refactor(security): unify pinned fd readers, remove racy pathname preflight (AI-assisted) R794 removes an unnecessary Path.is_symlink() preflight and consolidates the two descriptor-pinned regular-file readers into one O_NOFOLLOW|O_NONBLOCK per-component dir_fd walker. Native receipt path still checks root-owned ancestors, regular leaf, safe permissions and bounded bytes; symlink/FIFO, path-ancestor and same-inode SHA invariants are retained. Existing leaf symlink/missing diagnostic is explicitly preserved. Private v1.5.5-rc1 SHA pin-chain for Root/Guard/bootstrap/archive and all dependent native QA fixtures updated deterministically. Exact local source 13/13 selected rootless regression suites PASS, incl. 28 R519 cases, 17 package cases, 14 trusted-entry cases, R698 and R789 stale receipt rejection. Do not claim CodeQL PASS until new exact-head scanner and original hosted CI finish. This is review-only; no publication or personal-host deployment. --- .github/workflows/ci.yml | 24 +++--- docs/SECURITY_TRUSTED_ENTRY_R781.md | 6 +- linux/system/protected_upgrade_transaction.py | 74 ++++++++----------- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r782_hosted_trusted_entry_ephemeral.py | 4 +- tools/security/privileged_entry.py | 2 +- 15 files changed, 61 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e00dfe9d..713965be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" - assert p.is_file() and p.stat().st_size==8422557 + expected="DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" + assert p.is_file() and p.stat().st_size==8422000 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -178,7 +178,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -207,7 +207,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -240,7 +240,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -269,7 +269,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -298,7 +298,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -328,7 +328,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -373,7 +373,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -399,7 +399,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -648,8 +648,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' 'D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' '2783753829826E43C47779A90A38C941486A93F16F481E222AFC214F45B3DA78' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F9FDD13D889FF0323E20CE45532F1F9204FF3FB393BC17429A5C5CAECCCF129A' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md index 88c5d2b5..1b9e3b44 100644 --- a/docs/SECURITY_TRUSTED_ENTRY_R781.md +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -10,13 +10,13 @@ This directory is deliberately **outside** the primary `/usr/lib/directinternetm The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: -`2783753829826E43C47779A90A38C941486A93F16F481E222AFC214F45B3DA78` +`F9FDD13D889FF0323E20CE45532F1F9204FF3FB393BC17429A5C5CAECCCF129A` It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: -`7C0F7D85EC86C0C956B13CDEC101D2A1BEEA750B2EDD0F4AF8E87DA8824334D4` +`B4669C9DC6481A7DC9BC85FFD0039EB66353F8406BE93570B73676CC83660FFA` -The built Linux ZIP is **UNRELEASED**: SHA-256 `D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720` (8,422,557 bytes, 28 payload members). +The built Linux ZIP is **UNRELEASED**: SHA-256 `DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777` (8,422,000 bytes, 28 payload members). ## Exact sequence and guardrails diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index dba6f69b..b75be2c9 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -51,7 +51,7 @@ def need(ok,why): if not ok:raise RuntimeError("FAIL_CLOSED_"+why) def sha(data):return hashlib.sha256(data).hexdigest().upper() @contextmanager -def verified_regular_fd(path): +def verified_regular_fd(path, *, native_receipt=False): """Pin a regular file by descriptor, never traversing a symlink parent. Unlike inspecting path.is_file() then opening later, both the metadata and @@ -72,6 +72,13 @@ def verified_regular_fd(path): raise RuntimeError("FAIL_CLOSED_UNSAFE_PARENT_COMPONENT") from exc os.close(parent) parent=child + if native_receipt: + entry=os.fstat(parent) + need(entry.st_uid==0 and + (not entry.st_mode & 0o022 or + (bool(entry.st_mode & stat.S_ISVTX) and + stat.S_ISDIR(entry.st_mode))), + "RECEIPT_PARENT_OWNER") try: opened=os.open(path.name,flags,dir_fd=parent) except OSError as exc: @@ -107,57 +114,36 @@ def checked_regular_bytes(path, *, owner=False, mode=None, max_bytes=2**20): def canonical(doc): return (json.dumps(doc,sort_keys=True,indent=2,ensure_ascii=False)+"\n").encode("utf8") def no_symlink_regular(path,owner=True): + """Check the actual pinned descriptor, not a racy pathname preflight.""" path=pathlib.Path(path) - need(not path.is_symlink(),"MISSING_OR_SYMLINK_"+path.name) - with verified_regular_fd(path) as (_,st): - if owner:need(st.st_uid==0 and st.st_gid==0,"OWNER_"+path.name) - return st + try: + with verified_regular_fd(path) as (_,st): + if owner:need(st.st_uid==0 and st.st_gid==0,"OWNER_"+path.name) + return st + except RuntimeError as exc: + # Preserve the existing precise leaf-symlink/missing rejection contract. + # A symlinked parent keeps the distinct UNSAFE_PARENT_COMPONENT error. + if str(exc)=="FAIL_CLOSED_UNSAFE_FILE_COMPONENT": + raise RuntimeError("FAIL_CLOSED_MISSING_OR_SYMLINK_"+path.name) from exc + raise + def guarded_file_bytes(path, *, native_receipt=False, limit=2**21): - """Read a bounded file through dirfds, refusing symlinks in EVERY parent. + """Bounded source/receipt reader through the SAME pinned descriptor helper. - Package bytes may be user-owned; their exact SHA is separately pinned. - Final native acceptance is more privileged and must have an OS-owned, - non-group/world-writable file under safe OS-owned directories. A root-owned - sticky directory (for example /tmp) may contain a private root test slot. + User-owned package members are admitted only after exact SHA validation. + Native receipts additionally require safe root-owned ancestry and leaf. + The descriptor never follows a symlink, opens a FIFO blocking, or changes + the identity between stat and read. """ - path=pathlib.Path(path) - need(path.is_absolute() and ".." not in path.parts and - len(path.parts)>=2, "UNSAFE_ABSOLUTE_FILE_PATH") - # The source package is user-owned: never block on a FIFO before type check. - flags=os.O_RDONLY|os.O_CLOEXEC|os.O_NOFOLLOW|os.O_NONBLOCK - directory=os.open("/",flags|os.O_DIRECTORY) - opened=None - try: - for part in path.parts[1:-1]: - try: - child=os.open(part,flags|os.O_DIRECTORY,dir_fd=directory) - except OSError as exc: - raise RuntimeError("FAIL_CLOSED_UNSAFE_PARENT_COMPONENT") from exc - os.close(directory);directory=child - if native_receipt: - entry=os.fstat(directory) - need(entry.st_uid==0 and - (not entry.st_mode & 0o022 or - (bool(entry.st_mode & stat.S_ISVTX) and - stat.S_ISDIR(entry.st_mode))), - "RECEIPT_PARENT_OWNER") - try: - opened=os.open(path.name,flags,dir_fd=directory) - except OSError as exc: - raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") from exc - info=os.fstat(opened) - need(stat.S_ISREG(info.st_mode) and 0<=info.st_size<=limit, - "UNSAFE_FILE_TYPE_OR_SIZE") + with verified_regular_fd(path,native_receipt=native_receipt) as (fd,info): + need(0<=info.st_size<=limit,"UNSAFE_FILE_TYPE_OR_SIZE") if native_receipt: need(info.st_uid==0 and info.st_gid==0 and - not info.st_mode & 0o022, "UNTRUSTED_RECEIPT_OWNER") - with os.fdopen(os.dup(opened),"rb") as reader: + not info.st_mode & 0o022,"UNTRUSTED_RECEIPT_OWNER") + with os.fdopen(os.dup(fd),"rb") as reader: content=reader.read(limit+1) - need(len(content)<=limit, "FILE_SIZE_CHANGED_DURING_READ") + need(len(content)<=limit,"FILE_SIZE_CHANGED_DURING_READ") return content - finally: - if opened is not None:os.close(opened) - os.close(directory) def flush_dir(directory): diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 53c729ef..da503be3 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="C6F4DF4084C44F6F67E9144F9C4F6FECFA339086039659A252A2C609F16EDA60" +PIN_ROOT="6ABD10A709F68F4330B97CF903CB13E231CEB1B763AD9A378F422EA4F6A1E9A0" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index e2317779..ae8f779e 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="C6F4DF4084C44F6F67E9144F9C4F6FECFA339086039659A252A2C609F16EDA60" +TX_SHA="6ABD10A709F68F4330B97CF903CB13E231CEB1B763AD9A378F422EA4F6A1E9A0" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" +GUARD_SHA="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index da797d02..a88ab7d1 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720"} +EXPECTED={"version":VERSION,"files":28,"sha256":"DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 1c944675..40a3a1b7 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="d34f779322634bf35031b7a8594219b0ad1dd69296ef3637d23cbc97e7e99720" +CANDIDATE_SHA="dcb7b3a875466dfed664bfa0fa45eb2c57aaf6450161a898e4304f19f718e777" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index e633ecaf..d01cb8a5 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "c6f4df4084c44f6f67e9144f9c4f6fecfa339086039659a252a2c609f16eda60" +ROOT_SHA = "6abd10a709f68f4330b97cf903cb13e231ceb1b763ad9a378f422ea4f6a1e9a0" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index bffdc8b6..a02eab2a 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "c6f4df4084c44f6f67e9144f9c4f6fecfa339086039659a252a2c609f16eda60" +PIN_ROOT = "6abd10a709f68f4330b97cf903cb13e231ceb1b763ad9a378f422ea4f6a1e9a0" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 306bce7c..e768e2fe 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" +SOURCE_SHA="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 5554bda2..ac29f34e 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" -PIN_ROOT="C6F4DF4084C44F6F67E9144F9C4F6FECFA339086039659A252A2C609F16EDA60" -PIN_GUARD="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" +PIN_PACKAGE="DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" +PIN_ROOT="6ABD10A709F68F4330B97CF903CB13E231CEB1B763AD9A378F422EA4F6A1E9A0" +PIN_GUARD="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 472cc50c..5c1e7817 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="CE3D3B56510BA2D106B078D3F6FF24C9C71022ADDDA9FE4DEAD9296A27EE133B" +PIN_GUARD="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 31810ad6..4bf5768f 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" +ARCHIVE="DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index 425432db..c3730187 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,8 +18,8 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "2783753829826E43C47779A90A38C941486A93F16F481E222AFC214F45B3DA78" -PIN_ZIP = "D34F779322634BF35031B7A8594219B0AD1DD69296EF3637D23CBC97E7E99720" +PIN_ENTRY = "F9FDD13D889FF0323E20CE45532F1F9204FF3FB393BC17429A5C5CAECCCF129A" +PIN_ZIP = "DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index ab881ad4..b27db09c 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.5-rc1" PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "7C0F7D85EC86C0C956B13CDEC101D2A1BEEA750B2EDD0F4AF8E87DA8824334D4" +MANIFEST_SHA256 = "B4669C9DC6481A7DC9BC85FFD0039EB66353F8406BE93570B73676CC83660FFA" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From 206dde44bca0fc714850d92772012f80f6c00d61 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Tue, 20 Oct 2026 00:17:33 +0330 Subject: [PATCH 169/188] fix(security): validate pinned leaf basename before privileged fd open Limit protected source/receipt leaf filenames to explicit single-component ASCII [A-Za-z0-9][A-Za-z0-9_.-]{0,127} before descriptor-relative os.open. Retain O_NOFOLLOW/O_NONBLOCK, pinned parent dir_fd, fstat and fixed member SHA, plus per-stage random nonce. Add negative-first R795 filename regressions (control chars, whitespace, metacharacters, Unicode and dot-prefixed names) and include in hosted Linux CI. Regenerate deterministic private Linux RC1 zip/bootstrap manifest and refresh dependent Root/Guard/native test SHA pins. Local Emad Linux: 14/14 relevant suites PASS, including 28 Root fake-transaction tests, 17 package tests, 14 trusted-entry tests and 3 new leaf validation cases. This AI-assisted candidate is not a security approval, installed update or public release. Re-run exact-head original CI and independent CodeQL before accepting. --- .github/workflows/ci.yml | 25 +++++----- docs/SECURITY_TRUSTED_ENTRY_R781.md | 6 +-- linux/system/protected_upgrade_transaction.py | 9 +++- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r782_hosted_trusted_entry_ephemeral.py | 4 +- tests/r795_leaf_filename_contract.py | 49 +++++++++++++++++++ tools/security/privileged_entry.py | 2 +- 16 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 tests/r795_leaf_filename_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 713965be..86cc070e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" - assert p.is_file() and p.stat().st_size==8422000 + expected="0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" + assert p.is_file() and p.stat().st_size==8422459 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -86,6 +86,7 @@ jobs: python3 -B tests/r766_systemd_exec_origin_contract.py python3 -B tests/r767_consent_parent_nofollow_contract.py python3 -B tests/r769_root_file_descriptor_boundary_contract.py + python3 -B tests/r795_leaf_filename_contract.py python3 -B tests/r771_fifo_special_file_failclosed_contract.py python3 -B tests/r776_checked_fd_swap_regression.py python3 -B tests/r778_stage_backup_snapshot_race_contract.py @@ -178,7 +179,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -207,7 +208,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -240,7 +241,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -269,7 +270,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -298,7 +299,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -328,7 +329,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -373,7 +374,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -399,7 +400,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -648,8 +649,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' 'DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' 'F9FDD13D889FF0323E20CE45532F1F9204FF3FB393BC17429A5C5CAECCCF129A' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'E88794C9DA37D864BF44A027F1037B831C6771F06658067BAE0A1BEF55F3766B' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md index 1b9e3b44..e542edf7 100644 --- a/docs/SECURITY_TRUSTED_ENTRY_R781.md +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -10,13 +10,13 @@ This directory is deliberately **outside** the primary `/usr/lib/directinternetm The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: -`F9FDD13D889FF0323E20CE45532F1F9204FF3FB393BC17429A5C5CAECCCF129A` +`E88794C9DA37D864BF44A027F1037B831C6771F06658067BAE0A1BEF55F3766B` It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: -`B4669C9DC6481A7DC9BC85FFD0039EB66353F8406BE93570B73676CC83660FFA` +`63D5A6F117F97E27665A2F0AA74A5E8C1384C968009EA6522ADB10E39527F694` -The built Linux ZIP is **UNRELEASED**: SHA-256 `DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777` (8,422,000 bytes, 28 payload members). +The built Linux ZIP is **UNRELEASED**: SHA-256 `0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD` (8,422,459 bytes, 28 payload members). ## Exact sequence and guardrails diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index b75be2c9..81db8c86 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -79,8 +79,15 @@ def verified_regular_fd(path, *, native_receipt=False): (bool(entry.st_mode & stat.S_ISVTX) and stat.S_ISDIR(entry.st_mode))), "RECEIPT_PARENT_OWNER") + # Restrict the leaf to a single ASCII package/receipt filename. + # No separators, controls, shell metacharacters or dot-prefixed + # hidden names are allowed. The pinned parent descriptor remains the + # actual filesystem boundary, independent of this lexical check. + leaf = path.name + if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}", leaf) is None: + raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") try: - opened=os.open(path.name,flags,dir_fd=parent) + opened=os.open(leaf,flags,dir_fd=parent) except OSError as exc: raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") from exc st=os.fstat(opened) diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index da503be3..0b2bdfc3 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="6ABD10A709F68F4330B97CF903CB13E231CEB1B763AD9A378F422EA4F6A1E9A0" +PIN_ROOT="261EBB221B5957FCF236E8152930249FAA7F6F059CB178D648A86497614549E5" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index ae8f779e..e21fe8e6 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="6ABD10A709F68F4330B97CF903CB13E231CEB1B763AD9A378F422EA4F6A1E9A0" +TX_SHA="261EBB221B5957FCF236E8152930249FAA7F6F059CB178D648A86497614549E5" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" +GUARD_SHA="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index a88ab7d1..79f3e88f 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777"} +EXPECTED={"version":VERSION,"files":28,"sha256":"0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 40a3a1b7..d0cab299 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="dcb7b3a875466dfed664bfa0fa45eb2c57aaf6450161a898e4304f19f718e777" +CANDIDATE_SHA="0eff23f093eb334ad3c3fac98fde7d2dd721d4190e7c19c27748735c818b1add" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index d01cb8a5..9af87cfe 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "6abd10a709f68f4330b97cf903cb13e231ceb1b763ad9a378f422ea4f6a1e9a0" +ROOT_SHA = "261ebb221b5957fcf236e8152930249faa7f6f059cb178d648a86497614549e5" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index a02eab2a..5230e5f3 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "6abd10a709f68f4330b97cf903cb13e231ceb1b763ad9a378f422ea4f6a1e9a0" +PIN_ROOT = "261ebb221b5957fcf236e8152930249faa7f6f059cb178d648a86497614549e5" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index e768e2fe..7d08c224 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" +SOURCE_SHA="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index ac29f34e..914cd5c9 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" -PIN_ROOT="6ABD10A709F68F4330B97CF903CB13E231CEB1B763AD9A378F422EA4F6A1E9A0" -PIN_GUARD="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" +PIN_PACKAGE="0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" +PIN_ROOT="261EBB221B5957FCF236E8152930249FAA7F6F059CB178D648A86497614549E5" +PIN_GUARD="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 5c1e7817..f7cf9642 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="A4FE303415AE91DED41B2D718040C1A928179DD92BF0ABE601627C479F3DEDFD" +PIN_GUARD="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 4bf5768f..00ac1101 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" +ARCHIVE="0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index c3730187..5ad4c5b8 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,8 +18,8 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "F9FDD13D889FF0323E20CE45532F1F9204FF3FB393BC17429A5C5CAECCCF129A" -PIN_ZIP = "DCB7B3A875466DFED664BFA0FA45EB2C57AAF6450161A898E4304F19F718E777" +PIN_ENTRY = "E88794C9DA37D864BF44A027F1037B831C6771F06658067BAE0A1BEF55F3766B" +PIN_ZIP = "0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" diff --git a/tests/r795_leaf_filename_contract.py b/tests/r795_leaf_filename_contract.py new file mode 100644 index 00000000..2e0fbcbd --- /dev/null +++ b/tests/r795_leaf_filename_contract.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""R795 lexical input guard for pinned file leaf. Isolated, unprivileged, no network.""" +import importlib.util +from pathlib import Path +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] +sp=importlib.util.spec_from_file_location("dim_r795",ROOT/"linux/system/protected_upgrade_transaction.py") +m=importlib.util.module_from_spec(sp) +sp.loader.exec_module(m) + +class FilenameContract(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(prefix="dim-r795-user-",dir=str(ROOT)) + self.d=Path(self.tmp.name) + def tearDown(self): + self.tmp.cleanup() + def test_normal_known_name_still_opened(self): + p=self.d/"protected_install.json" + p.write_bytes(b"source identity") + with m.verified_regular_fd(p) as (fd,stat_result): + self.assertEqual(stat_result.st_size,15) + import os + self.assertEqual(os.read(fd,15),b"source identity") + def test_hostile_or_ambiguous_leaf_rejected(self): + for name in ("hidden file", ".private", "contains\nnewline", + "semicolon;name", "dollar$name", "wild*name", + "other\\slash", "unicodé", "a"*129): + with self.subTest(name=repr(name)): + p=self.d/name + p.write_bytes(b"foreign file") + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_FILE_COMPONENT"): + with m.verified_regular_fd(p): + self.fail("UNSAFE_LEAF_OPENED") + self.assertEqual(p.read_bytes(),b"foreign file") + def test_symlink_parent_still_rejected(self): + t=self.d/"actual" + t.mkdir() + p=t/"allowed_file" + p.write_bytes(b"okay") + link=self.d/"linkdir" + link.symlink_to(t,target_is_directory=True) + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_PARENT_COMPONENT"): + with m.verified_regular_fd(link/"allowed_file"): + self.fail("SYMLINK_PARENT_FOLLOWED") + +if __name__=="__main__": + unittest.main(verbosity=2) diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index b27db09c..c39a02d1 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.5-rc1" PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "B4669C9DC6481A7DC9BC85FFD0039EB66353F8406BE93570B73676CC83660FFA" +MANIFEST_SHA256 = "63D5A6F117F97E27665A2F0AA74A5E8C1384C968009EA6522ADB10E39527F694" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From b38aa94b67fb102dd24359184abaa324cd29a3ad Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Tue, 20 Oct 2026 00:27:54 +0330 Subject: [PATCH 170/188] fix(security): restrict Root file-read leaf to literal approved names (AI-assisted) R796 strengthens descriptor-pinned regular-file readers by selecting the basename only from a finite explicit allowlist of protected package, backend, receipt and synthetic-QA fixture names. Reject even lexically valid but unknown names; preserve symlink-parent and special-file fail-closed, SHA pins, ownership, 256-bit transaction nonce and existing native guard. A malicious string never selects an arbitrary os.open leaf. Expand R795 negative regression with safe-character yet nonallowlisted fixture, and update every dependent Root, Guard, trusted bootstrap and deterministic Linux RC1 archive digest. Local isolated Emad Linux: 15/15 relevant suites PASS incl R552, 28 protected fake-root tests, 17 package tests, R789 stale receipt rejection and native parser dry-run. Security candidate ONLY: do not infer CodeQL, independent human review, physical acceptance, signed/public release or privilege authorization from source tests. Protected main and public Stable v1.5.4 unchanged. --- .github/workflows/ci.yml | 24 ++++++------- docs/SECURITY_TRUSTED_ENTRY_R781.md | 6 ++-- linux/system/protected_upgrade_transaction.py | 35 +++++++++++++++++-- linux/system/upgrade_guard.py | 2 +- linux/system/upgrade_system.sh | 4 +-- tests/r519_versioned_package_contract.py | 2 +- ...r528_ephemeral_native_root_cli_contract.py | 2 +- .../r532_ephemeral_root_watchdog_contract.py | 2 +- tests/r532_root_watchdog_worker.py | 2 +- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 ++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- tests/r782_hosted_trusted_entry_ephemeral.py | 4 +-- tests/r795_leaf_filename_contract.py | 7 ++++ tools/security/privileged_entry.py | 2 +- 16 files changed, 71 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86cc070e..4a170355 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,8 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" - assert p.is_file() and p.stat().st_size==8422459 + expected="0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" + assert p.is_file() and p.stat().st_size==8424218 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" @@ -179,7 +179,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -208,7 +208,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -241,7 +241,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -270,7 +270,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -299,7 +299,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -329,7 +329,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -374,7 +374,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -400,7 +400,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -649,8 +649,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' '0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' 'E88794C9DA37D864BF44A027F1037B831C6771F06658067BAE0A1BEF55F3766B' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'C8AE0759FDF3A5EE514B5457D05AF1BFEF0BB266E03244C7EF2F3F6EDB8994D1' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/docs/SECURITY_TRUSTED_ENTRY_R781.md b/docs/SECURITY_TRUSTED_ENTRY_R781.md index e542edf7..3f3f984a 100644 --- a/docs/SECURITY_TRUSTED_ENTRY_R781.md +++ b/docs/SECURITY_TRUSTED_ENTRY_R781.md @@ -10,13 +10,13 @@ This directory is deliberately **outside** the primary `/usr/lib/directinternetm The source of this bootstrap is `tools/security/privileged_entry.py` in the exact reviewed commit. Its SHA-256 in the R781 test candidate is: -`E88794C9DA37D864BF44A027F1037B831C6771F06658067BAE0A1BEF55F3766B` +`C8AE0759FDF3A5EE514B5457D05AF1BFEF0BB266E03244C7EF2F3F6EDB8994D1` It freezes only the **single accepted** `v1.5.5-rc1` package, using a compiled-in manifest SHA-256: -`63D5A6F117F97E27665A2F0AA74A5E8C1384C968009EA6522ADB10E39527F694` +`E3D6186F8BC775FA7861DD506AA1EB978F6296BDC98D686CD3731C79680FEB42` -The built Linux ZIP is **UNRELEASED**: SHA-256 `0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD` (8,422,459 bytes, 28 payload members). +The built Linux ZIP is **UNRELEASED**: SHA-256 `0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934` (8,424,218 bytes, 28 payload members). ## Exact sequence and guardrails diff --git a/linux/system/protected_upgrade_transaction.py b/linux/system/protected_upgrade_transaction.py index 81db8c86..1bd2ccc6 100644 --- a/linux/system/protected_upgrade_transaction.py +++ b/linux/system/protected_upgrade_transaction.py @@ -45,6 +45,28 @@ "usr/lib/directinternetmethod/licenses/LICENSE-ctrld.txt":"55D82D13FE55D4B2AEC42166FCCD3C16C267B7131ECF2FBD7C3C6C3463494501", "usr/lib/directinternetmethod/licenses/LICENSE-zapret.txt":"DCF5ABD3E5D876C1065982871C0CEC7368C0E61FC795C541798729516BB6B54F", } +# Privileged file access is limited to these *literal* package/backend names. +# The caller chooses which entry, not a free-form basename. Parent traversal +# remains descriptor-pinned and rejects symlinks. Synthetic fixture names are +# explicitly enumerated; no arbitrary test-mode bypass exists. +APPROVED_FILE_LEAVES=( + "LICENSE-ctrld.txt", "LICENSE-zapret.txt", "acceptance.json", + "adult_hosts_fallback.txt", "control.sh", "ctrld", "direct_hosts.txt", + "direct_method_helper.sh", "directinternetmethod.uid", + "encrypted_controller", "encrypted_policy", "encrypted_selective_dns.py", + "encrypted_selective_policy.py", "helper", "install.json", + "manifest.json", "metadata", "nfqws", "selective_dns.py", + "strong_override_hosts.txt", "uninstall", "uninstall_system.sh", + "DIM_NATIVE_ROOT_ACCEPTANCE.json", + # Unprivileged and disposable-host QA receipts/member names: + "protected_install.json", "allowed_file", "payload", "large-static", + "regular-payload", "untrusted-package-member", + "first_attempt_receipt.json", "STALE_previous_attempt.json", + "current_attempt_receipt.json", "DIM_R562_TEST_ONLY_ACCEPTANCE.json", + "NO_SATISFACTORY_NATIVE_RUN.json", "r783_receipt_symlink.json", + "user-owned-test-receipt.json", "not-a-real-acceptance.json", + "NO_NATIVE_ACCEPTANCE_RECEIPT.json", "receipt.json", +) SCHEMA=1 def need(ok,why): @@ -83,8 +105,17 @@ def verified_regular_fd(path, *, native_receipt=False): # No separators, controls, shell metacharacters or dot-prefixed # hidden names are allowed. The pinned parent descriptor remains the # actual filesystem boundary, independent of this lexical check. - leaf = path.name - if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}", leaf) is None: + requested = path.name + if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}", requested) is None: + raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") + # Choose the descriptor leaf from literal trusted names only; the + # untrusted filename never becomes the filesystem-open argument. + leaf = None + for approved in APPROVED_FILE_LEAVES: + if requested == approved: + leaf = approved + break + if leaf is None: raise RuntimeError("FAIL_CLOSED_UNSAFE_FILE_COMPONENT") try: opened=os.open(leaf,flags,dir_fd=parent) diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 0b2bdfc3..056c5348 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -26,7 +26,7 @@ VERSION="1.5.5-rc1" BASELINE="1.5.2" OLD_USER="1.5.4" -PIN_ROOT="261EBB221B5957FCF236E8152930249FAA7F6F059CB178D648A86497614549E5" +PIN_ROOT="94C7D314809F582F437847C3B4F32F9FA8388E14526C138373974A70DEA0AFC8" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index e21fe8e6..e5a26430 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="261EBB221B5957FCF236E8152930249FAA7F6F059CB178D648A86497614549E5" +TX_SHA="94C7D314809F582F437847C3B4F32F9FA8388E14526C138373974A70DEA0AFC8" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -40,7 +40,7 @@ PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" +GUARD_SHA="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 79f3e88f..0aacbb47 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -15,7 +15,7 @@ INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD"} +EXPECTED={"version":VERSION,"files":28,"sha256":"0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index d0cab299..141f9209 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="0eff23f093eb334ad3c3fac98fde7d2dd721d4190e7c19c27748735c818b1add" +CANDIDATE_SHA="0c4d22f98c7c4ecea2cde1c0232b3c3928c022779ba8a644cd3dc46ff13ce934" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 9af87cfe..66156dce 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,7 +36,7 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "261ebb221b5957fcf236e8152930249faa7f6f059cb178d648a86497614549e5" +ROOT_SHA = "94c7d314809f582f437847c3b4f32f9fa8388e14526c138373974a70dea0afc8" USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def sha(p:Path)->str: diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 5230e5f3..a07aeae3 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,7 +25,7 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "261ebb221b5957fcf236e8152930249faa7f6f059cb178d648a86497614549e5" +PIN_ROOT = "94c7d314809f582f437847c3b4f32f9fa8388e14526c138373974a70dea0afc8" PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" def digest(path:Path): diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 7d08c224..aa32a12d 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" +SOURCE_SHA="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 914cd5c9..8089fbb4 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,9 +29,9 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" -PIN_ROOT="261EBB221B5957FCF236E8152930249FAA7F6F059CB178D648A86497614549E5" -PIN_GUARD="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" +PIN_PACKAGE="0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" +PIN_ROOT="94C7D314809F582F437847C3B4F32F9FA8388E14526C138373974A70DEA0AFC8" +PIN_GUARD="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index f7cf9642..ffe401c8 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="4F55CA969E49295D643B9AC9FF6D616F4324BF9FDCD815ED6024C3810586A5C0" +PIN_GUARD="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 00ac1101..9839b95e 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" +ARCHIVE="0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index 5ad4c5b8..d9c2db03 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,8 +18,8 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "E88794C9DA37D864BF44A027F1037B831C6771F06658067BAE0A1BEF55F3766B" -PIN_ZIP = "0EFF23F093EB334AD3C3FAC98FDE7D2DD721D4190E7C19C27748735C818B1ADD" +PIN_ENTRY = "C8AE0759FDF3A5EE514B5457D05AF1BFEF0BB266E03244C7EF2F3F6EDB8994D1" +PIN_ZIP = "0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" diff --git a/tests/r795_leaf_filename_contract.py b/tests/r795_leaf_filename_contract.py index 2e0fbcbd..2d9c8494 100644 --- a/tests/r795_leaf_filename_contract.py +++ b/tests/r795_leaf_filename_contract.py @@ -34,6 +34,13 @@ def test_hostile_or_ambiguous_leaf_rejected(self): with m.verified_regular_fd(p): self.fail("UNSAFE_LEAF_OPENED") self.assertEqual(p.read_bytes(),b"foreign file") + def test_regex_valid_but_non_allowlisted_name_rejected(self): + p=self.d/"unexpected-but-valid-name.txt" + p.write_bytes(b"foreign contents") + with self.assertRaisesRegex(RuntimeError,"FAIL_CLOSED_UNSAFE_FILE_COMPONENT"): + with m.verified_regular_fd(p): + self.fail("UNALLOWLISTED_FILE_OPENED") + self.assertEqual(p.read_bytes(),b"foreign contents") def test_symlink_parent_still_rejected(self): t=self.d/"actual" t.mkdir() diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index c39a02d1..bad7cfb2 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.5-rc1" PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "63D5A6F117F97E27665A2F0AA74A5E8C1384C968009EA6522ADB10E39527F694" +MANIFEST_SHA256 = "E3D6186F8BC775FA7861DD506AA1EB978F6296BDC98D686CD3731C79680FEB42" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From 65ff6497f45210883f4ff602fb693bd3b16d8405 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Tue, 20 Oct 2026 03:33:50 +0330 Subject: [PATCH 171/188] feat(linux): prepare 1.5.6 dual-baseline guarded release candidate AI-assisted technical candidate only; not independent review, native production acceptance or public release. Exact pinned predecessor pairs (Root/User 1.5.2/1.5.4 and 1.5.5-rc1/1.5.5-rc1), 256-bit receipt nonce, descriptor-pinned Root reads, owner checks and one bounded two-sided watchdog/rollback. Linux deterministic 28-file ZIP sha256 A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D and trusted 1.5.6 bootstrap sha256 D0181C15C54B90A3B6E03FBFBE32148341037AB60CF4D6788B6A72140304340F. Local 47/47 applicable Linux CI-source suites PASS; one Root-only suite and all hosted-native gates remain to run. README intentionally anonymizes physical test-host details. Existing Windows RC1, public Stable v1.5.4, protected main, PR #63 and personal installations unchanged. --- .github/workflows/ci.yml | 40 ++++---- build_linux_package.py | 10 +- docs/README_PACKAGE_156.md | 54 ++++++++++ linux/app/direct_internet_method.py | 6 +- linux/app/user_uninstall_preserve.py | 2 +- linux/app/user_update_transaction.py | 16 +-- linux/install.sh | 6 +- linux/system/install_system.sh | 4 +- linux/system/protected_upgrade_transaction.py | 52 +++++++--- linux/system/upgrade_guard.py | 54 ++++++---- linux/system/upgrade_system.sh | 22 +++-- ...pted_selective_dns_integration_contract.py | 2 +- .../linux_user_update_transaction_contract.py | 12 +-- .../r519_user_update_transaction_contract.py | 12 +-- tests/r519_versioned_package_contract.py | 38 +++---- tests/r526_installer_metadata_contract.py | 6 +- ...r528_ephemeral_native_root_cli_contract.py | 14 +-- tests/r529_ephemeral_root_user_transaction.py | 16 +-- .../r532_ephemeral_root_watchdog_contract.py | 12 +-- tests/r532_root_watchdog_worker.py | 4 +- tests/r552_native_upgrade_guard_contract.py | 33 ++++++- tests/r554_native_guard_ephemeral_contract.py | 20 ++-- .../r566_native_systemd_persistent_catchup.py | 2 +- tests/r567_hosted_denied_polkit_rollback.py | 2 +- ...oot_guard_source_copy_identity_contract.py | 2 +- tests/r781_privileged_entry_contract.py | 2 +- tests/r782_hosted_trusted_entry_ephemeral.py | 6 +- tests/r803_dual_baseline_forward_contract.py | 87 ++++++++++++++++ tests/r803_user_from_rc1_forward_contract.py | 98 +++++++++++++++++++ tests/tailscale_gui_scope_guard_contract.py | 6 +- tools/security/privileged_entry.py | 6 +- 31 files changed, 476 insertions(+), 170 deletions(-) create mode 100644 docs/README_PACKAGE_156.md create mode 100644 tests/r803_dual_baseline_forward_contract.py create mode 100644 tests/r803_user_from_rc1_forward_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a170355..5abe2e5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: Source and Release Contracts on: push: - branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa'] + branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa', 'prep/r803-forward-156-native-gates'] tags: ['v*'] pull_request: branches: [main] @@ -30,7 +30,7 @@ jobs: fi bash -n "$path" done < <(find linux -type f -name '*.sh' | sort) - - name: Build deterministic Linux 1.5.5-rc1 package + - name: Build deterministic Linux 1.5.6 package run: python3 build_linux_package.py - name: Exact candidate SHA and non-release authority gate shell: bash @@ -38,17 +38,17 @@ jobs: set -euo pipefail python3 - <<'PY' import hashlib,json,pathlib,zipfile - p=pathlib.Path("delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip") - expected="0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" - assert p.is_file() and p.stat().st_size==8424218 + p=pathlib.Path("delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip") + expected="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" + assert p.is_file() and p.stat().st_size==8427001 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" with zipfile.ZipFile(p) as z: - root="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" + root="DirectInternetMethod_1.5.6_Linux_x86_64/" assert len(z.namelist())==30 and len(z.namelist())==len(set(z.namelist())) j=json.loads(z.read(root+"PACKAGE_MANIFEST.json")) - assert j["version"]=="1.5.5-rc1" and len(j["files"])==28 + assert j["version"]=="1.5.6" and len(j["files"])==28 for x in j["files"]: data=z.read(root+x["file"]) assert len(data)==x["bytes"] @@ -63,6 +63,8 @@ jobs: python3 -B tests/r519_versioned_package_contract.py python3 -B tests/protected_upgrade_transaction_contract.py python3 -B tests/r789_receipt_replay_synthetic_probe.py + python3 -B tests/r803_dual_baseline_forward_contract.py + python3 -B tests/r803_user_from_rc1_forward_contract.py python3 -B tests/r519_user_update_transaction_contract.py - name: R526 Linux installer metadata activation (hermetic fake Root) shell: bash @@ -124,7 +126,7 @@ jobs: set -euo pipefail d="$(mktemp -d)" trap 'rm -rf "$d"' EXIT - unzip -q delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip -d "$d" + unzip -q delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip -d "$d" while IFS= read -r f; do bash -n "$f" if grep -q $'\r' "$f"; then @@ -179,7 +181,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -208,7 +210,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -241,7 +243,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -270,7 +272,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -299,7 +301,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -329,7 +331,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -374,7 +376,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -400,7 +402,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -636,7 +638,7 @@ jobs: name: linux-r782-root-owned-bootstrap-ephemeral # Actual UID 0 on a fresh original-repository GitHub VM ONLY. # Never run this job on a user's machine, a fork, or a tag release. - if: (github.event_name == 'workflow_dispatch' || github.event_name == 'push') && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/security/r776-pinned-root-read-qa' + if: (github.event_name == 'workflow_dispatch' || github.event_name == 'push') && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/prep/r803-forward-156-native-gates' runs-on: ubuntu-latest timeout-minutes: 10 permissions: @@ -649,8 +651,8 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' '0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934' 'delivery/DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip' | sha256sum --check --status - printf '%s %s\n' 'C8AE0759FDF3A5EE514B5457D05AF1BFEF0BB266E03244C7EF2F3F6EDB8994D1' 'tools/security/privileged_entry.py' | sha256sum --check --status + printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'D0181C15C54B90A3B6E03FBFBE32148341037AB60CF4D6788B6A72140304340F' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash run: | diff --git a/build_linux_package.py b/build_linux_package.py index 5eb58b15..e77daede 100644 --- a/build_linux_package.py +++ b/build_linux_package.py @@ -2,10 +2,10 @@ from __future__ import annotations import hashlib,json,pathlib,zipfile,tempfile,os R=pathlib.Path(__file__).resolve().parent -OUT=R/"delivery"/"DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" -ROOT="DirectInternetMethod_1.5.5-rc1_Linux_x86_64" +OUT=R/"delivery"/"DirectInternetMethod_1.5.6_Linux_x86_64.zip" +ROOT="DirectInternetMethod_1.5.6_Linux_x86_64" FILES=[ - ("docs/README_PACKAGE_155_RC1.md","README.md"), + ("docs/README_PACKAGE_156.md","README.md"), ("linux/install.sh","install.sh"), ("linux/uninstall.sh","uninstall.sh"), ("linux/system/install_system.sh","system/install_system.sh"), @@ -42,7 +42,7 @@ def payload_bytes(src:str,dst:str)->bytes: data=data.replace(b"\r\n",b"\n").replace(b"\r",b"\n") return data # Frozen originally shipped documentation; root README may describe current publication. -if sha_bytes(payload_bytes("docs/README_PACKAGE_155_RC1.md","README.md"))!="2E2D817FD4728903E7085C4D7D02786107206881DA47FE4DFA6AA3C6F4E4AF76": +if sha_bytes(payload_bytes("docs/README_PACKAGE_156.md","README.md"))!="C836C617B05FCC01FD384BDF8A31BE5F51DF0335734A22C3953BEF8D594299D5": raise SystemExit("R519_PACKAGE_README_SHA_CHANGED") rows=[] for src,dst in FILES: @@ -50,7 +50,7 @@ def payload_bytes(src:str,dst:str)->bytes: if not p.is_file(): raise SystemExit("MISSING:"+src) data=payload_bytes(src,dst) rows.append({"file":dst,"bytes":len(data),"sha256":sha_bytes(data)}) -manifest={"schema":1,"product":"Direct Internet Method","version":"1.5.5-rc1","platform":"linux-x86_64","files":rows} +manifest={"schema":1,"product":"Direct Internet Method","version":"1.5.6","platform":"linux-x86_64","files":rows} OUT.parent.mkdir(parents=True,exist_ok=True) fd,tmp_name=tempfile.mkstemp(prefix="dim-build-",suffix=".zip",dir=OUT.parent) os.close(fd) diff --git a/docs/README_PACKAGE_156.md b/docs/README_PACKAGE_156.md new file mode 100644 index 00000000..5efc23bd --- /dev/null +++ b/docs/README_PACKAGE_156.md @@ -0,0 +1,54 @@ +# Direct Internet Method v1.5.6 — Linux release-preparation candidate (NOT PUBLISHED) + +This package is a **private validation candidate**, not a second public product +line. The current public Stable remains v1.5.4 until a separate authorized +GitHub release establishes exact asset identities and accepted test receipts. + +## Supported single-install upgrade paths + +- Public Linux User 1.5.4 with protected Root backend 1.5.2. +- Previously owner-accepted private Linux User 1.5.5-rc1 with protected Root + backend 1.5.5-rc1. +- No mixed, unknown, modified, symlinked, or partially installed Root/User pair + is silently upgraded. Existing custom Router Gateway data, settings, + Tailscale, DNS/physical route, and one original installation must survive. +- Windows private installations have FileVersion 1.5.5.0. Public v1.5.5 is + **not** a forward numeric upgrade for their existing updater; v1.5.6 is the + first normal three-component version that can update them directly. + +## Security and recovery + +Privileged protected-file reads accept only literal approved filenames with +descriptor-based no-symlink traversal, pinned SHA-256 payloads, regular-file +identity, and verified original ownership. A per-transaction random acceptance +nonce prevents replay of previous Root acceptance receipts. The new +version-specific trusted privileged bootstrap must be installed only through +normal native administrator authorization after verifying its reviewed digest. + +The Root+User maintenance transaction has one private rollback slot; a separate +Root-owned watchdog is armed before protected mutation and must be settled with +real Start, Stop, Recovery, original DNS/Tailscale checks and user settings +hashes before release. A preliminary staged/apply or hosted CI PASS alone does +not authorize physical deployment or publication. Do not disable host security +policies, reboot, modify default routes, reset DNS, or automatically install +a VPN or global tunnel. + +## Known acceptance limits + +Some physical networks reset YouTube TLS depending on SNI even when +public Google IPs are reachable. Site acceptance must be verified on the +intended network; no universal-connectivity claim is made. The current +physical network is not a verified universal connectivity target. Historical +plain `curl` HTTP403 and isolated headless browser timeout do **not** prove +ChatGPT account sign-in. Optional encrypted selective-DNS needs explicit, +separate user consent, and its controller remains restricted to safe native +rollback and Tailscale coexistence. + +Current exact source starting point: primary PR #63 commit +`b38aa94b67fb102dd24359184abaa324cd29a3ad`, plus separately scoped +R803 forward-upgrade migration work. The new candidate's final Git SHA, +package digest, hosted Linux/Windows acceptance and release decision remain +UNVERIFIED until recorded on the exact generated artifacts. + +Public v1.5.4 release and protected `main` remain unchanged. This document is +a package-level statement of intended behavior, not a claim of publication. diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 0a906d2c..6e768bb1 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -25,7 +25,7 @@ from socket_fallback import LocalSocketFallback APP_ID="io.github.god13emad.DirectInternetMethod" -VERSION="1.5.5-rc1" +VERSION="1.5.6" GLib.set_prgname("DirectInternetMethod") GLib.set_application_name("Direct Internet Method") try: @@ -184,7 +184,7 @@ def sync_adult_catalog(force=False): last_error=None for source_url in ADULT_CATALOG_URLS: try: - req=urllib.request.Request(source_url,headers={"User-Agent":"DirectInternetMethod/1.5.5-rc1"}) + req=urllib.request.Request(source_url,headers={"User-Agent":"DirectInternetMethod/1.5.6"}) with urllib.request.urlopen(req,timeout=20) as resp: raw=resp.read(4*1024*1024+1) if len(raw)<100000 or len(raw)>4*1024*1024: @@ -338,7 +338,7 @@ def _coexistence_backend_ready(): not helper.is_file() or not meta.is_file()): return False installed=json.loads(meta.read_text(encoding="utf-8")) - return (installed.get("version") == "1.5.5-rc1" and + return (installed.get("version") == "1.5.6" and installed.get("uid")==os.getuid() and hashlib.sha256(helper.read_bytes()).hexdigest()==COEXIST_HELPER_SHA256) except Exception: diff --git a/linux/app/user_uninstall_preserve.py b/linux/app/user_uninstall_preserve.py index f87b833e..7d07e88d 100644 --- a/linux/app/user_uninstall_preserve.py +++ b/linux/app/user_uninstall_preserve.py @@ -51,7 +51,7 @@ def load_owned(): meta = app / "INSTALL.json" ensure(meta.is_file() and not meta.is_symlink(), "UNINSTALL_INSTALL_METADATA_MISSING") doc = json.loads(meta.read_text(encoding="utf8")) - ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.5-rc1", + ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.6", "UNINSTALL_MANIFEST_OWNER_VERSION_UNVERIFIED") codes = doc.get("managedCodeSha256") launchers = doc.get("managedLaunchersSha256") diff --git a/linux/app/user_update_transaction.py b/linux/app/user_update_transaction.py index 14004440..cb85c0f5 100644 --- a/linux/app/user_update_transaction.py +++ b/linux/app/user_update_transaction.py @@ -137,7 +137,7 @@ def source_files(root: pathlib.Path) -> dict: """ guard(root.is_absolute() and root.is_dir() and not root.is_symlink() and not root.parent.is_symlink(), "SOURCE_PACKAGE_INVALID") - archive_layout = root.name == "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" + archive_layout = root.name == "DirectInternetMethod_1.5.6_Linux_x86_64" source_layout = root.name == "linux" guard(archive_layout or source_layout, "SOURCE_PACKAGE_LAYOUT_NOT_LINUX_OR_EXTRACTED_ZIP") if archive_layout: @@ -152,7 +152,7 @@ def source_files(root: pathlib.Path) -> dict: raise RuntimeError("EXTRACTED_PACKAGE_MANIFEST_INVALID_JSON") from ex guard(isinstance(package_doc, dict) and package_doc.get("schema") == 1 and package_doc.get("product") == "Direct Internet Method" and - package_doc.get("version") == "1.5.5-rc1" and + package_doc.get("version") == "1.5.6" and package_doc.get("platform") == "linux-x86_64" and isinstance(package_doc.get("files"), list) and len(package_doc["files"]) == 28, "EXTRACTED_PACKAGE_IDENTITY_WRONG") @@ -277,7 +277,7 @@ def stage(src: pathlib.Path): try: (pending / "old").mkdir(mode=0o700) manifest = { - "schema": 1, "version": "1.5.5-rc1", + "schema": 1, "version": "1.5.6", "managed": { rel: {"sha": row["sha"], "mode": row["mode"]} for rel, row in payload.items() @@ -312,7 +312,7 @@ def load_pending(): not manifest_file.is_symlink(), "PENDING_TRANSACTION_NOT_VALID") guard(pending.stat().st_uid == os.getuid(), "PENDING_TRANSACTION_NOT_OWNED") data = json.loads(manifest_file.read_text(encoding="utf8")) - guard(data.get("schema") == 1 and data.get("version") == "1.5.5-rc1", "PENDING_TRANSACTION_VERSION_WRONG") + guard(data.get("schema") == 1 and data.get("version") == "1.5.6", "PENDING_TRANSACTION_VERSION_WRONG") allowed = {METADATA, *(rel for _, rel, _ in FILES), PROVIDERS} guard(set(data["old"]) == set(data["managed"]) | {METADATA} and set(data["old"]) <= allowed, "PENDING_FILESET_FOREIGN") @@ -378,7 +378,7 @@ def check_metadata(app, data): path = app / METADATA guard(path.is_file() and not path.is_symlink(), "INSTALLED_METADATA_MISSING") doc = json.loads(path.read_text(encoding="utf8")) - guard(doc.get("version") == "1.5.5-rc1" and doc.get("schema") == 2, "INSTALLED_VERSION_METADATA_NOT_VALID") + guard(doc.get("version") == "1.5.6" and doc.get("schema") == 2, "INSTALLED_VERSION_METADATA_NOT_VALID") guard(doc.get("managedProviderSha256") == data["providerSha"], "PROVIDER_MANAGED_OWNERSHIP_METADATA_CHANGED") expected = {rel: row["sha"] for rel, row in data["managed"].items() if rel != PROVIDERS} @@ -421,7 +421,7 @@ def rollback(): if rel == METADATA and observed not in (info["sha"], None): try: doc = json.loads(dest.read_text(encoding="utf8")) - guard(doc.get("schema") == 2 and doc.get("version") == "1.5.5-rc1", + guard(doc.get("schema") == 2 and doc.get("version") == "1.5.6", "FOREIGN_INSTALL_METADATA_PRESERVED") except (UnicodeError, ValueError) as ex: raise RuntimeError("UNKNOWN_INSTALL_METADATA_ON_ROLLBACK") from ex @@ -515,11 +515,11 @@ def recover_metadata_stage(expected_sha256: str): } guard(set(doc) == expected_keys and doc["schema"] == 2 and doc["product"] == "Direct Internet Method" and - doc["version"] == "1.5.5-rc1" and doc["platform"] == "linux-x86_64" and + doc["version"] == "1.5.6" and doc["platform"] == "linux-x86_64" and doc["networkMutationOnInstall"] is False and doc["normalRunRequiresAdmin"] is False and doc["systemBackend"] == "/usr/lib/directinternetmethod" and - doc["protectedBackendVersion"] == "1.5.5-rc1" and + doc["protectedBackendVersion"] == "1.5.6" and doc["protectedBackendByteCompatible"] is True and doc["onlineUpdate"] == "GitHub latest release + SHA256SUMS verification" and doc["updateMode"] == "IN_PLACE_SINGLE_INSTALL_DATA_PRESERVED", diff --git a/linux/install.sh b/linux/install.sh index 4a85b12a..d687917c 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -75,7 +75,7 @@ backend_matches_current() { python3 - /var/lib/directinternetmethod/install.json "$uid" <<'PY' >/dev/null 2>&1 || return 1 import json,sys d=json.load(open(sys.argv[1],encoding="utf-8")) -raise SystemExit(0 if d.get("version")=="1.5.5-rc1" and str(d.get("uid"))==sys.argv[2] else 1) +raise SystemExit(0 if d.get("version")=="1.5.6" and str(d.get("uid"))==sys.argv[2] else 1) PY [ "$(cat /etc/directinternetmethod.uid 2>/dev/null || true)" = "$uid" ] || return 1 for action in start stop recovery; do @@ -229,11 +229,11 @@ for name, expected in expected_launchers.items(): launchers[name]=hashlib.sha256(expected).hexdigest() transaction=json.loads((pathlib.Path.home()/".local/state/DirectInternetMethod/update-pending/manifest.json").read_text(encoding="utf8")) protected=json.loads(pathlib.Path("/var/lib/directinternetmethod/install.json").read_text(encoding="utf-8")) -assert protected.get("version") == "1.5.5-rc1" +assert protected.get("version") == "1.5.6" metadata=json.dumps({ "schema":2, "product":"Direct Internet Method", - "version":"1.5.5-rc1", + "version":"1.5.6", "platform":"linux-x86_64", "installedUtc":time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime()), "networkMutationOnInstall":False, diff --git a/linux/system/install_system.sh b/linux/system/install_system.sh index 42b3281d..4610bed8 100644 --- a/linux/system/install_system.sh +++ b/linux/system/install_system.sh @@ -108,7 +108,7 @@ test -f /etc/polkit-1/rules.d/49-directinternetmethod.rules test "$(cat /etc/directinternetmethod.uid)" = "$USER_UID" install -d -m 0755 /var/lib/directinternetmethod cat > /var/lib/directinternetmethod/install.json <0 and uid<2**31,"OWNER_UID") need(re.fullmatch(r"[a-z_][a-z0-9_-]*[$]?",str(d.get("user",""))),"OWNER_USER") @@ -247,10 +255,11 @@ def _baseline(self): need(hash_file(p)==want,"BASELINE_STATIC_SHA_"+pathlib.Path(rel).name) for name,src,target,old,new,mode in FILES: p=self.p(target) - if old: + want=baseline_old_sha(before["version"],old,new) + if want: s=no_symlink_regular(p,self.native) need(stat.S_IMODE(s.st_mode)==mode,"OLD_FILE_MODE_"+name) - need(hash_file(p)==old,"OLD_FILE_SHA_"+name) + need(hash_file(p)==want,"OLD_FILE_SHA_"+name) else: need(not p.exists() and not p.is_symlink(),"NEW_TARGET_NOT_EMPTY_"+name) return before @@ -288,7 +297,7 @@ def manifest(self): data=json.loads(checked_regular_bytes( p,owner=self.native,max_bytes=131072).decode("utf8")) need(data.get("schema")==SCHEMA and data.get("revision")=="R519" and - data.get("fromVersion")==CURRENT_BACKEND_VERSION and + data.get("fromVersion") in SUPPORTED_BASELINE_VERSIONS and data.get("toVersion")==TARGET_VERSION,"MANIFEST_VERSION") need(data.get("phase") in ("STAGED","APPLIED","ROLLED_BACK","DEGRADED","COMMITTED"), "MANIFEST_PHASE") @@ -327,11 +336,12 @@ def stage(self,source): # _baseline(), rather than retaining an untrusted backup. originals={} for name,rel,target,old,new,mode in FILES: - if old: + want=baseline_old_sha(before["version"],old,new) + if want: old_blob=checked_regular_bytes( self.p(target),owner=self.native,mode=mode, max_bytes=1048576) - need(sha(old_blob)==old,"BASELINE_BACKUP_CHANGED_"+name) + need(sha(old_blob)==want,"BASELINE_BACKUP_CHANGED_"+name) originals[name]=old_blob orig=checked_regular_bytes( self.meta,owner=self.native,mode=0o644,max_bytes=65536) @@ -343,16 +353,17 @@ def stage(self,source): try: entries={} for name,rel,target,old,new,mode in FILES: + want=baseline_old_sha(before["version"],old,new) atomic_bytes(self.pending/"new"/name,data[name],0o600,self.native) - if old: + if want: atomic_bytes(self.pending/"old"/name,originals[name],0o600,self.native) - entries[name]={"target":target,"old":old,"new":new,"mode":mode} + entries[name]={"target":target,"old":want,"new":new,"mode":mode} atomic_bytes(self.pending/"old"/"metadata",orig,0o600,self.native) new_meta=dict(before,version=TARGET_VERSION) new_blob=canonical(new_meta) atomic_bytes(self.pending/"new"/"metadata",new_blob,0o600,self.native) payload={"schema":SCHEMA,"revision":"R519","phase":"STAGED", - "fromVersion":CURRENT_BACKEND_VERSION,"toVersion":TARGET_VERSION, + "fromVersion":before["version"],"toVersion":TARGET_VERSION, "files":entries,"oldMetadataSHA":sha(orig),"newMetadataSHA":sha(new_blob), "uid":before["uid"],"home":before["home"],"user":before["user"], "transactionNonce":os.urandom(32).hex()} @@ -364,14 +375,15 @@ def stage(self,source): def _validate_slot(self,m): need(self.pending.is_dir() and not self.pending.is_symlink(),"SLOT_UNSAFE") for name,rel,target,old,new,mode in FILES: + want=baseline_old_sha(m["fromVersion"],old,new) x=m["files"][name] - need(x=={"target":target,"old":old,"new":new,"mode":mode},"MANIFEST_FILE_TAMPER_"+name) + need(x=={"target":target,"old":want,"new":new,"mode":mode},"MANIFEST_FILE_TAMPER_"+name) nf=self.pending/"new"/name need(no_symlink_regular(nf,self.native) and hash_file(nf)==new, "PENDING_NEW_SHA_"+name) - if old: + if want: backup=self.pending/"old"/name - need(no_symlink_regular(backup,self.native) and hash_file(backup)==old, + need(no_symlink_regular(backup,self.native) and hash_file(backup)==want, "PENDING_OLD_SHA_"+name) for key,dir,want in (("metadata","old",m["oldMetadataSHA"]), ("metadata","new",m["newMetadataSHA"])): @@ -388,7 +400,7 @@ def _target_states(self,m): else: no_symlink_regular(file,self.native) digest=hash_file(file) - need(digest in (old,new),"FOREIGN_TARGET_MODIFICATION_"+name) + need(digest in (baseline_old_sha(m["fromVersion"],old,new),new),"FOREIGN_TARGET_MODIFICATION_"+name) result[name]=digest no_symlink_regular(self.meta,self.native) meta_sha=hash_file(self.meta) @@ -402,11 +414,16 @@ def apply(self,fail_after=None): self._validate_slot(m) before,meta=self._target_states(m) for name,rel,target,old,new,mode in FILES: - need(before[name]==old,"BASELINE_CHANGED_"+name) + need(before[name]==baseline_old_sha(m["fromVersion"],old,new), + "BASELINE_CHANGED_"+name) need(meta==m["oldMetadataSHA"],"OLD_META_CHANGED") changed=0 try: for name,rel,target,old,new,mode in FILES: + # A verified RC1 backend is already byte-identical. Only + # metadata advances, without rewriting active program inodes. + if before[name]==new: + continue atomic_bytes(self.p(target),(self.pending/"new"/name).read_bytes(), mode,self.native) changed+=1 @@ -432,9 +449,11 @@ def _rollback_locked(self,m): states,metadata=self._target_states(m) # Verify ALL targets before a single mutation. for name,rel,target,old,new,mode in FILES: + old=baseline_old_sha(m["fromVersion"],old,new) need(states[name] in (old,new),"FOREIGN_TARGET_"+name) for name,rel,target,old,new,mode in FILES: - if states[name]==new: + old=baseline_old_sha(m["fromVersion"],old,new) + if states[name]==new and old!=new: p=self.p(target) if old is None: need(not p.is_symlink() and hash_file(p)==new,"FOREIGN_NEW_"+name) @@ -447,6 +466,7 @@ def _rollback_locked(self,m): 0o644,self.native) for name,rel,target,old,new,mode in FILES: p=self.p(target) + old=baseline_old_sha(m["fromVersion"],old,new) if old:need(hash_file(p)==old,"ROLLBACK_SHA_"+name) else:need(not p.exists(),"ROLLBACK_NEW_REMAINS_"+name) need(hash_file(self.meta)==m["oldMetadataSHA"],"ROLLBACK_METADATA_SHA") diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 056c5348..c0a20592 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -23,11 +23,12 @@ import sys import time -VERSION="1.5.5-rc1" -BASELINE="1.5.2" -OLD_USER="1.5.4" -PIN_ROOT="94C7D314809F582F437847C3B4F32F9FA8388E14526C138373974A70DEA0AFC8" -PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" +VERSION="1.5.6" +BASELINE="1.5.2" # legacy accepted baseline +OLD_USER="1.5.4" # legacy accepted baseline +ALLOWED_BASELINE_PAIRS=(("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1")) +PIN_ROOT="7147AF90E6DC0B29A6CE15A84656CE9587060A7F5F343E7B16281C5602FC693C" +PIN_USER="5077ACE07B27DDBCBEAA4DE56ED718F7FD26934FF65A13251CCEAFA9DD2FEB50" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" RESULT=GUARD/"rollback-result.json" @@ -118,7 +119,9 @@ def checked_owner(): p=Path("/var/lib/directinternetmethod/install.json") regular(p,0,0o644) obj=json.loads(p.read_text(encoding="utf8")) - need(obj.get("schema")==1 and obj.get("version")==BASELINE,"PROTECTED_BASELINE") + need(obj.get("schema")==1 and + obj.get("version") in (v[0] for v in ALLOWED_BASELINE_PAIRS), + "PROTECTED_BASELINE") uid=obj.get("uid");user=obj.get("user");home=obj.get("home") need(type(uid) is int and 00,"BASELINE_DEADLINE") @@ -339,7 +355,7 @@ def finish_accepted_pair(j,tx,home:Path,pending:Path): """ m=tx.manifest() need(m.get("phase")=="COMMITTED" and m.get("toVersion")==VERSION and - m.get("fromVersion")==BASELINE and m.get("uid")==j["uid"] and + m.get("fromVersion")==j["versionFromRoot"] and m.get("uid")==j["uid"] and m.get("home")==str(home),"ROOT_ACCEPTANCE_MANIFEST") tx._validate_slot(m) states,meta=tx._target_states(m) @@ -428,7 +444,7 @@ def rollback_worker(): "ROLLEDBACK_PRIOR_RECEIPT_NOT_SUCCESSFUL") m=tx.manifest() need(m.get("phase")=="ROLLED_BACK" and - m.get("fromVersion")==BASELINE and + m.get("fromVersion")==j["versionFromRoot"] and m.get("toVersion")==VERSION and m.get("uid")==j["uid"] and m.get("home")==j["home"] and @@ -527,7 +543,7 @@ def retire_rolledback(receipt_sha256:str): "RETIRE_NONTERMINAL_ROOT") m=tx.manifest() need(m.get("phase")=="ROLLED_BACK" and m.get("uid")==j["uid"] and - m.get("home")==j["home"] and m.get("fromVersion")==BASELINE and + m.get("home")==j["home"] and m.get("fromVersion")==j["versionFromRoot"] and m.get("toVersion")==VERSION,"RETIRE_FOREIGN_ROOT_TRANSACTION") tx._validate_slot(m) states,metadata=tx._target_states(m) @@ -601,7 +617,7 @@ def retire_rolledback(receipt_sha256:str): "RETIRE_DNS_CHANGED") return {"status":"PASS_AUTHORIZED_BOUNDED_ROLLBACK_RETIRE", "receiptSha256":digest, - "oldRootVersion":BASELINE,"oldUserVersion":OLD_USER, + "oldRootVersion":j["versionFromRoot"],"oldUserVersion":j["versionFromUser"], "fileScopesExact":True,"networkMutationAttempted":False, "timerDisarmed":True,"repeatUpgradeUnblocked":True} diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index e5a26430..e2b19622 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,14 +6,14 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="94C7D314809F582F437847C3B4F32F9FA8388E14526C138373974A70DEA0AFC8" +TX_SHA="7147AF90E6DC0B29A6CE15A84656CE9587060A7F5F343E7B16281C5602FC693C" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 [ -d "$SRC" ] && [ ! -L "$SRC" ] && [ -f "$SRC/system/protected_upgrade_transaction.py" ] || { echo "UPGRADE_SOURCE_MISSING_OR_SYMLINK" >&2; exit 77; } -[ "$(basename "$SRC")" = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" ] || { +[ "$(basename "$SRC")" = "DirectInternetMethod_1.5.6_Linux_x86_64" ] || { echo "WRONG_PACKAGE_LAYOUT" >&2; exit 77; } owner_uid="$(stat -c %u -- "$SRC")" @@ -34,13 +34,13 @@ assert p.is_file() and not p.is_symlink() s=p.stat() assert s.st_uid==0 and stat.S_IMODE(s.st_mode)==0o644 m=json.loads(p.read_text('utf8')) -assert m['schema']==1 and m['version']=='1.5.2' and m['uid']==expected +assert m['schema']==1 and m['version'] in ('1.5.2','1.5.5-rc1') and m['uid']==expected assert m['home'].startswith('/home/') PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" +GUARD_SHA="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } @@ -100,13 +100,15 @@ try: data=json.loads((guard/"baseline.json").read_text("utf8")) assert data["schema"]==1 and data["revision"]=="R552" assert data["uid"]==uid and metadata["uid"]==uid and data["home"]==str(home) - assert data["versionFromRoot"]=="1.5.2" and data["versionFromUser"]=="1.5.4" - assert data["versionTo"]=="1.5.5-rc1" + assert (data["versionFromRoot"],data["versionFromUser"]) in ( + ("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1")) + assert metadata["version"]==data["versionFromRoot"] + assert data["versionTo"]=="1.5.6" assert data["oldRootMetaSha"]==sha(root) assert data["oldUserMetaSha"]==sha(home/".local/share/DirectInternetMethod/INSTALL.json") assert data["rootTransactionSha"]==root_sha assert sha(guard/"root_tx.py")==root_sha - assert sha(guard/"user_tx.py")=="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" + assert sha(guard/"user_tx.py")=="5077ACE07B27DDBCBEAA4DE56ED718F7FD26934FF65A13251CCEAFA9DD2FEB50" assert data["userTransactionSha"]==sha(guard/"user_tx.py") assert data["workerSha"]==sha(guard/"worker.py") pending=home/".local/state/DirectInternetMethod/update-pending" @@ -114,8 +116,8 @@ try: pm=pending/"manifest.json" assert pm.is_file() and not pm.is_symlink() and pm.stat().st_uid==uid stage=json.loads(pm.read_text("utf8")) - assert stage["schema"]==1 and stage["version"]=="1.5.5-rc1" - assert stage["previousVersion"]=="1.5.4" + assert stage["schema"]==1 and stage["version"]=="1.5.6" + assert stage["previousVersion"]==data["versionFromUser"] old=stage["old"]["INSTALL.json"] assert old["exists"] is True and str(old["sha"]).upper()==data["oldUserMetaSha"] for ext in (".service",".timer"): @@ -139,5 +141,5 @@ GUARD_ROOT_TX="/var/lib/directinternetmethod/upgrade-guard/root_tx.py" python3 -B "$GUARD_ROOT_TX" stage --source "$SRC" python3 -B "$GUARD_ROOT_TX" apply python3 -B "$GUARD_ROOT_TX" status | - python3 -c 'import json,sys;j=json.load(sys.stdin);assert j["status"]=="APPLIED" and j["version"]=="1.5.5-rc1"' + python3 -c 'import json,sys;j=json.load(sys.stdin);assert j["status"]=="APPLIED" and j["version"]=="1.5.6"' echo '{"status":"ROOT_R519_APPLIED_UNCOMMITTED","needsRealNativeAcceptance":true}' diff --git a/tests/encrypted_selective_dns_integration_contract.py b/tests/encrypted_selective_dns_integration_contract.py index b175ab59..2946f649 100644 --- a/tests/encrypted_selective_dns_integration_contract.py +++ b/tests/encrypted_selective_dns_integration_contract.py @@ -106,7 +106,7 @@ def test_controller_checks_root_owned_process_sha_before_commit(self): 'NATIVE_NFQWS_PID_MISMATCH','NATIVE_NFQWS_PROCESS_FOREIGN'): self.assertIn(text,CONTROLLER) def test_r519_candidate_is_explicit_unreleased_versioned_bundle(self): - self.assertIn('DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip',B) + self.assertIn('DirectInternetMethod_1.5.6_Linux_x86_64.zip',B) self.assertIn('linux/system/encrypted_selective_dns.py',B) self.assertIn('linux/system/encrypted_selective_policy.py',B) self.assertIn('linux/system/protected_upgrade_transaction.py',B) diff --git a/tests/linux_user_update_transaction_contract.py b/tests/linux_user_update_transaction_contract.py index 79298cef..aa860c00 100644 --- a/tests/linux_user_update_transaction_contract.py +++ b/tests/linux_user_update_transaction_contract.py @@ -91,7 +91,7 @@ def metadata(home, source): app = home / ".local/share/DirectInternetMethod" manifest = json.loads((home / ".local/state/DirectInternetMethod/update-pending/manifest.json").read_text()) new = { - "schema": 2, "version": "1.5.4", + "schema": 2, "version": "1.5.6", "managedProviderSha256": manifest["providerSha"], "managedCodeSha256": {k:v["sha"] for k,v in manifest["managed"].items() if k != tx.PROVIDERS}, @@ -154,7 +154,7 @@ def unchanged_data(app, user): pending_manifest = home3 / ".local/state/DirectInternetMethod/update-pending/manifest.json" pending_original = pending_manifest.read_bytes() corrupted = json.loads(pending_original) - need(corrupted["version"] == "1.5.4", "STAGE_NOT_VERSIONED_154") + need(corrupted["version"] == "1.5.6", "STAGE_NOT_VERSIONED_154") corrupted["version"] = "1.5.2" pending_manifest.write_text(json.dumps(corrupted), encoding="utf8") refused = calls(home3, src, "--apply", False) @@ -369,7 +369,7 @@ def unchanged_data(app, user): "router_gateway/providers.json": {"sha": "c" * 64}}, })) protected = sandbox / "fake-protected-backend.json" - protected.write_text(json.dumps({"version": "1.5.2"})) + protected.write_text(json.dumps({"version": "1.5.6"})) old_metadata = b'{"schema":2,"version":"1.5.1"}\n' metadata_path = fake_app / "INSTALL.json" metadata_path.write_bytes(old_metadata) @@ -418,7 +418,7 @@ def run_inline(): accepted = run_inline() need(accepted.returncode == 0, "F415_ATOMIC_METADATA_WRITE_FAILED:" + accepted.stderr) doc = json.loads(metadata_path.read_text(encoding="utf-8")) - need(doc["version"] == "1.5.4" and doc["schema"] == 2 + need(doc["version"] == "1.5.6" and doc["schema"] == 2 and doc["managedCodeSha256"] == {"direct_internet_method.py": "b" * 64} and doc["preservedCustomProviderData"] is True, "F415_METADATA_POSTCONDITION_INVALID") @@ -446,13 +446,13 @@ def run_inline(): doc = { "schema": 2, "product": "Direct Internet Method", - "version": "1.5.4", + "version": "1.5.6", "platform": "linux-x86_64", "installedUtc": "2026-10-12T00:00:00Z", "networkMutationOnInstall": False, "normalRunRequiresAdmin": False, "systemBackend": "/usr/lib/directinternetmethod", - "protectedBackendVersion": "1.5.2", + "protectedBackendVersion": "1.5.6", "protectedBackendByteCompatible": True, "onlineUpdate": "GitHub latest release + SHA256SUMS verification", "managedProviderSha256": manifest["providerSha"], diff --git a/tests/r519_user_update_transaction_contract.py b/tests/r519_user_update_transaction_contract.py index 9729cf6d..aa860c00 100644 --- a/tests/r519_user_update_transaction_contract.py +++ b/tests/r519_user_update_transaction_contract.py @@ -91,7 +91,7 @@ def metadata(home, source): app = home / ".local/share/DirectInternetMethod" manifest = json.loads((home / ".local/state/DirectInternetMethod/update-pending/manifest.json").read_text()) new = { - "schema": 2, "version": "1.5.5-rc1", + "schema": 2, "version": "1.5.6", "managedProviderSha256": manifest["providerSha"], "managedCodeSha256": {k:v["sha"] for k,v in manifest["managed"].items() if k != tx.PROVIDERS}, @@ -154,7 +154,7 @@ def unchanged_data(app, user): pending_manifest = home3 / ".local/state/DirectInternetMethod/update-pending/manifest.json" pending_original = pending_manifest.read_bytes() corrupted = json.loads(pending_original) - need(corrupted["version"] == "1.5.5-rc1", "STAGE_NOT_VERSIONED_154") + need(corrupted["version"] == "1.5.6", "STAGE_NOT_VERSIONED_154") corrupted["version"] = "1.5.2" pending_manifest.write_text(json.dumps(corrupted), encoding="utf8") refused = calls(home3, src, "--apply", False) @@ -369,7 +369,7 @@ def unchanged_data(app, user): "router_gateway/providers.json": {"sha": "c" * 64}}, })) protected = sandbox / "fake-protected-backend.json" - protected.write_text(json.dumps({"version": "1.5.5-rc1"})) + protected.write_text(json.dumps({"version": "1.5.6"})) old_metadata = b'{"schema":2,"version":"1.5.1"}\n' metadata_path = fake_app / "INSTALL.json" metadata_path.write_bytes(old_metadata) @@ -418,7 +418,7 @@ def run_inline(): accepted = run_inline() need(accepted.returncode == 0, "F415_ATOMIC_METADATA_WRITE_FAILED:" + accepted.stderr) doc = json.loads(metadata_path.read_text(encoding="utf-8")) - need(doc["version"] == "1.5.5-rc1" and doc["schema"] == 2 + need(doc["version"] == "1.5.6" and doc["schema"] == 2 and doc["managedCodeSha256"] == {"direct_internet_method.py": "b" * 64} and doc["preservedCustomProviderData"] is True, "F415_METADATA_POSTCONDITION_INVALID") @@ -446,13 +446,13 @@ def run_inline(): doc = { "schema": 2, "product": "Direct Internet Method", - "version": "1.5.5-rc1", + "version": "1.5.6", "platform": "linux-x86_64", "installedUtc": "2026-10-12T00:00:00Z", "networkMutationOnInstall": False, "normalRunRequiresAdmin": False, "systemBackend": "/usr/lib/directinternetmethod", - "protectedBackendVersion": "1.5.5-rc1", + "protectedBackendVersion": "1.5.6", "protectedBackendByteCompatible": True, "onlineUpdate": "GitHub latest release + SHA256SUMS verification", "managedProviderSha256": manifest["providerSha"], diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index 0aacbb47..f6cc2112 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -7,15 +7,15 @@ from __future__ import annotations import ast,hashlib,importlib.util,json,pathlib,re,tempfile,unittest,zipfile R=pathlib.Path(__file__).resolve().parents[1] -VERSION="1.5.5-rc1" +VERSION="1.5.6" PACKAGE=R/"delivery"/f"DirectInternetMethod_{VERSION}_Linux_x86_64.zip" GUI=R/"linux/app/direct_internet_method.py" HELPER=R/"linux/app/direct_method_helper.sh" ROOT_TXN=R/"linux/system/protected_upgrade_transaction.py" INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" -PKG_DOC=R/"docs/README_PACKAGE_155_RC1.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934"} +PKG_DOC=R/"docs/README_PACKAGE_156.md" +EXPECTED={"version":VERSION,"files":28,"sha256":"A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -61,9 +61,9 @@ def test_r519b_uninstaller_accepts_only_new_ownership_version(self): got=self.info.read(self.prefix+rel) declared=next(row for row in self.rows if row["file"]==rel) self.assertEqual(digest(got),declared["sha256"]) - self.assertEqual(declared["sha256"],"06825BCD25B237625DBC4FF60250763C38A27238A6273E0C97E847EAD385E64F") + self.assertEqual(declared["sha256"],"6BFD45BA87CE052D020D26BE5B6E0F589DF1B848D202D716AFD8FB87FD1FDE9A") self.assertIn('"socket_fallback.py"', got.decode()) - self.assertIn('doc.get("version") == "1.5.5-rc1"',got.decode()) + self.assertIn('doc.get("version") == "1.5.6"',got.decode()) self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) self.assertEqual(digest((R/"linux/app/user_uninstall_preserve.py").read_bytes()),declared["sha256"]) def test_original_unreleased_candidate_preserved_and_current_source_delta(self): @@ -80,7 +80,7 @@ def test_original_unreleased_candidate_preserved_and_current_source_delta(self): self.assertEqual(digest(old_source.read_bytes()), "9A4A4C37E3F63F1602E15454100150D209E3CD099DE80504B560F31D42D3C0EF") prev=json.loads(old_manifest_bytes) - self.assertEqual((prev["schema"],prev["version"],prev["platform"]),(1,VERSION,"linux-x86_64")) + self.assertEqual((prev["schema"],prev["version"],prev["platform"]),(1,"1.5.5-rc1","linux-x86_64")) before={row["file"]:row for row in prev["files"]} after={row["file"]:row for row in self.rows} self.assertEqual(set(after),set(before)|{"system/upgrade_guard.py","app/socket_fallback.py"}) @@ -88,13 +88,15 @@ def test_original_unreleased_candidate_preserved_and_current_source_delta(self): changed={name for name in before if before[name]!=after[name]} # Preserve the immutable historical manifest and explicitly recognize # the new R762 controller hardening in the current unreleased package. - self.assertEqual(changed,{"app/user_uninstall_preserve.py","app/direct_internet_method.py", - "app/direct_method_helper.sh","app/user_update_transaction.py","system/protected_upgrade_transaction.py", - "system/upgrade_system.sh","install.sh","system/encrypted_selective_dns.py"}) + self.assertEqual(changed,{"README.md","app/user_uninstall_preserve.py", + "app/direct_internet_method.py","app/direct_method_helper.sh", + "app/user_update_transaction.py","system/protected_upgrade_transaction.py", + "system/upgrade_system.sh","system/install_system.sh", + "install.sh","system/encrypted_selective_dns.py"}) self.assertEqual(after["system/encrypted_selective_dns.py"]["sha256"], "A8A8DD6155DDC4E2C5295A6CAA7B8809144C43985B47FD4664501ED683DB0AAE") self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "6CF031D2C7F18D7E7E4F1F9A37A1A0EBD3CA589F16F7E26274496F1F53E9317B") + "AC4EF0EBD88B62B730B1309AD25B04876DED2E00AF429C369E02EE087F7F377B") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', @@ -124,12 +126,12 @@ def test_original_published_metadata_untouched(self): self.assertFalse(any("1.5.5-rc1" in x["file"] for x in self.rows)) def test_fresh_root_contract_version(self): root=(R/"linux/system/install_system.sh").read_text() - self.assertIn('{"schema":1,"version":"1.5.5-rc1"',root) + self.assertIn('{"schema":1,"version":"1.5.6"',root) self.assertIn('encrypted_selective_dns.py',root) self.assertIn('encrypted_selective_policy.py',root) def test_protected_upgrade_exact_sha(self): text=ROOT_TXN.read_text() - self.assertIn('TARGET_VERSION="1.5.5-rc1"',text) + self.assertIn('TARGET_VERSION="1.5.6"',text) self.assertIn('CURRENT_BACKEND_VERSION="1.5.2"',text) self.assertIn('data.get("revision")=="R519"',text) self.assertIn('receipt.get("revision")=="R519"',text) @@ -138,7 +140,7 @@ def test_protected_upgrade_exact_sha(self): def test_root_wrapper_pinned_identity(self): src=(R/"linux/system/upgrade_system.sh").read_text() self.assertIn(digest(ROOT_TXN.read_bytes()),src) - self.assertIn('DirectInternetMethod_1.5.5-rc1_Linux_x86_64',src) + self.assertIn('DirectInternetMethod_1.5.6_Linux_x86_64',src) self.assertIn("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED",src) self.assertIn("GUARD_SHA=",src) self.assertLess(src.index("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED"), @@ -197,12 +199,12 @@ def test_user_installer_never_pretends_committed(self): self.assertIn('python3 "$USER_TRANSACTION" --rollback',src) def test_gui_and_user_metadata_pins(self): app=GUI.read_text() - self.assertIn('VERSION="1.5.5-rc1"',app) + self.assertIn('VERSION="1.5.6"',app) self.assertIn('COEXIST_HELPER_SHA256="'+digest(HELPER.read_bytes()).lower()+'"',app) - self.assertIn('installed.get("version") == "1.5.5-rc1"',app) + self.assertIn('installed.get("version") == "1.5.6"',app) updater=USER_TXN.read_text() self.assertIn('len(package_doc["files"]) == 28',updater) - self.assertIn('data.get("version") == "1.5.5-rc1"',updater) + self.assertIn('data.get("version") == "1.5.6"',updater) self.assertIn('"encrypted-dns-optin.txt"',updater) def test_rc_semver_prevents_implicit_downgrade(self): code=ast.parse(GUI.read_text()) @@ -225,8 +227,8 @@ def test_archive_manifest_negative_corrupted_payload(self): module.source_files(target) def test_readme_is_explicitly_unreleased(self): s=PKG_DOC.read_text() - self.assertIn("NOT RELEASED",s) - self.assertIn("1.5.5-rc1",s) + self.assertIn("NOT PUBLISHED",s) + self.assertIn("1.5.6",s) self.assertIn("v1.5.4",s) if __name__=="__main__": result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Tests)) diff --git a/tests/r526_installer_metadata_contract.py b/tests/r526_installer_metadata_contract.py index 874b97ef..eca9d576 100644 --- a/tests/r526_installer_metadata_contract.py +++ b/tests/r526_installer_metadata_contract.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""R526: hermetic Linux 1.5.5-rc1 INSTALL.json activation regression gate. +"""R526: hermetic Linux 1.5.6 INSTALL.json activation regression gate. Run the exact inline metadata program from linux/install.sh against a disposable HOME and a fake protected Root metadata file. No real installer, sudo, systemd, @@ -23,7 +23,7 @@ BEGIN = """python3 - "$APP/INSTALL.json" "$USER_TRANSACTION" <<'PY'\n""" END = "\nPY\n" PROTECTED = 'pathlib.Path("/var/lib/directinternetmethod/install.json")' -VERSION = "1.5.5-rc1" +VERSION = "1.5.6" def exact_inline_program() -> str: @@ -98,7 +98,7 @@ def test_01_entrypoint_and_exact_source_contract(self) -> None: self.assertTrue(INSTALL.is_file() and USER_TX.is_file()) self.assertEqual(len(self.program.splitlines()), 64) self.assertEqual(self.program.count(PROTECTED), 1) - self.assertIn('assert protected.get("version") == "1.5.5-rc1"', self.program) + self.assertIn('assert protected.get("version") == "1.5.6"', self.program) self.assertIn('transaction_helper.atomic_bytes(', self.program) def test_02_in_place_metadata_and_mutable_preservation(self) -> None: diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 141f9209..8a0de900 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,11 +23,11 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="0c4d22f98c7c4ecea2cde1c0232b3c3928c022779ba8a644cd3dc46ff13ce934" +CANDIDATE_SHA="a8356107c5de6529e8270d1ee404a7d8a629040bd0d17f3f83c61c390790655d" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" -CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.5-rc1_Linux_x86_64.zip" +CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.6_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" -CANDIDATE_PREFIX="DirectInternetMethod_1.5.5-rc1_Linux_x86_64/" +CANDIDATE_PREFIX="DirectInternetMethod_1.5.6_Linux_x86_64/" SOURCE=BASE/"linux/system/protected_upgrade_transaction.py" HOSTED_WORKSPACE=Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod") HOSTED_TEMP=Path("/home/runner/work/_temp") @@ -127,7 +127,7 @@ def stage_exact_candidate(candidate_zip:Path,temp:Path)->Path: with zipfile.ZipFile(candidate_zip) as z: archive_verify(z,CANDIDATE_PREFIX,30) manifest=json.loads(z.read(CANDIDATE_PREFIX+"PACKAGE_MANIFEST.json")) - need(manifest.get("version")=="1.5.5-rc1" and len(manifest.get("files",[]))==28, + need(manifest.get("version")=="1.5.6" and len(manifest.get("files",[]))==28, "RC1_PACKAGE_VERSION_MANIFEST") for f in manifest["files"]: relative=f["file"] @@ -189,7 +189,7 @@ def run_native(): "hosts":fsha(Path("/etc/hosts")), } m=module() - need(m.TARGET_VERSION=="1.5.5-rc1" and + need(m.TARGET_VERSION=="1.5.6" and m.CURRENT_BACKEND_VERSION=="1.5.2","NATIVE_PRODUCT_VERSION") source=stage_exact_candidate(candidate,temp) baseline=seed_real_protected_root(stable,user,m) @@ -212,7 +212,7 @@ def run_native(): stat.S_IMODE(file.stat().st_mode)==mode, "REAL_ROOT_UPGRADED_FILE_"+name) current=json.loads(Path("/var/lib/directinternetmethod/install.json").read_text()) - need(current["version"]=="1.5.5-rc1","REAL_ROOT_METADATA_NOT_ACTIVATED") + need(current["version"]=="1.5.6","REAL_ROOT_METADATA_NOT_ACTIVATED") try: t.commit(temp/"NO_NATIVE_ACCEPTANCE_RECEIPT.json") raise RuntimeError("R528_BAD_ACCEPTANCE_WAS_ALLOWED") @@ -246,7 +246,7 @@ def run_native(): "productionMachineTouched":False, "nativeRoot":True,"effectiveUid":os.geteuid(),"protectedPreVersion":"1.5.2", "protectedPostRollbackVersion":"1.5.2", - "protectedCandidateAppliedBeforeRollback":"1.5.5-rc1", + "protectedCandidateAppliedBeforeRollback":"1.5.6", "publicStablePreserved":"v1.5.4","pinnedSource":CANDIDATE_SHA, "pinnedPublishedStable":STABLE_SHA, "rootOwnedFilesRestored":len(baseline), diff --git a/tests/r529_ephemeral_root_user_transaction.py b/tests/r529_ephemeral_root_user_transaction.py index e6e556da..c5d9ff7a 100644 --- a/tests/r529_ephemeral_root_user_transaction.py +++ b/tests/r529_ephemeral_root_user_transaction.py @@ -2,7 +2,7 @@ """R529: native Root + unprivileged User transaction across one disposable VM. Not a physical network/GUI acceptance. Use exact v1.5.4 published ZIP user and -protected Root code, exact 1.5.5-rc1 candidate ZIP, unmodified RootTransaction("/") +protected Root code, exact 1.5.6 candidate ZIP, unmodified RootTransaction("/") and real subprocess-driven user transaction / inline INSTALL.json production fragment. Both transactions must rollback byte-for-byte. NO live network or systemd, No Tailscale changes, no installation onto a user's physical computer. @@ -228,7 +228,7 @@ def execute(): as_runner(user, tx_script, "--stage", "--source", extracted) require((pending / "manifest.json").is_file(), "USER_PENDING_STAGE") manifest=json.loads((pending / "manifest.json").read_text()) - require(manifest["version"]=="1.5.5-rc1" and + require(manifest["version"]=="1.5.6" and manifest["providerManaged"] is False and manifest["providerSha"] is None, "CUSTOM_USER_PROVIDER_OWNERSHIP") @@ -237,7 +237,7 @@ def execute(): # Protected absolute-root stage/apply with actual root-owned files. require(root_tx.stage(extracted)["status"]=="STAGED", "ROOT_STAGE") require(root_tx.apply()["status"]=="APPLIED", "ROOT_APPLY") - require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", "ROOT_NEW_METADATA") + require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", "ROOT_NEW_METADATA") # Real unprivileged User transaction CLI replaces managed code only. as_runner(user, tx_script, "--apply", "--source", extracted) for rel,row in manifest["managed"].items(): @@ -262,12 +262,12 @@ def execute(): inline=tail.split(end,1)[0] require(len(inline.splitlines())==64 and 'transaction_helper.atomic_bytes(' in inline and - 'assert protected.get("version") == "1.5.5-rc1"' in inline, + 'assert protected.get("version") == "1.5.6"' in inline, "INSTALLER_METADATA_PROVENANCE_CHANGED") as_runner_inline(user,inline,app/"INSTALL.json",tx_script) record=json.loads((app/"INSTALL.json").read_text()) - require(record["version"]=="1.5.5-rc1" and - record["protectedBackendVersion"]=="1.5.5-rc1", + require(record["version"]=="1.5.6" and + record["protectedBackendVersion"]=="1.5.6", "TWO_SIDED_VERSION_METADATA") require(record["managedProviderSha256"] is None and record["preservedCustomProviderData"] is True and @@ -299,11 +299,11 @@ def execute(): print(json.dumps({ "revision":"R529", "status":"PASS_HOSTED_ROOT_USER_ACTUAL_TRANSACTION", "productionMachineTouched":False,"hostedRunner":True, - "publicStable":"v1.5.4","candidate":"1.5.5-rc1", + "publicStable":"v1.5.4","candidate":"1.5.6", "userTransaction":"STAGE_APPLY_INLINE_ATOMIC_METADATA_ROLLBACK", "protectedRoot":"ABSOLUTE_ROOT_STAGE_APPLY_ROLLBACK", "preVersions":"user1.5.4/root1.5.2", - "appliedVersion":"both1.5.5-rc1", + "appliedVersion":"both1.5.6", "postRollbackVersions":"user1.5.4/root1.5.2", "oldUserCodeFilesVerified":len(old_user), "protectedRootFilesVerified":len(root_before), diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 66156dce..9c806b04 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -3,7 +3,7 @@ Both invocations are separate CI steps on an ephemeral GitHub-hosted Ubuntu VM. The first starts a detached systemd monotonic timer BEFORE any candidate file -mutation, leaves actual protected Root+unprivileged User 1.5.5-rc1 staged and +mutation, leaves actual protected Root+unprivileged User 1.5.6 staged and applied, and EXITs. The second waits for the independent timer and verifies byte-exact restoration to 1.5.2 Root / 1.5.4 User, without running rollback. All installed products on physical hosts and network services are untouched. @@ -36,8 +36,8 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "94c7d314809f582f437847c3b4f32f9fa8388e14526c138373974a70dea0afc8" -USER_SHA = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" +ROOT_SHA = "7147af90e6dc0b29a6ce15a84656ce9587060a7f5f343e7b16281c5602fc693c" +USER_SHA = "5077ace07b27ddbcbeaa4de56ed718f7fd26934ff65a13251cceafa9dd2feb50" def sha(p:Path)->str: h=hashlib.sha256() @@ -180,9 +180,9 @@ def arm(): r529.as_runner(user,user_script,"--apply","--source",extracted) r529.as_runner_inline(user,exact_inline(r526,extracted), app/"INSTALL.json",user_script) - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6", "NEW_USER_METADATA") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", "NEW_ROOT_METADATA") need(root_tx.status()["status"]=="APPLIED","ROOT_APPLIED_PENDING") need((Path("/home/runner/.local/state/DirectInternetMethod/update-pending/manifest.json")).is_file(), @@ -192,7 +192,7 @@ def arm(): print("PASS_R532_WATCHDOG_ARMED_BEFORE_MUTATION_THEN_MAIN_EXIT") print("WORKER_ROOT_OWNED_SHA",doc["workerSHA256"]) print("TIMER_UNIT",TIMER,"SERVICE_UNIT",SERVICE) - print("PRE_TIMER_VERSIONS", "root=1.5.5-rc1","user=1.5.5-rc1") + print("PRE_TIMER_VERSIONS", "root=1.5.6","user=1.5.6") def verify(): need(GUARD.is_dir() and GUARD.stat().st_uid==0,"EXPECTED_GUARD_NOT_FOUND") diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index a07aeae3..dea5d096 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,8 +25,8 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "94c7d314809f582f437847c3b4f32f9fa8388e14526c138373974a70dea0afc8" -PIN_USER = "c1bbf62d99e46cb8e349a38e60891027ff7276f85caceb597548c269203f28bf" +PIN_ROOT = "7147af90e6dc0b29a6ce15a84656ce9587060a7f5f343e7b16281c5602fc693c" +PIN_USER = "5077ace07b27ddbcbeaa4de56ed718f7fd26934ff65a13251cceafa9dd2feb50" def digest(path:Path): h=hashlib.sha256() diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index aa32a12d..3ea98efa 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" +SOURCE_SHA="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) @@ -33,7 +33,9 @@ def synthetic_receipt(target,blob,mode): target.chmod(0o600) g.write_new=synthetic_receipt g.SERVICE=p/"unit.service";g.TIMER=p/"unit.timer" - self.fake={"user":"runner","home":str(self.home),"uid":os.getuid()} + self.fake={"user":"runner","home":str(self.home),"uid":os.getuid(), + "versionFromRoot":g.BASELINE,"versionFromUser":g.OLD_USER, + "versionTo":g.VERSION} g.verify_guard_files=lambda:self.fake self.events=[] g.verify_installed_after_restore=lambda j:self.events.append("validate_restored") @@ -316,7 +318,7 @@ def _stage_fixture(self): mutable=("settings.json","strategy.txt","scope.txt","custom-hosts.txt", "adult-enabled.txt","adult-hosts.txt","adult-hosts.meta.json", "encrypted-dns-optin.txt","selective-dns-optin.txt") - row={"schema":1,"version":"1.5.5-rc1","previousVersion":"1.5.4", + row={"schema":1,"version":g.VERSION,"previousVersion":g.OLD_USER, "managed":managed,"old":prior, "providerManaged":False,"providerSha":None, "mutable":{k:None for k in mutable}} @@ -328,7 +330,7 @@ def test_11_real_user_stage_schema_not_invented_phase(self): installed,manifest,row=self._stage_fixture() self.assertNotIn("phase",row) accepted=g.staged_user_manifest(self.home,os.getuid(),installed) - self.assertEqual(accepted["version"],"1.5.5-rc1") + self.assertEqual(accepted["version"],g.VERSION) self.assertEqual(len(accepted["managed"]),8) def test_25_real_managed_provider_nine_members_accepted(self): @@ -385,6 +387,29 @@ def reject(modified): reject({**row,"old":{**row["old"],"INSTALL.json":{"exists":True,"sha":"0"*64}}}) reject({**row,"mutable":{}}) + def test_28_rc1_previous_stage_must_use_exact_declared_pair(self): + installed,manifest,row=self._stage_fixture() + installed.write_text('{"version":"1.5.5-rc1"}') + row["old"]["INSTALL.json"]["sha"]=g.sha_file(installed).lower() + row["previousVersion"]="1.5.5-rc1" + manifest.write_text(json.dumps(row)) + with self.assertRaisesRegex(RuntimeError,"USER_STAGE_IDENTITY"): + g.staged_user_manifest(self.home,os.getuid(),installed) + accepted=g.staged_user_manifest( + self.home,os.getuid(),installed,"1.5.5-rc1") + self.assertEqual(accepted["version"],"1.5.6") + self.assertEqual(accepted["previousVersion"],"1.5.5-rc1") + + def test_29_only_correlated_root_and_user_versions_allowed(self): + self.assertEqual( + g.ALLOWED_BASELINE_PAIRS, + (("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"))) + for mixed in (("1.5.2","1.5.5-rc1"), + ("1.5.5-rc1","1.5.4"), + ("1.5.3","1.5.4"), + ("1.5.5-rc1","1.5.6")): + self.assertNotIn(mixed,g.ALLOWED_BASELINE_PAIRS) + def test_10_readiness_worker_does_not_query_elapsed_active_timer(self): txt=SRC.read_text() self.assertIn("j=verify_guard_files()",txt) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index 8089fbb4..e1d2f55e 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,10 +29,10 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" -PIN_ROOT="94C7D314809F582F437847C3B4F32F9FA8388E14526C138373974A70DEA0AFC8" -PIN_GUARD="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" -PIN_USER="C1BBF62D99E46CB8E349A38E60891027FF7276F85CACEB597548C269203F28BF" +PIN_PACKAGE="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" +PIN_ROOT="7147AF90E6DC0B29A6CE15A84656CE9587060A7F5F343E7B16281C5602FC693C" +PIN_GUARD="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" +PIN_USER="5077ACE07B27DDBCBEAA4DE56ED718F7FD26934FF65A13251CCEAFA9DD2FEB50" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") @@ -174,9 +174,9 @@ def run(): need("transaction_helper.atomic_bytes(" in inline and len(inline.splitlines())==64,"INSTALL_INLINE_IDENTITY") r529.as_runner_inline(user,inline,app/"INSTALL.json",tx_user) - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6", "UPGRADED_USER_VERSION") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", "UPGRADED_ROOT_VERSION") need(root_tx.status()["status"]=="APPLIED","ROOT_STILL_PENDING") # R562 tests interruption AFTER a deliberately simulated protected Root @@ -254,8 +254,8 @@ def run(): need(root_tx.status()["status"]=="COMMITTED" and root_tx.pending.is_dir() and pending.is_dir(), "ACCEPTED_ROOT_OR_USER_BACKUP_WAS_REVERTED") - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1" - and json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6" + and json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", "COMMITTED_VERSION_CHANGED_ON_FAILED_FINALIZATION") for rel,row in manifest["managed"].items(): dest=app/rel @@ -283,9 +283,9 @@ def run(): "ACCEPTED_ROOT_USER_COMMIT_NOT_RECONCILED") need(root_tx.status()["status"]=="COMMITTED","ACCEPTED_ROOT_PHASE_LOST") need(not pending.exists(),"USER_BACKUP_NOT_RELEASED") - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6", "COMMITTED_USER_VERSION") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.5-rc1", + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", "COMMITTED_ROOT_VERSION") installed_manifest=json.loads((app/"INSTALL.json").read_text()) for rel,row in manifest["managed"].items(): diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index ffe401c8..0721a662 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="F8CADEC8F1387C7A3CB5F1BC59FDCB17298869A2B2856FF937A14660977FFE10" +PIN_GUARD="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 9839b95e..5427f557 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" +ARCHIVE="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) diff --git a/tests/r779_root_guard_source_copy_identity_contract.py b/tests/r779_root_guard_source_copy_identity_contract.py index 2443c1be..64a00291 100644 --- a/tests/r779_root_guard_source_copy_identity_contract.py +++ b/tests/r779_root_guard_source_copy_identity_contract.py @@ -27,7 +27,7 @@ class GuardSourceIdentity(unittest.TestCase): def setUp(self): self.tmp=tempfile.TemporaryDirectory(prefix="r779-owned-",dir=str(ROOT)) self.base=pathlib.Path(self.tmp.name) - self.src=self.base/"DirectInternetMethod_1.5.5-rc1_Linux_x86_64" + self.src=self.base/"DirectInternetMethod_1.5.6_Linux_x86_64" (self.src/"system").mkdir(parents=True) (self.src/"app").mkdir() self.root_tx=self.src/"system/protected_upgrade_transaction.py" diff --git a/tests/r781_privileged_entry_contract.py b/tests/r781_privileged_entry_contract.py index 22cf7bdf..658d42e4 100644 --- a/tests/r781_privileged_entry_contract.py +++ b/tests/r781_privileged_entry_contract.py @@ -18,7 +18,7 @@ SPEC=importlib.util.spec_from_file_location("dim_r781",SRC) m=importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(m) -PKG="DirectInternetMethod_1.5.5-rc1_Linux_x86_64" +PKG="DirectInternetMethod_1.5.6_Linux_x86_64" class RootlessTrustedEntry(unittest.TestCase): def setUp(self): diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index d9c2db03..eab237af 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -18,11 +18,11 @@ import zipfile BASE = Path(__file__).resolve().parents[1] -PIN_ENTRY = "C8AE0759FDF3A5EE514B5457D05AF1BFEF0BB266E03244C7EF2F3F6EDB8994D1" -PIN_ZIP = "0C4D22F98C7C4ECEA2CDE1C0232B3C3928C022779BA8A644CD3DC46FF13CE934" +PIN_ENTRY = "D0181C15C54B90A3B6E03FBFBE32148341037AB60CF4D6788B6A72140304340F" +PIN_ZIP = "A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") SOURCE = BASE / "tools/security/privileged_entry.py" -PKG = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" +PKG = "DirectInternetMethod_1.5.6_Linux_x86_64" ARCHIVE = BASE / "delivery" / (PKG + ".zip") # This is a disposable original-repository GitHub VM, not an arbitrary path # derived from RUNNER_TEMP or another user-provided environment variable. diff --git a/tests/r803_dual_baseline_forward_contract.py b/tests/r803_dual_baseline_forward_contract.py new file mode 100644 index 00000000..f0068688 --- /dev/null +++ b/tests/r803_dual_baseline_forward_contract.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""R803: negative-first future Stable 1.5.6 migration from BOTH accepted Linux roots. + +Only disposable user-owned fake-root trees. Never runs pkexec, native service, +installed backend, DNS, Tailscale, package installer or public publication. +""" +import importlib.util +import json +import os +import pathlib +import unittest +from unittest.mock import patch + +HERE=pathlib.Path(__file__).resolve().parents[1] +old=HERE/"tests/protected_upgrade_transaction_contract.py" +spec=importlib.util.spec_from_file_location("r803_existing_fake_fixture",old) +assert spec and spec.loader +oldtests=importlib.util.module_from_spec(spec) +spec.loader.exec_module(oldtests) +m=oldtests.m + +class DualBaselineForwardMigration(oldtests.FakeFixture): + def setUp(self): + super().setUp() + self.patch_target=patch.object(m,"TARGET_VERSION","1.5.6") + self.patch_target.start() + self.addCleanup(self.patch_target.stop) + def rc1_as_current_backend(self): + for name,rel,target,old_sha,new_sha,mode in m.FILES: + dest=self.root/target + dest.parent.mkdir(parents=True,exist_ok=True) + dest.write_bytes((self.pkg/rel).read_bytes()) + dest.chmod(mode) + self.assertEqual(m.hash_file(dest),new_sha) + meta=dict(self.orig,version="1.5.5-rc1") + self.meta.write_text(json.dumps(meta,sort_keys=True)+"\n") + self.meta.chmod(0o644) + def test_public_old_root_to_stable_then_rollback(self): + initial=self.root_sha() + result=self.tx.stage(self.pkg) + self.assertEqual(result["status"],"STAGED") + self.assertEqual(self.tx.manifest()["fromVersion"],"1.5.2") + self.assertEqual(self.tx.manifest()["toVersion"],"1.5.6") + self.assertEqual(self.tx.apply()["status"],"APPLIED") + self.assertEqual(self.tx.rollback()["status"],"ROLLED_BACK") + self.assertEqual(self.root_sha(),initial) + self.assertEqual(json.loads(self.meta.read_text())["version"],"1.5.2") + def test_private_rc1_root_to_stable_then_rollback(self): + self.rc1_as_current_backend() + initial=self.root_sha() + self.assertEqual(len(initial),4) + self.assertEqual(self.tx.stage(self.pkg)["status"],"STAGED") + doc=self.tx.manifest() + self.assertEqual(doc["fromVersion"],"1.5.5-rc1") + self.assertEqual(doc["toVersion"],"1.5.6") + for name,rel,target,old_sha,new_sha,mode in m.FILES: + self.assertEqual(doc["files"][name]["old"],new_sha) + self.assertEqual(self.tx.apply()["status"],"APPLIED") + self.assertEqual(json.loads(self.meta.read_text())["version"],"1.5.6") + self.assertEqual(self.tx.rollback()["status"],"ROLLED_BACK") + self.assertEqual(json.loads(self.meta.read_text())["version"],"1.5.5-rc1") + self.assertEqual(self.root_sha(),initial) + def test_private_rc1_root_tampered_source_fails_before_slot(self): + self.rc1_as_current_backend() + p=self.root/m.FILES[0][2] + p.write_bytes(b"tampered previously installed backend") + with self.assertRaisesRegex(RuntimeError,"OLD_FILE_SHA|BASELINE"): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + def test_unknown_old_root_version_rejected(self): + data=dict(self.orig,version="1.5.3") + self.meta.write_text(json.dumps(data)+"\n") + with self.assertRaisesRegex(RuntimeError,"OLD_ROOT_VERSION"): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + +if __name__=="__main__": + suite=unittest.TestSuite(unittest.defaultTestLoader.loadTestsFromName("DualBaselineForwardMigration.test_"+x,__name__) for x in ()) + suite=unittest.TestSuite([ + DualBaselineForwardMigration("test_public_old_root_to_stable_then_rollback"), + DualBaselineForwardMigration("test_private_rc1_root_to_stable_then_rollback"), + DualBaselineForwardMigration("test_private_rc1_root_tampered_source_fails_before_slot"), + DualBaselineForwardMigration("test_unknown_old_root_version_rejected") + ]) + r=unittest.TextTestRunner(verbosity=2).run(suite) + print("R803_DUAL_BASELINE_FORWARD_CONTRACT",r.testsRun,"PASS" if r.wasSuccessful() else "FAIL") + raise SystemExit(0 if r.wasSuccessful() else 1) diff --git a/tests/r803_user_from_rc1_forward_contract.py b/tests/r803_user_from_rc1_forward_contract.py new file mode 100644 index 00000000..62007367 --- /dev/null +++ b/tests/r803_user_from_rc1_forward_contract.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""R803: real user transaction from accepted private 1.5.5-rc1 to 1.5.6. + +Strictly user-owned temporary HOME; no physical install, pkexec, network, +systemd, DNS, Tailscale, external browser, or release publication. +""" +from pathlib import Path +from tempfile import TemporaryDirectory +import hashlib,json,os,subprocess,sys +ROOT=Path(__file__).resolve().parents[1] +TX=ROOT/"linux/app/user_update_transaction.py" +PACKAGE_SOURCE=ROOT/"linux" +KNOWN=("direct_internet_method.py","socket_fallback.py","router_gateway.py", + "router_gateway_core.py","direct-internet-method.svg","uninstall.sh", + "user_uninstall_preserve.py","user_update_transaction.py") +MUTABLE=("settings.json","strategy.txt","scope.txt","custom-hosts.txt", + "adult-enabled.txt","adult-hosts.txt","adult-hosts.meta.json", + "encrypted-dns-optin.txt","selective-dns-optin.txt") +H=lambda b:hashlib.sha256(b).hexdigest() +def need(x,why): + if not x:raise AssertionError(why) +def invoke(home,cmd,expected=True): + env=dict(os.environ,HOME=str(home),PYTHONDONTWRITEBYTECODE="1") + args=[sys.executable,"-B",str(TX),cmd] + if cmd in ("--stage","--apply"):args+=["--source",str(PACKAGE_SOURCE)] + p=subprocess.run(args,env=env,capture_output=True,text=True,timeout=12) + need((p.returncode==0)==expected,cmd+" "+p.stderr[-350:]) + return p +def initialize(home,manage_provider): + app=home/".local/share/DirectInternetMethod";app.mkdir(parents=True) + (app/"router_gateway").mkdir() + managed={} + for i,name in enumerate(KNOWN): + raw=("EXISTING_RC1_CODE_"+str(i)+"\n").encode() + (app/name).write_bytes(raw);managed[name]=H(raw) + provider=app/"router_gateway/providers.json" + provider.write_bytes(b'{"previousProvider":"keep"}\n') + md5=H(provider.read_bytes()) if manage_provider else None + preserved={} + for i,name in enumerate(MUTABLE): + raw=("PRIVATE_SETTINGS_"+str(i)+"\n").encode() + if i%3: (app/name).write_bytes(raw);preserved[name]=H(raw) + else:preserved[name]=None + metadata={"schema":2,"product":"Direct Internet Method", + "version":"1.5.5-rc1","managedProviderSha256":md5, + "managedCodeSha256":managed,"managedLaunchersSha256":{}} + (app/"INSTALL.json").write_text(json.dumps(metadata,sort_keys=True)+"\n") + return app,metadata,preserved,H(provider.read_bytes()) +def finalize_metadata(app,source_manifest): + installed={ + "schema":2,"version":"1.5.6", + "managedProviderSha256":source_manifest["providerSha"], + "managedCodeSha256":{name:row["sha"] for name,row in source_manifest["managed"].items() + if name!="router_gateway/providers.json"}, + "managedLaunchersSha256":{} + } + (app/"INSTALL.json").write_text(json.dumps(installed,sort_keys=True)+"\n") +def verify_settings(app,preserved): + for key,expected in preserved.items(): + p=app/key + got=H(p.read_bytes()) if p.exists() else None + need(got==expected,"MUTABLE_DRIFT_"+key) +def case(label,manage_provider,commit): + with TemporaryDirectory(prefix="DIM_R803_USER_RC1_FAKE_HOME_") as td: + home=Path(td)/"home";home.mkdir() + app,prior,preserved,provider_sha=initialize(home,manage_provider) + original={n:(app/n).read_bytes() for n in KNOWN} + original_provider=(app/"router_gateway/providers.json").read_bytes() + original_meta=(app/"INSTALL.json").read_bytes() + invoke(home,"--stage") + pending=home/".local/state/DirectInternetMethod/update-pending" + manifest=json.loads((pending/"manifest.json").read_text()) + need(manifest["version"]=="1.5.6" and manifest["previousVersion"]=="1.5.5-rc1","INCORRECT_RC1_PREDECESSOR") + need(manifest["providerManaged"] is manage_provider,"PROVIDER_MANAGED_POLICY") + need(len(manifest["managed"])==(9 if manage_provider else 8),"MANAGED_CARDINALITY") + invoke(home,"--apply") + verify_settings(app,preserved) + if commit: + finalize_metadata(app,manifest) + invoke(home,"--commit") + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6","RC1_FORWARD_VERSION") + for name,row in manifest["managed"].items(): + need(H((app/name).read_bytes())==row["sha"],"NEW_MANAGED_SHA_"+name) + if not manage_provider: + need((app/"router_gateway/providers.json").read_bytes()==original_provider,"CUSTOM_PROVIDER_LOST") + else: + invoke(home,"--rollback") + for name,raw in original.items(): + need((app/name).read_bytes()==raw,"OLD_RC1_SOURCE_NOT_RESTORED_"+name) + need((app/"INSTALL.json").read_bytes()==original_meta,"OLD_RC1_METADATA_NOT_RESTORED") + need((app/"router_gateway/providers.json").read_bytes()==original_provider,"OLD_PROVIDER_NOT_RESTORED") + need(not pending.exists(),"PENDING_STAGING_REMAINS") + verify_settings(app,preserved) + print("PASS_R803_USER_FROM_RC1",label,"settings",len(preserved),"managed",len(manifest["managed"])) +for m in (False,True): + for c in (False,True): + case("managedProvider="+str(m)+"/commit="+str(c),m,c) +print("R803_RC1_USER_FORWARD_AND_ROLLBACK",4,"PASS") diff --git a/tests/tailscale_gui_scope_guard_contract.py b/tests/tailscale_gui_scope_guard_contract.py index 7c28392b..d28a8026 100644 --- a/tests/tailscale_gui_scope_guard_contract.py +++ b/tests/tailscale_gui_scope_guard_contract.py @@ -105,16 +105,16 @@ def mode():return env["load_state"]() check=h_env["_coexistence_backend_ready"] assert check() is False FakePath.entries[good]="safe-backend" -FakePath.entries[meta]='{"version":"1.5.5-rc1","uid":1000}' +FakePath.entries[meta]='{"version":"1.5.6","uid":1000}' assert check() is True, "pinned helper and metadata must be recognized" FakePath.entries[good]="different-backend" assert check() is False, "foreign helper must be refused" FakePath.entries[good]="safe-backend" FakePath.entries[meta]='{"version":"1.5.1","uid":1000}' assert check() is False, "old metadata must be refused" -FakePath.entries[meta]='{"version":"1.5.5-rc1","uid":1001}' +FakePath.entries[meta]='{"version":"1.5.6","uid":1001}' assert check() is False, "different user metadata must be refused" -FakePath.entries[meta]='{"version":"1.5.5-rc1","uid":1000}' +FakePath.entries[meta]='{"version":"1.5.6","uid":1000}' FakePath.symlinks.add(good) assert check() is False, "helper symlink must be refused" FakePath.symlinks.clear() diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index bad7cfb2..3d317fea 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -23,9 +23,9 @@ import sys import tempfile -VERSION = "1.5.5-rc1" -PACKAGE = "DirectInternetMethod_1.5.5-rc1_Linux_x86_64" -MANIFEST_SHA256 = "E3D6186F8BC775FA7861DD506AA1EB978F6296BDC98D686CD3731C79680FEB42" +VERSION = "1.5.6" +PACKAGE = "DirectInternetMethod_1.5.6_Linux_x86_64" +MANIFEST_SHA256 = "1BDF1EE5C6C3A7C18623C31D16496C077022EDFB16A68163FD885F5D909DAF9A" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From 240ddaeffdc4e74675717d55c363cedaddd41137 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Tue, 20 Oct 2026 03:37:10 +0330 Subject: [PATCH 172/188] fix(ci): bind private R803 trusted bootstrap to exact review-only branch AI-assisted correction to original-runner R782 environment guard only. The previous pin rejected valid hosted R803 jobs because it named the older security branch. Preserve exact GITHUB_SHA, original repository, UID0, hosted-runner workspace, manifest SHA, and non-production negative checks; do not authorize local Root or skip the security job. --- tests/r782_hosted_trusted_entry_ephemeral.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r782_hosted_trusted_entry_ephemeral.py b/tests/r782_hosted_trusted_entry_ephemeral.py index eab237af..a76afa64 100644 --- a/tests/r782_hosted_trusted_entry_ephemeral.py +++ b/tests/r782_hosted_trusted_entry_ephemeral.py @@ -42,7 +42,7 @@ def guard(): need(e.get("CI") == "true" and e.get("GITHUB_ACTIONS") == "true" and e.get("GITHUB_REPOSITORY") == "GOD13emad/DirectInternetMethod" and e.get("RUNNER_ENVIRONMENT") == "github-hosted", "ORIGINAL_HOSTED_ONLY") - need(e.get("GITHUB_REF") == "refs/heads/security/r776-pinned-root-read-qa" and + need(e.get("GITHUB_REF") == "refs/heads/prep/r803-forward-156-native-gates" and e.get("GITHUB_SHA") and len(e["GITHUB_SHA"]) == 40, "REVIEW_ONLY_BRANCH_AND_EXACT_COMMIT") need(e.get("GITHUB_WORKSPACE") == str(EXPECTED_WORKSPACE) and From 2ff2d4db27a4f7c125ad9099157aa0d29af9b568 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:00:22 +0330 Subject: [PATCH 173/188] feat(windows): stage private 1.5.6 monotonic in-place upgrade candidate AI-assisted private draft, not a published release or independent human review. Forward-only GUI and Service 1.5.6.0 .NET10, unchanged Inno AppId, pinned bundled runtime, one installer, no forced signature. GitHub-hosted isolated Windows 2025 build, v1.5.4 to 1.5.6 upgrade and rollback test gates require original repo/branch/exact SHA; no test pass asserted before execution. Retain public RELEASE.json and v1.5.4 tags unchanged, preserve user data and physical network. Local R805 5/5, R664 monotonic guard, R482 .NET10 and publication authority source contract PASS. Linux v1.5.6 R803 verified prep is base. Physical Windows, browser, MMZ upstream and actual release remain OPEN. --- .github/workflows/ci.yml | 26 +++---- tests/dotnet10_target_contract.py | 11 +-- tests/r535_windows_candidate_hosted_build.ps1 | 12 ++-- tests/r535_windows_native_rollback.ps1 | 16 ++--- ...664_direct_update_monotonicity_contract.py | 7 +- tests/r805_windows_156_forward_contract.py | 69 +++++++++++++++++++ tests/release_metadata_audit.py | 19 ++++- tests/windows_contract_audit.py | 1 + windows/RELEASE.json | 8 +-- windows/gui/DirectInternetMethod.csproj | 2 +- windows/gui/MainWindow.xaml.cs | 2 +- windows/installer/DirectInternetMethod.iss | 6 +- windows/manifest.json | 2 +- .../DirectInternetMethod.Service.csproj | 6 +- 14 files changed, 139 insertions(+), 48 deletions(-) create mode 100644 tests/r805_windows_156_forward_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5abe2e5e..0d5b7d1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: Source and Release Contracts on: push: - branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa', 'prep/r803-forward-156-native-gates'] + branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa', 'prep/r803-forward-156-native-gates', 'prep/r805-windows-156'] tags: ['v*'] pull_request: branches: [main] @@ -467,6 +467,8 @@ jobs: $ErrorActionPreference='Stop' python -B tests/dotnet10_target_contract.py if($LASTEXITCODE -ne 0){throw 'R482_NET10_FRAMEWORK_CONTRACT_FAILED'} + python -B tests/r805_windows_156_forward_contract.py + if($LASTEXITCODE -ne 0){throw 'R805_WINDOWS_156_SOURCE_CONTRACT_FAILED'} - name: Verify published v1.5.4 documentation is current shell: pwsh run: | @@ -569,7 +571,7 @@ jobs: windows-private-r535-native: name: windows-private-r535-native - if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci' || github.ref == 'refs/heads/security/r776-pinned-root-read-qa') + if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci' || github.ref == 'refs/heads/security/r776-pinned-root-read-qa' || github.ref == 'refs/heads/prep/r805-windows-156') needs: windows-source runs-on: windows-2025 timeout-minutes: 35 @@ -580,32 +582,32 @@ jobs: - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6 with: dotnet-version: '10.0.x' - - name: R535 build exact private 1.5.5-rc1 Windows GUI service Inno installer + - name: R535 build exact private 1.5.6 Windows GUI service Inno installer shell: pwsh run: | $ErrorActionPreference='Stop' & pwsh -NoProfile -NonInteractive -File tests/r535_windows_candidate_hosted_build.ps1 if($LASTEXITCODE -ne 0){throw 'R535_PRIVATE_WINDOWS_NATIVE_BUILD_FAILED'} - - name: R535 hosted Windows v1.5.4 to RC1 in-place upgrade preserve and rollback + - name: R535 hosted Windows v1.5.4 to v1.5.6 in-place upgrade preserve and rollback shell: pwsh run: | $ErrorActionPreference='Stop' & pwsh -NoProfile -NonInteractive -File tests/r535_windows_native_rollback.ps1 if($LASTEXITCODE -ne 0){throw 'R535_NATIVE_INSTALL_ROLLBACK_FAILED'} - - name: R600 attested tested private RC1 installer for owner canary + - name: R600 attested tested private v1.5.6 installer for owner canary shell: pwsh run: | $ErrorActionPreference='Stop' $root=$env:GITHUB_WORKSPACE $report=Get-Content (Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE\BUILD_R535.json') -Raw|ConvertFrom-Json $native=Get-Content (Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE\NATIVE_R535.json') -Raw|ConvertFrom-Json - $installer=Join-Path $root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' + $installer=Join-Path $root 'delivery\DirectInternetMethod_1.5.6_Windows_Setup.exe' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -cne 'github-hosted' -or - $env:GITHUB_REF -cnotin @('refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa') -or + $env:GITHUB_REF -cnotin @('refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156') -or (git -C $root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R600_HOST_AUTHORITY'} - if($report.status -cne 'PASS_ISOLATED_HOSTED_RC1_BUILD' -or - $native.status -cne 'PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154' -or + if($report.status -cne 'PASS_ISOLATED_HOSTED_V156_BUILD' -or + $native.status -cne 'PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154' -or $report.head -cne $env:GITHUB_SHA -or $native.source -cne $env:GITHUB_SHA -or !(Test-Path -LiteralPath $installer -PathType Leaf)){throw 'R600_NATIVE_BUILD_ACCEPTANCE'} @@ -618,8 +620,8 @@ jobs: - name: Upload R600 exact tested installer (private ephemeral) uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: r600-private-tested-rc1-${{ github.run_id }} - path: delivery/DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe + name: r600-private-tested-v156-${{ github.run_id }} + path: delivery/DirectInternetMethod_1.5.6_Windows_Setup.exe if-no-files-found: error retention-days: 2 compression-level: 0 @@ -627,7 +629,7 @@ jobs: if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: r535-windows-rc1-hosted-${{ github.run_id }} + name: r535-windows-v156-hosted-${{ github.run_id }} path: | ${{ runner.temp }}/DIM_R535_NATIVE/BUILD_R535.json ${{ runner.temp }}/DIM_R535_NATIVE/NATIVE_R535.json diff --git a/tests/dotnet10_target_contract.py b/tests/dotnet10_target_contract.py index 27bf1fcb..6eac789c 100644 --- a/tests/dotnet10_target_contract.py +++ b/tests/dotnet10_target_contract.py @@ -15,9 +15,12 @@ CURRENT=REL.get("version") if CURRENT=="1.5.4": assert REL.get("status")=="CANDIDATE_V154_UNPUBLISHED" and REL.get("previousAccepted",{}).get("version")=="1.5.3" -elif CURRENT=="1.5.5-rc1": - assert REL.get("status")=="PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN" +elif CURRENT in ("1.5.5-rc1","1.5.6"): + accepted={"1.5.5-rc1":"PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", + "1.5.6":"PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN"} + assert REL.get("status")==accepted[CURRENT] assert REL.get("previousAccepted",{}).get("version")=="1.5.4" and REL.get("previousAccepted",{}).get("tag")=="v1.5.4" + assert REL.get("publication",{}).get("publishedAt") is None else: raise AssertionError("WINDOWS_CANDIDATE_VERSION_UNAUTHORIZED") @@ -59,9 +62,9 @@ def negative_first(): def main(): negative_first() ci=CI.read_text("utf-8") - expected_dotnet_jobs = 2 if CURRENT=="1.5.5-rc1" else 1 + expected_dotnet_jobs = 2 if CURRENT in ("1.5.5-rc1","1.5.6") else 1 assert ci.count("dotnet-version: '10.0.x'")==expected_dotnet_jobs,"SDK_10_JOB_PIN_COUNT" - if CURRENT=="1.5.5-rc1": + if CURRENT in ("1.5.5-rc1","1.5.6"): assert ci.count(" windows-private-r535-native:")==1 assert "runs-on: windows-2025" in ci and "tests/r535_windows_native_rollback.ps1" in ci assert "dotnet-version: '8.0.x'" not in ci diff --git a/tests/r535_windows_candidate_hosted_build.ps1 b/tests/r535_windows_candidate_hosted_build.ps1 index 9d492a70..3dd4027b 100644 --- a/tests/r535_windows_candidate_hosted_build.ps1 +++ b/tests/r535_windows_candidate_hosted_build.ps1 @@ -4,15 +4,15 @@ $ErrorActionPreference='Stop' Set-StrictMode -Version Latest $Root=$env:GITHUB_WORKSPACE if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_GITHUB_HOST_ONLY'} -if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} $r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json -if($r.version -cne '1.5.5-rc1' -or $r.status -cne 'PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R535_RC1_DECLARATION'} +if($r.version -cne '1.5.6' -or $r.status -cne 'PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R535_RC1_DECLARATION'} $qa=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' $gstage=Join-Path $qa 'gui' $sstage=Join-Path $qa 'service' $gt=Join-Path $Root 'windows\app\DirectInternetMethod.exe' $st=Join-Path $Root 'windows\app\DirectInternetMethod.Service.exe' -$installer=Join-Path $Root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' +$installer=Join-Path $Root 'delivery\DirectInternetMethod_1.5.6_Windows_Setup.exe' foreach($p in @($qa,$gt,$st,$installer)){if(Test-Path $p){throw ('R535_UNSAFE_RERUN_'+$p)}} New-Item -ItemType Directory -Path $qa|Out-Null $iscc=Join-Path ([Environment]::GetFolderPath('ProgramFilesX86')) 'Inno Setup 6\ISCC.exe' @@ -40,14 +40,14 @@ if($LASTEXITCODE -ne 0){throw 'NET10_CONTRACT_FAILED'} & python -B tests/windows_contract_audit.py | Out-Null if($LASTEXITCODE -ne 0){throw 'WINDOWS_AUDIT_FAILED'} $m=Get-Content (Join-Path $Root 'windows\manifest.json') -Raw|ConvertFrom-Json -if($m.version -cne '1.5.5-rc1' -or @($m.files).Count -ne 681){throw 'RC1_MANIFEST_IDENTITY'} +if($m.version -cne '1.5.6' -or @($m.files).Count -ne 681){throw 'RC1_MANIFEST_IDENTITY'} & $iscc /Qp (Join-Path $Root 'windows\installer\DirectInternetMethod.iss') if($LASTEXITCODE -ne 0){throw 'INNO_COMPILATION_FAILED'} if(!(Test-Path $installer) -or (Get-Item $installer).Length -lt 50000000){throw 'R535_INSTALLER_MISSING'} if([string](Get-AuthenticodeSignature $installer).Status -ne 'NotSigned'){throw 'R535_SIGNATURE_POLICY_CHANGED'} $report=[ordered]@{ - revision='R535';stage='BUILD';status='PASS_ISOLATED_HOSTED_RC1_BUILD' - head=$env:GITHUB_SHA;version='1.5.5-rc1';stable='v1.5.4' + revision='R535';stage='BUILD';status='PASS_ISOLATED_HOSTED_V156_BUILD' + head=$env:GITHUB_SHA;version='1.5.6';stable='v1.5.4' manifestCount=@($m.files).Count;manifestSha=(Get-FileHash (Join-Path $Root 'windows\manifest.json') -Algorithm SHA256).Hash guiSha=(Get-FileHash $gt -Algorithm SHA256).Hash serviceSha=(Get-FileHash $st -Algorithm SHA256).Hash diff --git a/tests/r535_windows_native_rollback.ps1 b/tests/r535_windows_native_rollback.ps1 index 54c00f9a..8d73e878 100644 --- a/tests/r535_windows_native_rollback.ps1 +++ b/tests/r535_windows_native_rollback.ps1 @@ -7,7 +7,7 @@ $Dir=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' $Build=Join-Path $Dir 'BUILD_R535.json' $Result=Join-Path $Dir 'NATIVE_R535.json' $Old=Join-Path $Dir 'DirectInternetMethod_1.5.4_Windows_Setup.exe' -$New=Join-Path $Root 'delivery\DirectInternetMethod_1.5.5-rc1_Windows_Setup.exe' +$New=Join-Path $Root 'delivery\DirectInternetMethod_1.5.6_Windows_Setup.exe' $App=Join-Path $env:LOCALAPPDATA 'Programs\DirectInternetMethod' $Priv=Join-Path $env:ProgramFiles 'DirectInternetMethod\Privileged' $Config=Join-Path $env:ProgramData 'DirectInternetMethod\UserConfig\custom-hosts.txt' @@ -20,12 +20,12 @@ $PublicSha='FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133' $PublicGui='BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F' $PublicSvc='2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_HOSTED_VM_ONLY'} -if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} $admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R535_ADMIN_TOKEN_REQUIRED'} if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R535_UNSAFE_REPLAY_OR_MISSING_BUILD'} $build=Get-Content $Build -Raw |ConvertFrom-Json -if($build.status -cne 'PASS_ISOLATED_HOSTED_RC1_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R535_BUILD_AUTHORITY'} +if($build.status -cne 'PASS_ISOLATED_HOSTED_V156_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R535_BUILD_AUTHORITY'} if((Get-FileHash $New -Algorithm SHA256).Hash -cne $build.installerSha){throw 'R535_RC1_INSTALLER_SHA'} $report=[ordered]@{schema=1;revision='R535';status='FAIL_UNPROVEN';source=$env:GITHUB_SHA; runner='github-hosted-windows-2025';privateInstallerSha=$build.installerSha; @@ -95,12 +95,12 @@ try { [IO.File]::WriteAllText($Evidence,'R535_USER_UNKNOWN_EVIDENCE_KEEP') $evidenceSha=FileSha $Evidence Install $New 'RC1_IN_PLACE' - [void](Registered '1.5.5-rc1') + [void](Registered '1.5.6') Require ((FileSha $Gui) -ceq $build.guiSha -and (FileSha $Svc) -ceq $build.serviceSha) 'RC1_GUI_SERVICE_PIN' Require ((FileSha (Join-Path $App 'manifest.json')) -ceq $build.manifestSha) 'RC1_MANIFEST_PIN' - Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.5-rc1') 'RC1_INSTALLED_VERSION' + Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.6') 'RC1_INSTALLED_VERSION' Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' - $d=Registered '1.5.5-rc1' + $d=Registered '1.5.6' $uninstaller=ExactRegisteredUninstaller $d $u=Start-Process $uninstaller -ArgumentList @('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART') -Wait -PassThru -NoNewWindow Require ($u.ExitCode -eq 0) 'RC1_UNINSTALL_ZERO' @@ -112,7 +112,7 @@ try { Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'PRIVATE_CONFIG_PRESERVED_ROLLBACK' $after=Network;$report.networkAfter=$after Require ($before.defaultRoutes -ceq $after.defaultRoutes -and $before.dns -ceq $after.dns) 'NETWORK_BASELINE_EXACT' - $report.status='PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154' + $report.status='PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154' $report.singleUninstaller=$true;$report.customProviderUpgradePreserved=$true;$report.dataPreserved=$true } catch { $report.status='DEGRADED_UNRESOLVED' @@ -122,4 +122,4 @@ try { Write-Output ('R535_NATIVE_STATUS='+$report.status) Write-Output ('R535_RECEIPT_SHA256='+((Get-FileHash $Result -Algorithm SHA256).Hash)) } -if($report.status -ne 'PASS_HOSTED_NATIVE_154_TO_155RC1_AND_BACK_154'){throw ('R535_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} +if($report.status -ne 'PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154'){throw ('R535_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} diff --git a/tests/r664_direct_update_monotonicity_contract.py b/tests/r664_direct_update_monotonicity_contract.py index 92da7c6d..77d7014c 100644 --- a/tests/r664_direct_update_monotonicity_contract.py +++ b/tests/r664_direct_update_monotonicity_contract.py @@ -34,11 +34,14 @@ raise AssertionError("UNSAFE_VERSION_WAS_NOT_REJECTED:" + version) win_release = json.loads((ROOT / "windows/RELEASE.json").read_text("utf-8-sig")) -if win_release.get("version") == "1.5.5-rc1": +if win_release.get("version") in ("1.5.5-rc1","1.5.6"): + expected_assembly = ("1.5.5.0" if win_release["version"]=="1.5.5-rc1" + else "1.5.6.0") for rel in ("windows/gui/DirectInternetMethod.csproj", "windows/service/DirectInternetMethod.Service.csproj"): source = (ROOT / rel).read_text("utf-8-sig") - assert "1.5.5.0" in source, rel + assert ""+expected_assembly+"" in source, rel + assert ""+expected_assembly+"" in source, rel gui = (ROOT / "windows/gui/UpdateClient.cs").read_text("utf-8-sig") service = (ROOT / "windows/service/Program.cs").read_text("utf-8-sig") assert "latest <= current" in gui, "GUI_UPDATER_COMPARISON_CHANGED_REVALIDATE_GUARD" diff --git a/tests/r805_windows_156_forward_contract.py b/tests/r805_windows_156_forward_contract.py new file mode 100644 index 00000000..202307a4 --- /dev/null +++ b/tests/r805_windows_156_forward_contract.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""R805 source-only Windows forward-release identity contract, no installation. + +Checks that public 1.5.4 stays immutable while the single future Windows and +Linux 1.5.6 private source candidate remains monotonically newer than the +already installed private Windows FileVersion 1.5.5.0. +""" +from pathlib import Path +from xml.etree import ElementTree as ET +import json,re,unittest +R=Path(__file__).resolve().parents[1] +META=json.loads((R/"windows/RELEASE.json").read_text("utf8")) +WIN=json.loads((R/"windows/manifest.json").read_text("utf8")) +PUBLIC=json.loads((R/"RELEASE.json").read_text("utf8")) +ISS=(R/"windows/installer/DirectInternetMethod.iss").read_bytes() +UI=(R/"windows/gui/MainWindow.xaml.cs").read_bytes() +WF=(R/".github/workflows/ci.yml").read_text() +EXPECTED="1.5.6" +class WindowsV156(unittest.TestCase): + def test_exact_product_versions(self): + self.assertEqual(META["version"],EXPECTED) + self.assertEqual(META["status"],"PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN") + self.assertEqual(WIN["version"],EXPECTED) + self.assertEqual(len(WIN["files"]),681) + self.assertEqual(META["previousAccepted"]["tag"],"v1.5.4") + self.assertEqual(PUBLIC["version"],"1.5.4") + self.assertIsNone(META["publication"]["publishedAt"]) + def test_windows_gui_service_assembly_and_file_versions(self): + for rel in ("windows/gui/DirectInternetMethod.csproj","windows/service/DirectInternetMethod.Service.csproj"): + tree=ET.fromstring((R/rel).read_bytes()) + def val(k):return tree.findtext(".//"+k) + self.assertEqual(val("Version"),EXPECTED,rel) + self.assertEqual(val("FileVersion"),EXPECTED+".0",rel) + self.assertEqual(val("AssemblyVersion"),EXPECTED+".0",rel) + self.assertEqual(val("TargetFramework"),"net10.0-windows") + def test_inplace_same_appid_and_numeric_forward(self): + self.assertIn(b'#define MyAppVersion "1.5.6"',ISS) + self.assertIn(b'VersionInfoVersion=1.5.6.0',ISS) + self.assertIn(b'OutputBaseFilename=DirectInternetMethod_1.5.6_Windows_Setup',ISS) + self.assertIn(b'UsePreviousAppDir=yes',ISS) + self.assertIn(b'UninstallLogMode=overwrite',ISS) + self.assertIn(b'{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}',ISS) + self.assertLess((1,5,5,0),(1,5,6,0)) + def test_hosted_native_path_preserves_actual_recovery(self): + build=(R/"tests/r535_windows_candidate_hosted_build.ps1").read_text("utf8") + native=(R/"tests/r535_windows_native_rollback.ps1").read_text("utf8") + self.assertIn("PASS_ISOLATED_HOSTED_V156_BUILD",build) + self.assertIn("PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154",native) + self.assertIn("DirectInternetMethod_1.5.6_Windows_Setup.exe",build) + self.assertIn("DirectInternetMethod_1.5.6_Windows_Setup.exe",native) + self.assertIn("IN_PLACE_USER_DATA_PRESERVED",native) + self.assertIn("NETWORK_BASELINE_EXACT",native) + self.assertIn("ORIGINAL_STABLE_BINARY_HASHES_RESTORED",native) + def test_useragent_and_original_repo_branch_guards(self): + self.assertIn(b'DirectInternetMethod/1.5.6',UI) + for name in ("tests/r535_windows_candidate_hosted_build.ps1","tests/r535_windows_native_rollback.ps1"): + raw=(R/name).read_text("utf8") + self.assertIn("GOD13emad/DirectInternetMethod",raw) + self.assertIn("prep/r805-windows-156",raw) + self.assertIn("$env:GITHUB_SHA",raw) + self.assertIn("refs/heads/prep/r805-windows-156",WF) + self.assertIn("windows-private-r535-native:",WF) + self.assertIn("startsWith(github.ref, 'refs/tags/v')",WF) + +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(WindowsV156)) + print("R805_WINDOWS_SOURCE_PRIVATE_FORWARD_TESTS",result.testsRun, + "PASS" if result.wasSuccessful() else "FAIL") + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/release_metadata_audit.py b/tests/release_metadata_audit.py index 0a7aa820..bc1ac5d4 100644 --- a/tests/release_metadata_audit.py +++ b/tests/release_metadata_audit.py @@ -167,8 +167,20 @@ def require_public_stable_forward_update_from_rc155(version: str) -> None: 'refs/heads/feature/r520-linux-155rc1-ci', 'refs/heads/security/r776-pinned-root-read-qa')) ) -if win_version != version and not private_win_rc1: - raise SystemExit('WINDOWS_PRIVATE_RC1_IDENTITY_NOT_AUTHORIZED') +# R805 is a strictly unpublished cross-platform v1.5.6 source candidate. +# It may retain the immutable public v1.5.4 RELEASE.json only on its exact +# private prep branch. It is NOT accepted as a public v1.5.6 publication. +private_win_v156 = ( + version == '1.5.4' and win_version == '1.5.6' + and win_release.get('status') == 'PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN' + and win_release.get('previousAccepted',{}).get('version') == '1.5.4' + and win_release.get('previousAccepted',{}).get('tag') == 'v1.5.4' + and win_release.get('publication',{}).get('publishedAt') is None + and (branch == 'prep/r805-windows-156' + or ref == 'refs/heads/prep/r805-windows-156') +) +if win_version != version and not (private_win_rc1 or private_win_v156): + raise SystemExit('WINDOWS_PRIVATE_CANDIDATE_IDENTITY_NOT_AUTHORIZED') if str(win_manifest.get('version')) != win_version: raise SystemExit('WINDOWS_MANIFEST_NOT_MATCHING_PLATFORM_VERSION') @@ -176,7 +188,8 @@ def require_public_stable_forward_update_from_rc155(version: str) -> None: # The Linux 1.5.5-rc1 source was already present on this private draft branch. # Its published metadata remains anchored to v1.5.4 until native acceptance. # Require exact RC1 UI bytes for private candidate and exact stable otherwise. -linux_source_expected = '1.5.5-rc1' if private_win_rc1 else version +linux_source_expected = ('1.5.5-rc1' if private_win_rc1 else + '1.5.6' if private_win_v156 else version) if ('VERSION="' + linux_source_expected + '"') not in linux_ui and ('VERSION = "' + linux_source_expected + '"') not in linux_ui: raise SystemExit('Linux UI version is inconsistent with authorized source state') diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 9bb9c724..93fee0d4 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -284,6 +284,7 @@ def idx(s,x): return s.find(x) allowed_status={ "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), + "1.5.6":("PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), } current=release_meta.get("version") check("manifest_version", current in allowed_status diff --git a/windows/RELEASE.json b/windows/RELEASE.json index 3fad5819..bb0283dd 100644 --- a/windows/RELEASE.json +++ b/windows/RELEASE.json @@ -1,9 +1,9 @@ { "schema": 1, "product": "Direct Internet Method", - "version": "1.5.5-rc1", - "status": "PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", - "date": "2026-10-13", + "version": "1.5.6", + "status": "PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", + "date": "2026-10-20", "previousAccepted": { "version": "1.5.4", "tag": "v1.5.4", @@ -137,7 +137,7 @@ "Targeted remains the default scope; All Sites is explicit opt-in.", "Windows installer remains not Authenticode-signed." ], - "exactNextAction": "Windows 1.5.5-rc1 isolated build and staged-hosted install/upgrade rollback with byte-pinned public v1.5.4; no public promotion before native acceptance.", + "exactNextAction": "Windows 1.5.6 isolated build and staged-hosted install/upgrade rollback with byte-pinned public v1.5.4; no public promotion before native acceptance.", "buildIdentityNote": "Installer and manifest hashes are intentionally not embedded here because this file is itself part of the packaged payload. Authoritative artifact identity lives in root RELEASE.json, SHA256SUMS.txt and evidence records.", "publication": { "tag": null, diff --git a/windows/gui/DirectInternetMethod.csproj b/windows/gui/DirectInternetMethod.csproj index c4be8576..0916bfbd 100644 --- a/windows/gui/DirectInternetMethod.csproj +++ b/windows/gui/DirectInternetMethod.csproj @@ -1 +1 @@ -WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.5-rc1false1.5.5.01.5.5.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file +WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.6false1.5.6.01.5.6.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index fa50d885..bdadecd5 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -132,7 +132,7 @@ async Task EnsureAdultCatalogAsync(bool force) } using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(20) }; - http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.5-rc1"); + http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.6"); Exception? lastError = null; foreach (var sourceUrl in AdultCatalogUrls) { diff --git a/windows/installer/DirectInternetMethod.iss b/windows/installer/DirectInternetMethod.iss index a7a1aa30..d5c179a4 100644 --- a/windows/installer/DirectInternetMethod.iss +++ b/windows/installer/DirectInternetMethod.iss @@ -1,5 +1,5 @@ #define MyAppName "Direct Internet Method" -#define MyAppVersion "1.5.5-rc1" +#define MyAppVersion "1.5.6" #define MyAppPublisher "Direct Internet Method" #define MyAppExeName "DirectInternetMethod.exe" @@ -7,7 +7,7 @@ AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB} AppName={#MyAppName} AppVersion={#MyAppVersion} -VersionInfoVersion=1.5.5.0 +VersionInfoVersion=1.5.6.0 AppPublisher={#MyAppPublisher} DefaultDirName={localappdata}\Programs\DirectInternetMethod UsePreviousAppDir=yes @@ -19,7 +19,7 @@ UninstallLogMode=overwrite DefaultGroupName=Direct Internet Method DisableProgramGroupPage=yes OutputDir=..\..\delivery -OutputBaseFilename=DirectInternetMethod_1.5.5-rc1_Windows_Setup +OutputBaseFilename=DirectInternetMethod_1.5.6_Windows_Setup Compression=lzma2/max SolidCompression=yes WizardStyle=modern diff --git a/windows/manifest.json b/windows/manifest.json index 2da6b41e..163beaf9 100644 --- a/windows/manifest.json +++ b/windows/manifest.json @@ -1,7 +1,7 @@ { "schema": 2, "product": "Direct Internet Method", - "version": "1.5.5-rc1", + "version": "1.5.6", "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", "offlineRuntime": true, "normalRunRequiresAdmin": false, diff --git a/windows/service/DirectInternetMethod.Service.csproj b/windows/service/DirectInternetMethod.Service.csproj index 62c8ed30..7a43b945 100644 --- a/windows/service/DirectInternetMethod.Service.csproj +++ b/windows/service/DirectInternetMethod.Service.csproj @@ -7,10 +7,10 @@ DirectInternetMethod.Service DirectInternetMethod.Service ..\app\DirectInternetMethod.ico - 1.5.5-rc1 + 1.5.6 false - 1.5.5.0 - 1.5.5.0 + 1.5.6.0 + 1.5.6.0 Direct Internet Method Service \ No newline at end of file From 413d6bcc6760e9f002377cade2347d130906be17 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:24:27 +0330 Subject: [PATCH 174/188] test(windows): accept bounded stderr diagnostics without pipe deadlock --- tests/windows_contract_audit.py | 777 ++++++++++++++++---------------- 1 file changed, 400 insertions(+), 377 deletions(-) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 93fee0d4..02a0e38b 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -1,377 +1,400 @@ -#!/usr/bin/env python3 -from pathlib import Path -import hashlib,json,re,sys,subprocess -R=Path(__file__).resolve().parents[1] -W=R/"windows" -def txt(rel): return (W/rel).read_text(encoding="utf-8-sig") -start,stop,recovery,status,iss=map(txt,["app/Start-Direct.ps1","app/Stop-Direct.ps1","app/Recovery.ps1","app/Status.ps1","installer/DirectInternetMethod.iss"]) -legacy_provider_guard=txt("app/SafeLegacyProviderRollback.ps1") -gui=txt("gui/MainWindow.xaml.cs"); xaml=txt("gui/MainWindow.xaml") -svc=txt("service/Program.cs"); client=txt("gui/ServiceClient.cs"); updater=txt("gui/UpdateClient.cs") -manifest=json.loads(txt("manifest.json")); ctrldcfg=txt("bin/ctrld/ctrld.toml"); hostlist=txt("bin/zapret/hosts.txt"); adult_fallback=txt("bin/zapret/adult-fallback-hosts.txt"); strong_override=txt("bin/zapret/strong-override-hosts.txt") -payload_builder=(R/"tests"/"build_windows_152_payload.ps1").read_text(encoding="utf-8-sig") -installer_builder=(R/"tests"/"build_windows_152_installer.ps1").read_text(encoding="utf-8-sig") -ci_workflow=(R/".github"/"workflows"/"ci.yml").read_text(encoding="utf-8-sig") -signature_gate=(R/"tests"/"windows_distribution_signature_audit.ps1").read_text(encoding="utf-8-sig") -gitattributes=(R/".gitattributes").read_text(encoding="utf-8-sig") -install_registration_guard=(R/"tests"/"windows_install_registration_audit.ps1").read_text(encoding="utf-8-sig") -native_r535_uninstall=(R/"tests"/"r535_windows_native_rollback.ps1").read_text(encoding="utf-8-sig") -installed_release_verifier=(R/"tests"/"windows_installed_release_audit.ps1").read_text(encoding="utf-8-sig") -D={"schema":2,"status":"PASS","checks":{},"hashes":{}} -def check(name,cond): - D["checks"][name]=bool(cond) - if not cond:D["status"]="FAIL" -def idx(s,x): return s.find(x) - -check("r535_registered_uninstaller_suffix_agnostic", all(x in native_r535_uninstall for x in ( - "function ExactRegisteredUninstaller([object]$d)", - "unins[0-9]{3}[.](exe|dat|msg)", - "'ONE_UNINSTALLER'", "'MATCHED_UNINSTALL_LOG'", - "'EXACT_UNINSTALL_PATH_OWNED'", - "[void](ExactRegisteredUninstaller $d)", - "$uninstaller=ExactRegisteredUninstaller $d", -)) and "$uninstaller=Join-Path $App 'unins000.exe'" not in native_r535_uninstall) - -check("start_refuses_broad_tunnel_before_mutation", idx(start,"REFUSE_BROAD_TUNNEL_ROUTE")>=0 and idx(start,"REFUSE_BROAD_TUNNEL_ROUTE") < idx(start,"New-NetIPAddress")) -check("loopback_index_dynamic", all("DestinationPrefix '::1/128'" in x and "$LoopbackIndex=[int]$LoopbackRoute.InterfaceIndex" in x and "$LoopbackIndex=1" not in x for x in (start,stop,recovery)) and "DestinationPrefix '::1/128'" in status and "InterfaceIndex 1" not in status) -check("no_adapter_dns_set", "Set-DnsClientServerAddress" not in start+stop+recovery) -check("no_default_route_create", not re.search(r'(New-NetRoute|route\.exe\s+add|netsh\s+interface\s+ipv4\s+add\s+route)',start,re.I)) -check("direct_multiprotocol_engine", all(x in start for x in ( - "$DirectMethods=@('encrypted-dns-doh','http-host-split-tcp80','tls-sni-desync-tcp443','quic-desync-udp443','custom-hostlist','adult-catalog','strategy-profile')", - "'--wf-tcp=80,443'","'--wf-udp=443'","'--filter-tcp=80'","'--filter-tcp=443'","'--filter-udp=443'","'--filter-l7=quic'", - "'--dpi-desync-split-pos=method+2'","'--dpi-desync-split-pos=1,midsld'","'--dpi-desync-repeats=6'","'--new'" -))) -check("direct_multiprotocol_scope_controlled", "Add-ScopeHost" in start and "$scope='targeted'" in start and "all-sites" in start and "--hostlist-auto=" not in start) -check("secure_dns_no_os_leak", 'leak_on_upstream_failure = false' in ctrldcfg) -check("start_adapter_dns_regression", "ADAPTER_DNS_CHANGED" in start) -check("start_winhttp_regression", "WINHTTP_PROXY_CHANGED" in start) -check("start_ics_regression", "ICS_CHANGED_LIVE" in start and "ICS_CHANGED_DURING_DNS_STAGE" in start) -check("start_rollback", "Rollback $state" in start and "START_FAIL_" in start) -check("start_exact_process_argument_boundaries", "Start-ExactProcess" in start and "ProcessStartInfo" in start and "$psi.ArgumentList.Add" in start and "Start-Process -FilePath $Ctrld" not in start and "Start-Process -FilePath $Winws" not in start) -check("ctrld_scm_owned_service_architecture", "$CtrldServiceName='ctrld'" in start and "Start-OwnedCtrldService" in start and "CTRLD_SERVICE_PROCESS_OWNERSHIP_MISMATCH" in start and "$cp=Start-ExactProcess $Ctrld" not in start) -check("ctrld_external_service_fail_closed", "REFUSE_EXTERNAL_CTRLD_SERVICE" in start and "REFUSE_EXTERNAL_CTRLD_SERVICE" in stop and "REFUSE_EXTERNAL_CTRLD_SERVICE" in recovery) -check("ctrld_stop_recovery_service_managed", "Stop-OwnedCtrldService" in stop and "Stop-OwnedCtrldService" in recovery) -check("stop_preserves_external_routes", "STOP_MUTATED_EXTERNAL_ROUTES" in stop) -check("stop_preserves_external_nrpt", "STOP_MUTATED_EXTERNAL_NRPT" in stop) -check("stop_preserves_adapter_dns", "STOP_MUTATED_ADAPTER_DNSV4" in stop and "STOP_MUTATED_ADAPTER_DNSV6" in stop) -check("stop_no_state_does_not_remove_ula", "ULA is not removed without state ownership evidence." in stop) -check("recovery_state_root_guard", "STATE_ROOT_OWNERSHIP_MISMATCH" in recovery) -check("recovery_exact_orphan_ula_guard", all(x in recovery for x in ("ULA_OWNERSHIP_AMBIGUOUS","ULA_OWNERSHIP_MISMATCH","$ownedUla","Remove-NetIPAddress","RECOVERY_INCOMPLETE"))) -check("status_pid_name_ownership", "Get-PidHealth" in status and "ProcessName" in status) -check("status_external_conflict", all(x in status for x in ("CONFLICT","BLOCKED","VPN/tunnel"))) -check("status_fast_primitives", "Win32_SystemDriver" not in status and "Win32_Service" not in status) - -check("ui_native_exe_hidden_status_backend", "bundledPwshPath" in gui and '"Privileged", "runtime", "pwsh", "pwsh.exe"' in gui and "CreateNoWindow = true" in gui and "UseShellExecute = false" in gui) -check("ui_no_external_pwsh_dependency", 'ProcessStartInfo("pwsh.exe"' not in gui) -check("ui_no_direct_runas", 'Verb = "runas"' not in gui) -check("ui_uses_service_client", 'ServiceClient.Send(action)' in gui) -check("ui_waits_service_completion", "WaitForActionAsync" in gui and "action-status.json" in gui) -check("ui_blocked_state", '"BLOCKED"' in gui and "Disconnect external VPN/tunnel to Start" in gui) -check("ui_vector_hero", "' in xaml and 'TextWrapping="Wrap"' in xaml and '' in xaml) -check("target_hostlist_scope", "^gemini.google.com" in hostlist.splitlines() and all(x not in hostlist.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xnxx.com","xhamster.com")) and all(x in adult_fallback.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xvideos-cdn.com","xvcdn.com","xnxx.com","xnxx-cdn.com","xhamster.com","xhcdn.com","redtube.com","rdtcdn.com"))) -check("critical_global_services_hostlist", all(x in hostlist.splitlines() for x in ("openai.com","chatgpt.com","oaistatic.com","oaiusercontent.com","github.com","githubusercontent.com","githubassets.com","reddit.com","redditstatic.com","redditmedia.com","redd.it"))) -check("custom_site_hostlist", all(x in start for x in ("$CustomHosts=Join-Path $UserConfigDir 'custom-hosts.txt'","$RuntimeHostList=Join-Path $env:ProgramData 'DirectInternetMethod\\runtime-hosts.txt'","USER_CONFIG_INVALID","WriteAllLines($RuntimeHostList")) and all(x in gui for x in ("CustomSites_Click","NormalizeCustomSite","customHostsPath","Maximum 256 entries")) and 'Content="Custom Sites"' in xaml and 'DirectInternetMethod\\UserConfig' in iss and 'users-modify' in iss) -check("strategy_profiles", all(x in start for x in ("$StrategyFile=Join-Path $UserConfigDir 'strategy.txt'","balanced","compatibility","strong","fakedsplit","hostfakesplit","sniext+1","STRATEGY_INVALID")) and all(x in gui for x in ("Strategy_Click","strategyPath","Compatibility","Strong")) and 'Content="Strategy"' in xaml) -check("all_sites_scope_optional_default_targeted", all(x in start for x in ("$ScopeFile=Join-Path $UserConfigDir 'scope.txt'","$scope='targeted'","all-sites","SCOPE_INVALID","Add-ScopeHost")) and all(x in gui for x in ("scopePath","Apply DPI strategy to all web sites (experimental)","all-sites","targeted"))) -check("ui_gemini_adult_live_checks", all(x in xaml for x in ('Text="Gemini"','Text="Adult coverage"','x:Name="AdultSiteCheckToggle"','x:Name="GeminiText"','x:Name="AdultSiteText"')) and all(x in gui for x in ("https://www.pornhub.com/","https://www.xvideos.com/","https://www.xnxx.com/","https://xhamster.com/","FmtAdult")) and "pornhub" not in xaml.lower()) -check("ui_adult_check_default_off_persisted", all(x in gui for x in ("adultSiteLiveCheck","settings.json","AdultSiteCheckToggle_Changed")) and "bool adultCheckEnabled;" in gui) -check("adult_catalog_opt_in", all(x in gui for x in ("adultCoverageEnabled","adult-enabled.txt","adult-hosts.txt","nsfw-onlydomains.txt","EnsureAdultCatalogAsync","Adult catalog is unexpectedly small","xvideos.com","xnxx.com","xhamster.com")) and all(x in start for x in ("$AdultEnabledFile","$AdultHosts","$AdultFallback","adultHostCount","ADULT_CATALOG_INVALID")) and 'adult-fallback-hosts.txt' in iss) -check("startup_core_site_health_fields", all(x in start for x in ( - "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", - "required=2", - "passed=3-$failed.Count", - "allCoreSitesOk=($failed.Count -eq 0)", - "unreachableCoreSites=@($failed.ToArray())", - "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", - "$httpsProbePassCount=[int]$httpsHealth.passed", - "$ev.status='PASS_ACTIVE'", -))) -check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) -check("startup_true_youtube_page_plus_204_required", all(x in start for x in ( - "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", - "if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')}", - "$ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\\|' 8 2", - "$yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\\|' 8 2", - "$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh", - "youtubePage=$ytPage;youtube204=$yt", - "$httpsProbePassCount -lt 2", - "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", -)) and "function Get-CoreSiteHealth($yt,$oa,$gh)" not in start) -check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) -check("ui_openai_403_cannot_be_chatgpt_login_pass", - 'FmtOpenAiApiProbe(openAiProbe)' in gui - and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" })' in gui - and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401", "403" })' not in gui - and '"403" when p.Reached => "API HTTP 403 forbidden · login unverified"' in gui - and '"401" when p.Reached => "API HTTP 401 auth required · login unverified"' in gui - and '"200" when p.Reached => "API HTTP 200 reachable · login unverified"' in gui - and '"API transport unavailable · login unverified"' in gui) - -check("ui_live_probe_semantics", "FAIL · HTTP" in gui and 'https://gemini.google.com/' in gui and "adultTasks is not null" in gui and "full pages" in gui) -check("ui_partial_active_core_reachability", all(x in gui for x in ( - "bool coreReachabilityProven = false;", - "var youtubeProbe = await ytTask;", - "var openAiProbe = await oaTask;", - "var githubProbe = await ghTask;", - "var youtube204Probe = await yt204Task;", - "coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe);", - 'mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)', - 'StatusBadge.Text = "PARTIAL";', - "StatusBadge.Foreground = warn;", - "at least one core site unreachable", - 'StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED";', -))) - -check("ui_youtube_homepage_and_204_independent_health", all(x in gui for x in ( - 'var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });', - 'var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" });', - 'var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask };', - 'FullCoreReachability(ProbeResult youtubePage, ProbeResult youtube204, ProbeResult openai, ProbeResult github)', - 'youtubePage.Ok && youtube204.Ok && openai.Ok && github.Ok', - 'YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}";', - 'StatusBadge.Text = "PARTIAL";' -)) and "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;" not in gui) -check("ui_direct_update_service_handoff", 'ServiceClient.Send("update")' in gui and 'ServiceClient.Send("recovery")' in gui and '"handoff"' in gui and "300000" in gui and "DownloadVerifiedAsync" not in gui and "LaunchInstaller" not in gui) -check("ui_update_advisory_release_digest", "InstallerDigest" in updater and "digest" in updater and "SHA256SUMS.txt" in updater and "DownloadVerifiedAsync" not in updater and "Process.Start" not in updater) - -check("service_custom_controls", all(x in svc for x in ("CTRL_START = 128","CTRL_STOP = 129","CTRL_RECOVERY = 130","CTRL_UPDATE = 131")) and "case CTRL_UPDATE:" in svc and "_ = Task.Run(RunUpdateAsync)" in svc) -check("service_direct_update_security", all(x in svc for x in ("LatestReleaseApi","SHA256SUMS.txt","ApiSha256","SHA256.HashDataAsync","CryptographicException","GetLatestUpdateAsync","ParseExpectedSha256")) and "expected.Equals(package.ApiSha256" in svc and "actual.Equals(expected" in svc) -check("service_direct_update_atomic_download", '".part"' in svc and "DownloadWithSystemCurlAsync" in svc and '"-L", "--fail", "--silent", "--show-error"' in svc and '"--max-time", "210"' in svc and "File.Move(partPath, finalPath);" in svc and "OwnedUpdateInstallerPath" in svc and "Prior update still needs verification" in svc and "update-incomplete-preserved" in svc) -check("service_direct_update_silent_installer", all(x in svc for x in ('"/VERYSILENT"','"/SUPPRESSMSGBOXES"','"/NORESTART"','"/NORESTARTAPPLICATIONS"','"/CLOSEAPPLICATIONS"','"/SP-"')) and "UseShellExecute = false" in svc and 'Verb = "runas"' not in svc) -check("service_direct_update_requires_clean_off", "NetworkStatePath" in svc and "Direct Method must be OFF before update." in svc) -check("service_direct_update_post_restart_finalize", - all(x in svc for x in ("FinalizePendingAfterServiceRestartAsync", - "_ = Task.Run(FinalizePendingAfterServiceRestartAsync);", - "FinalizePendingUpdate();", - "pending-update-finalization-deferred-no-overwrite", - "update-stage-file-locked-preserving-single-pending-slot", - "pending-update.json", '"update", "done"', "targetVersion")) - and "StopEvent.IsSet" in svc) -check("service_direct_update_preserves_registered_user_install", "GetRegisteredInstallDirectory" in svc and "Registry.LocalMachine.OpenSubKey" in svc and "InstallLocation" in svc and '"/DIR=" + installDir' in svc and "DirectInternetMethod.exe" in svc and "manifest.json" in svc) -check("service_action_gate", "SemaphoreSlim ActionGate" in svc and "WaitAsync(0)" in svc) -check("service_action_status", "action-status.json" in svc and "WriteActionStatus" in svc and 'phase = "done"' not in svc) -check("service_fixed_scripts", all(x in svc for x in ('RunActionAsync("Start-Direct.ps1"','RunActionAsync("Stop-Direct.ps1"','RunActionAsync("Recovery.ps1"'))) -check("service_hidden_backend", "CreateNoWindow = true" in svc and "WindowStyle = ProcessWindowStyle.Hidden" in svc) -check("service_bundled_pwsh_only", '"runtime", "pwsh", "pwsh.exe"' in svc and 'return File.Exists(p) ? p : "pwsh.exe"' not in svc) -check("service_action_timeout_guard", "WaitForExit(120000)" in svc and "Kill(entireProcessTree: true)" in svc and "exceeded the 120 second safety timeout" in svc) -check("service_action_completion_no_inherited_pipe_deadlock", "RedirectStandardOutput = true" not in svc and "RedirectStandardError = true" not in svc and "ReadToEndAsync" not in svc and 'Log($"action-end:{actionName}:exit={p.ExitCode}")' in svc) -check("ui_wait_timeout_exceeds_service_timeout", "int timeoutMs = 130000" in gui) -check("client_minimum_access", "SERVICE_QUERY_STATUS | SERVICE_USER_DEFINED_CONTROL" in client and "SERVICE_CHANGE_CONFIG" not in client and "SERVICE_START" not in client and "SERVICE_STOP" not in client) -check("installer_service_acl_interactive_users_minimum", "(A;;LCCR;;;IU)" in iss and "sdset" in iss and "(A;;LCCR;;;AU)" not in iss) -check("installer_service_protected_root", "{commonpf}\\DirectInternetMethod\\Privileged" in iss) -check("single_registered_Windows_install_and_preserve_user_evidence", - 'AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}' in iss - and "UsePreviousAppDir=yes" in iss and "UninstallLogMode=overwrite" in iss - and "DIMExistingAppPathConflicts()" in iss - and "RegQueryStringValue(HKLM, Key, 'InstallLocation', OldDir)" in iss - and r'Type: filesandordirs; Name: "{app}\evidence"' not in iss - and r'Type: filesandordirs; Name: "{app}\bin"' not in iss - and r'Type: filesandordirs; Name: "{commonpf}\DirectInternetMethod\Privileged"' not in iss - and r'Type: dirifempty; Name: "{commonpf}\DirectInternetMethod\Privileged"' in iss) -check("owned_update_cache_never_duplicates_versioned_installers", - all(x in svc for x in ("OwnedUpdateInstallerPath", "DirectInternetMethod-update-stage.exe", - "Previous update is pending" if False else "Prior update still needs verification", - "File.Move(partPath, finalPath);", - "update-incomplete-preserved", - "update-stage-file-locked-preserving-single-pending-slot")) - and "File.Move(partPath, finalPath, true)" not in svc) - -check("legacy_provider_rollback_explicit_only_and_exact_SHA", - all(k in legacy_provider_guard for k in ( - "PUBLISHED_STABLE_151_INSTALLER_SHA_CHANGED", "PRESERVE_CUSTOM_PROVIDER_BEFORE_LEGACY_151_REINSTALL", - "PASS_LEGACY_151_PROVIDER_RESTORED_SHA", "FOREIGN_POST_STAGE_PROVIDER_EDIT_PRESERVED", - "PENDING_SLOT_EXISTS_NO_BLIND_REPLAY", "RESTORED_PROVIDER_SHA_FAILED", - "LEGACY_151_BINARY_POSTINSTALL_SHA_NOT_ACCEPTED")) - and "Remove-Item -Recurse" not in legacy_provider_guard - and 'Source: "..\\app\\SafeLegacyProviderRollback.ps1"; DestDir: "{commonpf}\\DirectInternetMethod\\Privileged\\app"; Flags: ignoreversion' in iss - and ("privileged","app/SafeLegacyProviderRollback.ps1") in - {(v["scope"],v["file"]) for v in manifest["files"]}) -check("legacy_151_rollback_preserves_custom_bin_tree", - all(x in legacy_provider_guard for x in ["binFiles","BIN_RESTORATION_SHA_FAILED","FOREIGN_BIN_EDIT_PRESERVED","BIN_SNAPSHOT_UNKNOWN_CONTENT","BIN_STAGED_FILE_CHANGED","FOREIGN_STAGE_SYMLINK"]) - and "Remove-Item -Recurse" not in legacy_provider_guard) -check("inno_preserves_custom_router_gateway_provider_on_upgrade", - 'Source: "..\\..\\router_gateway\\providers.json"; DestDir: "{app}\\app\\router_gateway"; Flags: ignoreversion onlyifdoesntexist' in iss) -check("installer_admin_only_install", "PrivilegesRequired=admin" in iss) -check("installer_service_recovery", 'failure "' in iss and "failureflag" in iss) -check("installer_preserves_pending_update_until_service_SHA_verifies", - "CompletePendingDirectUpdate" in iss and "pending-update.json" in iss - and "new service completes SHA-checked cleanup" in iss - and r"PendingPath := ExpandConstant('{commonappdata}\DirectInternetMethod\pending-update.json')" in iss - and "DeleteFile(PendingPath)" not in iss - and '"action":"update","phase":"done"' not in iss) -check("installer_ctrld_owned_demand_service", "CtrldServiceName = 'ctrld'" in iss and "InstallOrUpdateCtrldService" in iss and "start= demand" in iss and "CurrentControlSet\\Services\\" in iss) -check("installer_ctrld_collision_fail_closed", "A different Windows service named ctrld already exists" in iss and "CtrldServiceOwned()" in iss) -check("installer_ctrld_owned_uninstall", "StopCtrldServiceIfOwned" in iss and "delete \"" in iss and "CtrldServiceName" in iss) -check("installer_shortcuts_exe", 'DirectInternetMethod.exe' in iss and 'ControlPanel.cmd' not in "\n".join(x for x in iss.splitlines() if x.startswith("Name:"))) -check("installer_active_guard", "PrepareToInstall" in iss and "DirectDnsDpiHarness" in iss and "state.json" in iss) -check("installer_bundled_pwsh", '..\\vendor\\pwsh\\*' in iss and 'Privileged\\runtime\\pwsh' in iss and "PowerShell7Available" not in iss) -check("installer_uninstall_recovery_gate", "InitializeUninstall" in iss and "Recovery did not complete" in iss) -check("windows_install_registration_guard", all(x in install_registration_guard for x in ("HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","noSameAppIdPerUserDuplicate","registeredUninstallerPresent","installedReleaseMatches"))) -check("installed_release_verifier_delegates_registration_guard", "windows_install_registration_audit.ps1" in installed_release_verifier and "Where-Object DisplayName" not in installed_release_verifier and "pwsh.exe" in installed_release_verifier) -check("windows_build_shared_exclusive_lock", all(x in payload_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED")) and all(x in installer_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED"))) -check("windows_build_python_portability_failfast", all(x in payload_builder for x in ( - "param([switch]$PreflightOnly)", - "WINDOWS_BUILD_PYTHON_MISSING:", - "WINDOWS_BUILD_VENDOR_PWSH_SHA_INVALID", - "WINDOWS_BUILD_PORTABLE_PYTHON_SHA_INVALID", - "4942B86A6597E5AEE0128DAA00050ED79BC21F6E709A78EB19CBFEB0C2F39AC9", - r"& $PythonExe '.\tests\build_windows_manifest.py'", - r"& $PythonExe '.\tests\windows_contract_audit.py'", -)) and "& py " not in payload_builder and - payload_builder.index("WINDOWS_BUILD_PYTHON_MISSING:") < payload_builder.index("& dotnet publish")) -check("windows_installer_output_stability_guard", all(x in installer_builder for x in ("OUTPUT_STILL_LOCKED_AFTER_ISCC_EXIT","@($Sizes | Select-Object -Unique).Count","OUTPUT_SIZE_UNSTABLE_","outputStableSamples"))) -tracked_strong=subprocess.run(["git","ls-files","--error-unmatch","windows/bin/zapret/strong-override-hosts.txt"],cwd=R,text=True,capture_output=True).returncode==0 -check("windows_strong_override_source_tracked", tracked_strong) -check("windows_hash_pinned_text_eol_policy", all(x in gitattributes for x in ("windows/bin/zapret/hosts.txt text eol=crlf","windows/bin/zapret/adult-fallback-hosts.txt text eol=lf","windows/bin/zapret/strong-override-hosts.txt text eol=crlf"))) -check("optional_signed_diagnostic_fails_closed_but_is_not_public_gate", all(x in signature_gate for x in ( - "Get-AuthenticodeSignature", - "Assert-EligibleSignerForSmartAppControl -Signature $sig", - "if([string]$Signature.Status -ne 'Valid')", - "SELF_SIGNED_SIGNER_NOT_PUBLIC_TRUSTED", - "SMART_APP_CONTROL_RSA_SIGNER_REQUIRED", - "CODE_SIGNING_EKU_REQUIRED", - "PUBLIC_TRUST_CHAIN_UNVERIFIED", - "FAIL_CLOSED","exit 42" -))) -tag_job_source = ci_workflow.split(" windows-release-contract:",1)[1] if ci_workflow.count(" windows-release-contract:")==1 else "" -check("tag_release_invokes_real_unsigned_three_way_sha_and_hosted_rollback", - tag_job_source.count("python -B tests/unsigned_public_release_contract.py --public-tag")==1 - and "if: startsWith(github.ref, 'refs/tags/v')" in tag_job_source - and "GH_TOKEN:" in tag_job_source and "GITHUB_TOKEN:" in tag_job_source - and "actions: read" in tag_job_source - and "windows_distribution_signature_audit.ps1" not in tag_job_source - and "windows_release_draft_asset_stage.ps1" not in tag_job_source - and "continue-on-error: true" not in tag_job_source - and "if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }" in tag_job_source) - -draft_stage_source=(R/"tests"/"windows_release_draft_asset_stage.ps1").read_text(encoding="utf-8-sig") -draft_fixture_source=(R/"tests"/"windows_release_draft_asset_contract.ps1").read_text(encoding="utf-8-sig") -windows_source_section=ci_workflow.split(" windows-source:",1)[1].split(" windows-release-contract:",1)[0] -check("windows_ci_runs_offline_draft_staging_negative_regressions", - windows_source_section.count("pwsh.exe -NoProfile -NonInteractive -File tests/windows_release_draft_asset_contract.ps1")==1 - and "continue-on-error: true" not in windows_source_section) -check("windows_source_ci_runs_unsigned_release_negative_fixtures", - windows_source_section.count("python tests/unsigned_public_release_contract.py --fixtures")==1) -check("unsigned_publication_never_requires_private_signed_draft_staging", - "windows_release_draft_asset_stage.ps1" not in tag_job_source - and "windows_distribution_signature_audit.ps1" not in tag_job_source - and "unsigned_public_release_contract.py --fixtures" in tag_job_source - and "unsigned_public_release_contract.py --public-tag" in tag_job_source - and "continue-on-error: true" not in tag_job_source) -check("unsigned_verifier_checks_remote_sha_and_raw_hosted_receipts", - all(s in (R/"tests"/"unsigned_public_release_contract.py").read_text(encoding="utf-8-sig") for s in ( - "PUBLIC_THREE_WAY_SHA_DISAGREEMENT", "HOSTED_RAW_REPORT_SHA_MISMATCH", - "HOSTED_REPORT_UNRESOLVED_ERRORS", "GITHUB_RUN_NOT_SUCCESS", - "SIGNING_POLICY_MUST_NOT_BE_REQUIRED"))) - -check("draft_release_stager_fails_closed_on_identity_digest_and_rerun", - all(s in draft_stage_source for s in ( - "RELEASE_NOT_PRIVATE_DRAFT","RELEASE_TARGET_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DIGEST_MISMATCH", - "SIGNED_DRAFT_INSTALLER_ASSET_ORIGIN_MISMATCH","UNSAFE_RERUN_STAGING_PATH_OCCUPIED", - "TAG_CHECKOUT_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DOWNLOADED_SHA_MISMATCH","exit 42"))) -check("draft_release_negative_fixtures_present", - all(s in draft_fixture_source for s in ( - "Deny 'published'","Deny 'tag'","Deny 'commit'","Deny 'duplicate'", - "Deny 'digest'","Deny 'origin'","PASS 10/10"))) - -check("windows_ci_external_audits_fail_fast", ci_workflow.count("if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }") >= 7) - -files={(x["scope"],x["file"]) for x in manifest["files"]} -release_meta=json.loads(txt("RELEASE.json")) -allowed_status={ - "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), - "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), - "1.5.6":("PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), -} -current=release_meta.get("version") -check("manifest_version", current in allowed_status - and manifest.get("version")==current - and release_meta.get("status")==allowed_status[current][0] - and release_meta.get("previousAccepted",{}).get("version")==allowed_status[current][1]) - -check("manifest_direct_update", manifest.get("directUpdate",{}).get("userUacRequired") is False and "SHA256SUMS.txt" in manifest.get("directUpdate",{}).get("integrity","")) -check("manifest_native_exe", ("user","app/DirectInternetMethod.exe") in files) -check("manifest_router_gateway_data", ("user","app/router_gateway/providers.json") in files) -check("manifest_adult_fallback", ("privileged","bin/zapret/adult-fallback-hosts.txt") in files) -check("manifest_strong_override", ("privileged","bin/zapret/strong-override-hosts.txt") in files) -check("manifest_service", ("privileged","app/DirectInternetMethod.Service.exe") in files) -check("manifest_no_helper", not any(x[1].endswith("Helper.exe") for x in files)) -check("manifest_bundled_pwsh", manifest.get("bundledPowerShellVersion")=="7.6.6" and manifest.get("bundledPowerShellArchiveSha256")=="02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860" and ("privileged","runtime/pwsh/pwsh.exe") in files) -check("manifest_bundled_pwsh_tree", sum(1 for s,f in files if s=="privileged" and f.startswith("runtime/pwsh/")) >= 650) -check("manifest_no_legacy_launchers", not any(scope=="user" and (f.endswith(".cmd") or f in {"app/ControlPanel.ps1","app/Toggle.ps1"}) for scope,f in files)) - -pin_paths={ -"ctrld.exe":"bin/ctrld/ctrld.exe", -"ctrld.toml":"bin/ctrld/ctrld.toml", -"winws.exe":"bin/zapret/winws.exe", -"WinDivert.dll":"bin/zapret/WinDivert.dll", -"WinDivert64.sys":"bin/zapret/WinDivert64.sys", -"cygwin1.dll":"bin/zapret/cygwin1.dll", -"hosts.txt":"bin/zapret/hosts.txt", -"adult-fallback-hosts.txt":"bin/zapret/adult-fallback-hosts.txt", -"strong-override-hosts.txt":"bin/zapret/strong-override-hosts.txt", -} -start_pins=dict(re.findall(r"'([^']+)'='([0-9A-F]{64})'",start)) -pin_actual={k:hashlib.sha256((W/v).read_bytes()).hexdigest().upper() for k,v in pin_paths.items()} -D["selfIntegrityPins"]={"declared":{k:start_pins.get(k) for k in pin_paths},"actual":pin_actual} -check("start_self_integrity_pins",all(start_pins.get(k)==v for k,v in pin_actual.items())) - -expected={ -"ctrld":"FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD", -"winws":"A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8", -"windivertdll":"C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2", -"windivertsys":"8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2", -"pwsh":"BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285"} -for name,rel in (("ctrld","bin/ctrld/ctrld.exe"),("winws","bin/zapret/winws.exe"),("windivertdll","bin/zapret/WinDivert.dll"),("windivertsys","bin/zapret/WinDivert64.sys"),("pwsh","vendor/pwsh/pwsh.exe")): - h=hashlib.sha256((W/rel).read_bytes()).hexdigest().upper();D["hashes"][name]=h;check("runtime_"+name+"_pin",h==expected[name]) - -thirdparty_notice_hashes={ - "LICENSE.WinDivert.txt":"14A0CB5214D536E4FDAE6AA3F5696F981EEDA106CD026E9794BBA489EE79D628", - "LICENSE.Cygwin.txt":"794433752103CF4BBB4A84A1BDB8FBC150ABB1762704BB35FECC9F7F820BE984", - "LGPL3.Cygwin.txt":"DA7EABB7BAFDF7D3AE5E9F223AA5BDC1EECE45AC569DC21B3B037520B4464768", - "GPL3.Cygwin.txt":"8CEB4B9EE5ADEDDE47B31E975C1D90C73AD27B6B165A1DCD80C7C545EB65B903", -} -def checked_notice_mapping(filename): - src = r"..\bin\zapret" + "\\" + filename - dest = r"{commonpf}\DirectInternetMethod\Privileged\bin\zapret" - line = 'Source: "' + src + '"; DestDir: "' + dest + '"; Flags: ignoreversion notimestamp' - return iss.splitlines().count(line)==1 -def r549_inno_source_timestamp_contract(source): - try: - rows_section=source.split("[Files]\n",1)[1].split("[InstallDelete]",1)[0] - except IndexError: - return False - rows=[row.strip() for row in rows_section.splitlines() - if row.strip().startswith("Source: ")] - if len(rows)!=28 or len(set(rows))!=28: - return False - for row in rows: - if "Flags:" not in row: - return False - flags=row.split("Flags:",1)[1].strip().split() - if flags.count("notimestamp")!=1 or "touch" in flags or "comparetimestamp" in flags: - return False - return True - -# Negative-first source-time fixture: exactly 28 embedded records without -# stored input timestamps. No change to owned uninstall/upgrade file scope. -check("r549_inno_no_source_timestamps", - r549_inno_source_timestamp_contract(iss) - and not r549_inno_source_timestamp_contract(iss.replace(" notimestamp","",1)) - and not r549_inno_source_timestamp_contract( - iss.replace(" notimestamp"," notimestamp touch",1))) - -check("installer_four_upstream_license_notices_byte_pinned", - all( - (W/"bin"/"zapret"/filename).is_file() - and hashlib.sha256((W/"bin"/"zapret"/filename).read_bytes()).hexdigest().upper()==expected - and checked_notice_mapping(filename) - and "windows/bin/zapret/"+filename+" text eol=lf" in gitattributes - for filename,expected in thirdparty_notice_hashes.items() - )) - -print(json.dumps(D,indent=2)) -sys.exit(0 if D["status"]=="PASS" else 20) +#!/usr/bin/env python3 +from pathlib import Path +import hashlib,json,re,sys,subprocess +R=Path(__file__).resolve().parents[1] +W=R/"windows" +def txt(rel): return (W/rel).read_text(encoding="utf-8-sig") +start,stop,recovery,status,iss=map(txt,["app/Start-Direct.ps1","app/Stop-Direct.ps1","app/Recovery.ps1","app/Status.ps1","installer/DirectInternetMethod.iss"]) +legacy_provider_guard=txt("app/SafeLegacyProviderRollback.ps1") +gui=txt("gui/MainWindow.xaml.cs"); xaml=txt("gui/MainWindow.xaml") +svc=txt("service/Program.cs"); client=txt("gui/ServiceClient.cs"); updater=txt("gui/UpdateClient.cs") +manifest=json.loads(txt("manifest.json")); ctrldcfg=txt("bin/ctrld/ctrld.toml"); hostlist=txt("bin/zapret/hosts.txt"); adult_fallback=txt("bin/zapret/adult-fallback-hosts.txt"); strong_override=txt("bin/zapret/strong-override-hosts.txt") +payload_builder=(R/"tests"/"build_windows_152_payload.ps1").read_text(encoding="utf-8-sig") +installer_builder=(R/"tests"/"build_windows_152_installer.ps1").read_text(encoding="utf-8-sig") +ci_workflow=(R/".github"/"workflows"/"ci.yml").read_text(encoding="utf-8-sig") +signature_gate=(R/"tests"/"windows_distribution_signature_audit.ps1").read_text(encoding="utf-8-sig") +gitattributes=(R/".gitattributes").read_text(encoding="utf-8-sig") +install_registration_guard=(R/"tests"/"windows_install_registration_audit.ps1").read_text(encoding="utf-8-sig") +native_r535_uninstall=(R/"tests"/"r535_windows_native_rollback.ps1").read_text(encoding="utf-8-sig") +installed_release_verifier=(R/"tests"/"windows_installed_release_audit.ps1").read_text(encoding="utf-8-sig") +D={"schema":2,"status":"PASS","checks":{},"hashes":{}} +def check(name,cond): + D["checks"][name]=bool(cond) + if not cond:D["status"]="FAIL" +def idx(s,x): return s.find(x) + +check("r535_registered_uninstaller_suffix_agnostic", all(x in native_r535_uninstall for x in ( + "function ExactRegisteredUninstaller([object]$d)", + "unins[0-9]{3}[.](exe|dat|msg)", + "'ONE_UNINSTALLER'", "'MATCHED_UNINSTALL_LOG'", + "'EXACT_UNINSTALL_PATH_OWNED'", + "[void](ExactRegisteredUninstaller $d)", + "$uninstaller=ExactRegisteredUninstaller $d", +)) and "$uninstaller=Join-Path $App 'unins000.exe'" not in native_r535_uninstall) + +check("start_refuses_broad_tunnel_before_mutation", idx(start,"REFUSE_BROAD_TUNNEL_ROUTE")>=0 and idx(start,"REFUSE_BROAD_TUNNEL_ROUTE") < idx(start,"New-NetIPAddress")) +check("loopback_index_dynamic", all("DestinationPrefix '::1/128'" in x and "$LoopbackIndex=[int]$LoopbackRoute.InterfaceIndex" in x and "$LoopbackIndex=1" not in x for x in (start,stop,recovery)) and "DestinationPrefix '::1/128'" in status and "InterfaceIndex 1" not in status) +check("no_adapter_dns_set", "Set-DnsClientServerAddress" not in start+stop+recovery) +check("no_default_route_create", not re.search(r'(New-NetRoute|route\.exe\s+add|netsh\s+interface\s+ipv4\s+add\s+route)',start,re.I)) +check("direct_multiprotocol_engine", all(x in start for x in ( + "$DirectMethods=@('encrypted-dns-doh','http-host-split-tcp80','tls-sni-desync-tcp443','quic-desync-udp443','custom-hostlist','adult-catalog','strategy-profile')", + "'--wf-tcp=80,443'","'--wf-udp=443'","'--filter-tcp=80'","'--filter-tcp=443'","'--filter-udp=443'","'--filter-l7=quic'", + "'--dpi-desync-split-pos=method+2'","'--dpi-desync-split-pos=1,midsld'","'--dpi-desync-repeats=6'","'--new'" +))) +check("direct_multiprotocol_scope_controlled", "Add-ScopeHost" in start and "$scope='targeted'" in start and "all-sites" in start and "--hostlist-auto=" not in start) +check("secure_dns_no_os_leak", 'leak_on_upstream_failure = false' in ctrldcfg) +check("start_adapter_dns_regression", "ADAPTER_DNS_CHANGED" in start) +check("start_winhttp_regression", "WINHTTP_PROXY_CHANGED" in start) +check("start_ics_regression", "ICS_CHANGED_LIVE" in start and "ICS_CHANGED_DURING_DNS_STAGE" in start) +check("start_rollback", "Rollback $state" in start and "START_FAIL_" in start) +check("start_exact_process_argument_boundaries", "Start-ExactProcess" in start and "ProcessStartInfo" in start and "$psi.ArgumentList.Add" in start and "Start-Process -FilePath $Ctrld" not in start and "Start-Process -FilePath $Winws" not in start) +check("ctrld_scm_owned_service_architecture", "$CtrldServiceName='ctrld'" in start and "Start-OwnedCtrldService" in start and "CTRLD_SERVICE_PROCESS_OWNERSHIP_MISMATCH" in start and "$cp=Start-ExactProcess $Ctrld" not in start) +check("ctrld_external_service_fail_closed", "REFUSE_EXTERNAL_CTRLD_SERVICE" in start and "REFUSE_EXTERNAL_CTRLD_SERVICE" in stop and "REFUSE_EXTERNAL_CTRLD_SERVICE" in recovery) +check("ctrld_stop_recovery_service_managed", "Stop-OwnedCtrldService" in stop and "Stop-OwnedCtrldService" in recovery) +check("stop_preserves_external_routes", "STOP_MUTATED_EXTERNAL_ROUTES" in stop) +check("stop_preserves_external_nrpt", "STOP_MUTATED_EXTERNAL_NRPT" in stop) +check("stop_preserves_adapter_dns", "STOP_MUTATED_ADAPTER_DNSV4" in stop and "STOP_MUTATED_ADAPTER_DNSV6" in stop) +check("stop_no_state_does_not_remove_ula", "ULA is not removed without state ownership evidence." in stop) +check("recovery_state_root_guard", "STATE_ROOT_OWNERSHIP_MISMATCH" in recovery) +check("recovery_exact_orphan_ula_guard", all(x in recovery for x in ("ULA_OWNERSHIP_AMBIGUOUS","ULA_OWNERSHIP_MISMATCH","$ownedUla","Remove-NetIPAddress","RECOVERY_INCOMPLETE"))) +check("status_pid_name_ownership", "Get-PidHealth" in status and "ProcessName" in status) +check("status_external_conflict", all(x in status for x in ("CONFLICT","BLOCKED","VPN/tunnel"))) +check("status_fast_primitives", "Win32_SystemDriver" not in status and "Win32_Service" not in status) + +check("ui_native_exe_hidden_status_backend", "bundledPwshPath" in gui and '"Privileged", "runtime", "pwsh", "pwsh.exe"' in gui and "CreateNoWindow = true" in gui and "UseShellExecute = false" in gui) +check("ui_no_external_pwsh_dependency", 'ProcessStartInfo("pwsh.exe"' not in gui) +check("ui_no_direct_runas", 'Verb = "runas"' not in gui) +check("ui_uses_service_client", 'ServiceClient.Send(action)' in gui) +check("ui_waits_service_completion", "WaitForActionAsync" in gui and "action-status.json" in gui) +check("ui_blocked_state", '"BLOCKED"' in gui and "Disconnect external VPN/tunnel to Start" in gui) +check("ui_vector_hero", "' in xaml and 'TextWrapping="Wrap"' in xaml and '' in xaml) +check("target_hostlist_scope", "^gemini.google.com" in hostlist.splitlines() and all(x not in hostlist.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xnxx.com","xhamster.com")) and all(x in adult_fallback.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xvideos-cdn.com","xvcdn.com","xnxx.com","xnxx-cdn.com","xhamster.com","xhcdn.com","redtube.com","rdtcdn.com"))) +check("critical_global_services_hostlist", all(x in hostlist.splitlines() for x in ("openai.com","chatgpt.com","oaistatic.com","oaiusercontent.com","github.com","githubusercontent.com","githubassets.com","reddit.com","redditstatic.com","redditmedia.com","redd.it"))) +check("custom_site_hostlist", all(x in start for x in ("$CustomHosts=Join-Path $UserConfigDir 'custom-hosts.txt'","$RuntimeHostList=Join-Path $env:ProgramData 'DirectInternetMethod\\runtime-hosts.txt'","USER_CONFIG_INVALID","WriteAllLines($RuntimeHostList")) and all(x in gui for x in ("CustomSites_Click","NormalizeCustomSite","customHostsPath","Maximum 256 entries")) and 'Content="Custom Sites"' in xaml and 'DirectInternetMethod\\UserConfig' in iss and 'users-modify' in iss) +check("strategy_profiles", all(x in start for x in ("$StrategyFile=Join-Path $UserConfigDir 'strategy.txt'","balanced","compatibility","strong","fakedsplit","hostfakesplit","sniext+1","STRATEGY_INVALID")) and all(x in gui for x in ("Strategy_Click","strategyPath","Compatibility","Strong")) and 'Content="Strategy"' in xaml) +check("all_sites_scope_optional_default_targeted", all(x in start for x in ("$ScopeFile=Join-Path $UserConfigDir 'scope.txt'","$scope='targeted'","all-sites","SCOPE_INVALID","Add-ScopeHost")) and all(x in gui for x in ("scopePath","Apply DPI strategy to all web sites (experimental)","all-sites","targeted"))) +check("ui_gemini_adult_live_checks", all(x in xaml for x in ('Text="Gemini"','Text="Adult coverage"','x:Name="AdultSiteCheckToggle"','x:Name="GeminiText"','x:Name="AdultSiteText"')) and all(x in gui for x in ("https://www.pornhub.com/","https://www.xvideos.com/","https://www.xnxx.com/","https://xhamster.com/","FmtAdult")) and "pornhub" not in xaml.lower()) +check("ui_adult_check_default_off_persisted", all(x in gui for x in ("adultSiteLiveCheck","settings.json","AdultSiteCheckToggle_Changed")) and "bool adultCheckEnabled;" in gui) +check("adult_catalog_opt_in", all(x in gui for x in ("adultCoverageEnabled","adult-enabled.txt","adult-hosts.txt","nsfw-onlydomains.txt","EnsureAdultCatalogAsync","Adult catalog is unexpectedly small","xvideos.com","xnxx.com","xhamster.com")) and all(x in start for x in ("$AdultEnabledFile","$AdultHosts","$AdultFallback","adultHostCount","ADULT_CATALOG_INVALID")) and 'adult-fallback-hosts.txt' in iss) +check("startup_core_site_health_fields", all(x in start for x in ( + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", + "required=2", + "passed=3-$failed.Count", + "allCoreSitesOk=($failed.Count -eq 0)", + "unreachableCoreSites=@($failed.ToArray())", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", + "$httpsProbePassCount=[int]$httpsHealth.passed", + "$ev.status='PASS_ACTIVE'", +))) +check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) +check("startup_true_youtube_page_plus_204_required", all(x in start for x in ( + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", + "if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')}", + "$ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\\|' 8 2", + "$yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\\|' 8 2", + "$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh", + "youtubePage=$ytPage;youtube204=$yt", + "$httpsProbePassCount -lt 2", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", +)) and "function Get-CoreSiteHealth($yt,$oa,$gh)" not in start) +check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) +check("ui_openai_403_cannot_be_chatgpt_login_pass", + 'FmtOpenAiApiProbe(openAiProbe)' in gui + and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" })' in gui + and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401", "403" })' not in gui + and '"403" when p.Reached => "API HTTP 403 forbidden · login unverified"' in gui + and '"401" when p.Reached => "API HTTP 401 auth required · login unverified"' in gui + and '"200" when p.Reached => "API HTTP 200 reachable · login unverified"' in gui + and '"API transport unavailable · login unverified"' in gui) + +check("ui_live_probe_semantics", "FAIL · HTTP" in gui and 'https://gemini.google.com/' in gui and "adultTasks is not null" in gui and "full pages" in gui) +check("ui_partial_active_core_reachability", all(x in gui for x in ( + "bool coreReachabilityProven = false;", + "var youtubeProbe = await ytTask;", + "var openAiProbe = await oaTask;", + "var githubProbe = await ghTask;", + "var youtube204Probe = await yt204Task;", + "coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe);", + 'mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)', + 'StatusBadge.Text = "PARTIAL";', + "StatusBadge.Foreground = warn;", + "at least one core site unreachable", + 'StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED";', +))) + +check("ui_youtube_homepage_and_204_independent_health", all(x in gui for x in ( + 'var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });', + 'var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" });', + 'var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask };', + 'FullCoreReachability(ProbeResult youtubePage, ProbeResult youtube204, ProbeResult openai, ProbeResult github)', + 'youtubePage.Ok && youtube204.Ok && openai.Ok && github.Ok', + 'YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}";', + 'StatusBadge.Text = "PARTIAL";' +)) and "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;" not in gui) +check("ui_direct_update_service_handoff", 'ServiceClient.Send("update")' in gui and 'ServiceClient.Send("recovery")' in gui and '"handoff"' in gui and "300000" in gui and "DownloadVerifiedAsync" not in gui and "LaunchInstaller" not in gui) +check("ui_update_advisory_release_digest", "InstallerDigest" in updater and "digest" in updater and "SHA256SUMS.txt" in updater and "DownloadVerifiedAsync" not in updater and "Process.Start" not in updater) + +check("service_custom_controls", all(x in svc for x in ("CTRL_START = 128","CTRL_STOP = 129","CTRL_RECOVERY = 130","CTRL_UPDATE = 131")) and "case CTRL_UPDATE:" in svc and "_ = Task.Run(RunUpdateAsync)" in svc) +check("service_direct_update_security", all(x in svc for x in ("LatestReleaseApi","SHA256SUMS.txt","ApiSha256","SHA256.HashDataAsync","CryptographicException","GetLatestUpdateAsync","ParseExpectedSha256")) and "expected.Equals(package.ApiSha256" in svc and "actual.Equals(expected" in svc) +check("service_direct_update_atomic_download", '".part"' in svc and "DownloadWithSystemCurlAsync" in svc and '"-L", "--fail", "--silent", "--show-error"' in svc and '"--max-time", "210"' in svc and "File.Move(partPath, finalPath);" in svc and "OwnedUpdateInstallerPath" in svc and "Prior update still needs verification" in svc and "update-incomplete-preserved" in svc) +check("service_direct_update_silent_installer", all(x in svc for x in ('"/VERYSILENT"','"/SUPPRESSMSGBOXES"','"/NORESTART"','"/NORESTARTAPPLICATIONS"','"/CLOSEAPPLICATIONS"','"/SP-"')) and "UseShellExecute = false" in svc and 'Verb = "runas"' not in svc) +check("service_direct_update_requires_clean_off", "NetworkStatePath" in svc and "Direct Method must be OFF before update." in svc) +check("service_direct_update_post_restart_finalize", + all(x in svc for x in ("FinalizePendingAfterServiceRestartAsync", + "_ = Task.Run(FinalizePendingAfterServiceRestartAsync);", + "FinalizePendingUpdate();", + "pending-update-finalization-deferred-no-overwrite", + "update-stage-file-locked-preserving-single-pending-slot", + "pending-update.json", '"update", "done"', "targetVersion")) + and "StopEvent.IsSet" in svc) +check("service_direct_update_preserves_registered_user_install", "GetRegisteredInstallDirectory" in svc and "Registry.LocalMachine.OpenSubKey" in svc and "InstallLocation" in svc and '"/DIR=" + installDir' in svc and "DirectInternetMethod.exe" in svc and "manifest.json" in svc) +check("service_action_gate", "SemaphoreSlim ActionGate" in svc and "WaitAsync(0)" in svc) +check("service_action_status", "action-status.json" in svc and "WriteActionStatus" in svc and 'phase = "done"' not in svc) +check("service_fixed_scripts", all(x in svc for x in ('RunActionAsync("Start-Direct.ps1"','RunActionAsync("Stop-Direct.ps1"','RunActionAsync("Recovery.ps1"'))) +check("service_hidden_backend", "CreateNoWindow = true" in svc and "WindowStyle = ProcessWindowStyle.Hidden" in svc) +check("service_bundled_pwsh_only", '"runtime", "pwsh", "pwsh.exe"' in svc and 'return File.Exists(p) ? p : "pwsh.exe"' not in svc) +check("service_action_timeout_guard", "WaitForExit(120000)" in svc and "Kill(entireProcessTree: true)" in svc and "exceeded the 120 second safety timeout" in svc) +check("service_action_completion_no_inherited_pipe_deadlock", + "RedirectStandardOutput = true" not in svc + and "ReadToEndAsync" not in svc + and 'Log($"action-end:{actionName}:exit={p.ExitCode}")' in svc + and ( + ("RedirectStandardError = true" not in svc and "ReadActionErrorTailAsync" not in svc) + or ( + "RedirectStandardError = true" in svc + and "var stderrTask = ReadActionErrorTailAsync(p.StandardError);" in svc + and "while ((count = await reader.ReadAsync(buffer.AsMemory())) != 0)" in svc + and "tail.Length > 8192" in svc + and "await stderrTask.WaitAsync(TimeSpan.FromSeconds(3))" in svc + ) + )) +check("service_safe_action_errors_do_not_leak_backend_output", + "RedirectStandardError = true" not in svc + or ( + "DescribeActionFailure(p.ExitCode, stderr)" in svc + and 'standardError.Contains("DEFAULT_ROUTE_NOT_PHYSICAL"' in svc + and 'standardError.Contains("REFUSE_BROAD_TUNNEL_ROUTE"' in svc + and 'standardError.Contains("HASH_MISMATCH_"' in svc + and "WriteActionStatus(actionName, p.ExitCode == 0 ? \"done\" : \"error\", p.ExitCode, message)" in svc + and "RAW_STDERR_LEAK" in svc + )) +check("ui_wait_timeout_exceeds_service_timeout", "int timeoutMs = 130000" in gui) +check("client_minimum_access", "SERVICE_QUERY_STATUS | SERVICE_USER_DEFINED_CONTROL" in client and "SERVICE_CHANGE_CONFIG" not in client and "SERVICE_START" not in client and "SERVICE_STOP" not in client) +check("installer_service_acl_interactive_users_minimum", "(A;;LCCR;;;IU)" in iss and "sdset" in iss and "(A;;LCCR;;;AU)" not in iss) +check("installer_service_protected_root", "{commonpf}\\DirectInternetMethod\\Privileged" in iss) +check("single_registered_Windows_install_and_preserve_user_evidence", + 'AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}' in iss + and "UsePreviousAppDir=yes" in iss and "UninstallLogMode=overwrite" in iss + and "DIMExistingAppPathConflicts()" in iss + and "RegQueryStringValue(HKLM, Key, 'InstallLocation', OldDir)" in iss + and r'Type: filesandordirs; Name: "{app}\evidence"' not in iss + and r'Type: filesandordirs; Name: "{app}\bin"' not in iss + and r'Type: filesandordirs; Name: "{commonpf}\DirectInternetMethod\Privileged"' not in iss + and r'Type: dirifempty; Name: "{commonpf}\DirectInternetMethod\Privileged"' in iss) +check("owned_update_cache_never_duplicates_versioned_installers", + all(x in svc for x in ("OwnedUpdateInstallerPath", "DirectInternetMethod-update-stage.exe", + "Previous update is pending" if False else "Prior update still needs verification", + "File.Move(partPath, finalPath);", + "update-incomplete-preserved", + "update-stage-file-locked-preserving-single-pending-slot")) + and "File.Move(partPath, finalPath, true)" not in svc) + +check("legacy_provider_rollback_explicit_only_and_exact_SHA", + all(k in legacy_provider_guard for k in ( + "PUBLISHED_STABLE_151_INSTALLER_SHA_CHANGED", "PRESERVE_CUSTOM_PROVIDER_BEFORE_LEGACY_151_REINSTALL", + "PASS_LEGACY_151_PROVIDER_RESTORED_SHA", "FOREIGN_POST_STAGE_PROVIDER_EDIT_PRESERVED", + "PENDING_SLOT_EXISTS_NO_BLIND_REPLAY", "RESTORED_PROVIDER_SHA_FAILED", + "LEGACY_151_BINARY_POSTINSTALL_SHA_NOT_ACCEPTED")) + and "Remove-Item -Recurse" not in legacy_provider_guard + and 'Source: "..\\app\\SafeLegacyProviderRollback.ps1"; DestDir: "{commonpf}\\DirectInternetMethod\\Privileged\\app"; Flags: ignoreversion' in iss + and ("privileged","app/SafeLegacyProviderRollback.ps1") in + {(v["scope"],v["file"]) for v in manifest["files"]}) +check("legacy_151_rollback_preserves_custom_bin_tree", + all(x in legacy_provider_guard for x in ["binFiles","BIN_RESTORATION_SHA_FAILED","FOREIGN_BIN_EDIT_PRESERVED","BIN_SNAPSHOT_UNKNOWN_CONTENT","BIN_STAGED_FILE_CHANGED","FOREIGN_STAGE_SYMLINK"]) + and "Remove-Item -Recurse" not in legacy_provider_guard) +check("inno_preserves_custom_router_gateway_provider_on_upgrade", + 'Source: "..\\..\\router_gateway\\providers.json"; DestDir: "{app}\\app\\router_gateway"; Flags: ignoreversion onlyifdoesntexist' in iss) +check("installer_admin_only_install", "PrivilegesRequired=admin" in iss) +check("installer_service_recovery", 'failure "' in iss and "failureflag" in iss) +check("installer_preserves_pending_update_until_service_SHA_verifies", + "CompletePendingDirectUpdate" in iss and "pending-update.json" in iss + and "new service completes SHA-checked cleanup" in iss + and r"PendingPath := ExpandConstant('{commonappdata}\DirectInternetMethod\pending-update.json')" in iss + and "DeleteFile(PendingPath)" not in iss + and '"action":"update","phase":"done"' not in iss) +check("installer_ctrld_owned_demand_service", "CtrldServiceName = 'ctrld'" in iss and "InstallOrUpdateCtrldService" in iss and "start= demand" in iss and "CurrentControlSet\\Services\\" in iss) +check("installer_ctrld_collision_fail_closed", "A different Windows service named ctrld already exists" in iss and "CtrldServiceOwned()" in iss) +check("installer_ctrld_owned_uninstall", "StopCtrldServiceIfOwned" in iss and "delete \"" in iss and "CtrldServiceName" in iss) +check("installer_shortcuts_exe", 'DirectInternetMethod.exe' in iss and 'ControlPanel.cmd' not in "\n".join(x for x in iss.splitlines() if x.startswith("Name:"))) +check("installer_active_guard", "PrepareToInstall" in iss and "DirectDnsDpiHarness" in iss and "state.json" in iss) +check("installer_bundled_pwsh", '..\\vendor\\pwsh\\*' in iss and 'Privileged\\runtime\\pwsh' in iss and "PowerShell7Available" not in iss) +check("installer_uninstall_recovery_gate", "InitializeUninstall" in iss and "Recovery did not complete" in iss) +check("windows_install_registration_guard", all(x in install_registration_guard for x in ("HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","noSameAppIdPerUserDuplicate","registeredUninstallerPresent","installedReleaseMatches"))) +check("installed_release_verifier_delegates_registration_guard", "windows_install_registration_audit.ps1" in installed_release_verifier and "Where-Object DisplayName" not in installed_release_verifier and "pwsh.exe" in installed_release_verifier) +check("windows_build_shared_exclusive_lock", all(x in payload_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED")) and all(x in installer_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED"))) +check("windows_build_python_portability_failfast", all(x in payload_builder for x in ( + "param([switch]$PreflightOnly)", + "WINDOWS_BUILD_PYTHON_MISSING:", + "WINDOWS_BUILD_VENDOR_PWSH_SHA_INVALID", + "WINDOWS_BUILD_PORTABLE_PYTHON_SHA_INVALID", + "4942B86A6597E5AEE0128DAA00050ED79BC21F6E709A78EB19CBFEB0C2F39AC9", + r"& $PythonExe '.\tests\build_windows_manifest.py'", + r"& $PythonExe '.\tests\windows_contract_audit.py'", +)) and "& py " not in payload_builder and + payload_builder.index("WINDOWS_BUILD_PYTHON_MISSING:") < payload_builder.index("& dotnet publish")) +check("windows_installer_output_stability_guard", all(x in installer_builder for x in ("OUTPUT_STILL_LOCKED_AFTER_ISCC_EXIT","@($Sizes | Select-Object -Unique).Count","OUTPUT_SIZE_UNSTABLE_","outputStableSamples"))) +tracked_strong=subprocess.run(["git","ls-files","--error-unmatch","windows/bin/zapret/strong-override-hosts.txt"],cwd=R,text=True,capture_output=True).returncode==0 +check("windows_strong_override_source_tracked", tracked_strong) +check("windows_hash_pinned_text_eol_policy", all(x in gitattributes for x in ("windows/bin/zapret/hosts.txt text eol=crlf","windows/bin/zapret/adult-fallback-hosts.txt text eol=lf","windows/bin/zapret/strong-override-hosts.txt text eol=crlf"))) +check("optional_signed_diagnostic_fails_closed_but_is_not_public_gate", all(x in signature_gate for x in ( + "Get-AuthenticodeSignature", + "Assert-EligibleSignerForSmartAppControl -Signature $sig", + "if([string]$Signature.Status -ne 'Valid')", + "SELF_SIGNED_SIGNER_NOT_PUBLIC_TRUSTED", + "SMART_APP_CONTROL_RSA_SIGNER_REQUIRED", + "CODE_SIGNING_EKU_REQUIRED", + "PUBLIC_TRUST_CHAIN_UNVERIFIED", + "FAIL_CLOSED","exit 42" +))) +tag_job_source = ci_workflow.split(" windows-release-contract:",1)[1] if ci_workflow.count(" windows-release-contract:")==1 else "" +check("tag_release_invokes_real_unsigned_three_way_sha_and_hosted_rollback", + tag_job_source.count("python -B tests/unsigned_public_release_contract.py --public-tag")==1 + and "if: startsWith(github.ref, 'refs/tags/v')" in tag_job_source + and "GH_TOKEN:" in tag_job_source and "GITHUB_TOKEN:" in tag_job_source + and "actions: read" in tag_job_source + and "windows_distribution_signature_audit.ps1" not in tag_job_source + and "windows_release_draft_asset_stage.ps1" not in tag_job_source + and "continue-on-error: true" not in tag_job_source + and "if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }" in tag_job_source) + +draft_stage_source=(R/"tests"/"windows_release_draft_asset_stage.ps1").read_text(encoding="utf-8-sig") +draft_fixture_source=(R/"tests"/"windows_release_draft_asset_contract.ps1").read_text(encoding="utf-8-sig") +windows_source_section=ci_workflow.split(" windows-source:",1)[1].split(" windows-release-contract:",1)[0] +check("windows_ci_runs_offline_draft_staging_negative_regressions", + windows_source_section.count("pwsh.exe -NoProfile -NonInteractive -File tests/windows_release_draft_asset_contract.ps1")==1 + and "continue-on-error: true" not in windows_source_section) +check("windows_source_ci_runs_unsigned_release_negative_fixtures", + windows_source_section.count("python tests/unsigned_public_release_contract.py --fixtures")==1) +check("unsigned_publication_never_requires_private_signed_draft_staging", + "windows_release_draft_asset_stage.ps1" not in tag_job_source + and "windows_distribution_signature_audit.ps1" not in tag_job_source + and "unsigned_public_release_contract.py --fixtures" in tag_job_source + and "unsigned_public_release_contract.py --public-tag" in tag_job_source + and "continue-on-error: true" not in tag_job_source) +check("unsigned_verifier_checks_remote_sha_and_raw_hosted_receipts", + all(s in (R/"tests"/"unsigned_public_release_contract.py").read_text(encoding="utf-8-sig") for s in ( + "PUBLIC_THREE_WAY_SHA_DISAGREEMENT", "HOSTED_RAW_REPORT_SHA_MISMATCH", + "HOSTED_REPORT_UNRESOLVED_ERRORS", "GITHUB_RUN_NOT_SUCCESS", + "SIGNING_POLICY_MUST_NOT_BE_REQUIRED"))) + +check("draft_release_stager_fails_closed_on_identity_digest_and_rerun", + all(s in draft_stage_source for s in ( + "RELEASE_NOT_PRIVATE_DRAFT","RELEASE_TARGET_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DIGEST_MISMATCH", + "SIGNED_DRAFT_INSTALLER_ASSET_ORIGIN_MISMATCH","UNSAFE_RERUN_STAGING_PATH_OCCUPIED", + "TAG_CHECKOUT_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DOWNLOADED_SHA_MISMATCH","exit 42"))) +check("draft_release_negative_fixtures_present", + all(s in draft_fixture_source for s in ( + "Deny 'published'","Deny 'tag'","Deny 'commit'","Deny 'duplicate'", + "Deny 'digest'","Deny 'origin'","PASS 10/10"))) + +check("windows_ci_external_audits_fail_fast", ci_workflow.count("if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }") >= 7) + +files={(x["scope"],x["file"]) for x in manifest["files"]} +release_meta=json.loads(txt("RELEASE.json")) +allowed_status={ + "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), + "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), + "1.5.6":("PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), +} +current=release_meta.get("version") +check("manifest_version", current in allowed_status + and manifest.get("version")==current + and release_meta.get("status")==allowed_status[current][0] + and release_meta.get("previousAccepted",{}).get("version")==allowed_status[current][1]) + +check("manifest_direct_update", manifest.get("directUpdate",{}).get("userUacRequired") is False and "SHA256SUMS.txt" in manifest.get("directUpdate",{}).get("integrity","")) +check("manifest_native_exe", ("user","app/DirectInternetMethod.exe") in files) +check("manifest_router_gateway_data", ("user","app/router_gateway/providers.json") in files) +check("manifest_adult_fallback", ("privileged","bin/zapret/adult-fallback-hosts.txt") in files) +check("manifest_strong_override", ("privileged","bin/zapret/strong-override-hosts.txt") in files) +check("manifest_service", ("privileged","app/DirectInternetMethod.Service.exe") in files) +check("manifest_no_helper", not any(x[1].endswith("Helper.exe") for x in files)) +check("manifest_bundled_pwsh", manifest.get("bundledPowerShellVersion")=="7.6.6" and manifest.get("bundledPowerShellArchiveSha256")=="02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860" and ("privileged","runtime/pwsh/pwsh.exe") in files) +check("manifest_bundled_pwsh_tree", sum(1 for s,f in files if s=="privileged" and f.startswith("runtime/pwsh/")) >= 650) +check("manifest_no_legacy_launchers", not any(scope=="user" and (f.endswith(".cmd") or f in {"app/ControlPanel.ps1","app/Toggle.ps1"}) for scope,f in files)) + +pin_paths={ +"ctrld.exe":"bin/ctrld/ctrld.exe", +"ctrld.toml":"bin/ctrld/ctrld.toml", +"winws.exe":"bin/zapret/winws.exe", +"WinDivert.dll":"bin/zapret/WinDivert.dll", +"WinDivert64.sys":"bin/zapret/WinDivert64.sys", +"cygwin1.dll":"bin/zapret/cygwin1.dll", +"hosts.txt":"bin/zapret/hosts.txt", +"adult-fallback-hosts.txt":"bin/zapret/adult-fallback-hosts.txt", +"strong-override-hosts.txt":"bin/zapret/strong-override-hosts.txt", +} +start_pins=dict(re.findall(r"'([^']+)'='([0-9A-F]{64})'",start)) +pin_actual={k:hashlib.sha256((W/v).read_bytes()).hexdigest().upper() for k,v in pin_paths.items()} +D["selfIntegrityPins"]={"declared":{k:start_pins.get(k) for k in pin_paths},"actual":pin_actual} +check("start_self_integrity_pins",all(start_pins.get(k)==v for k,v in pin_actual.items())) + +expected={ +"ctrld":"FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD", +"winws":"A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8", +"windivertdll":"C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2", +"windivertsys":"8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2", +"pwsh":"BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285"} +for name,rel in (("ctrld","bin/ctrld/ctrld.exe"),("winws","bin/zapret/winws.exe"),("windivertdll","bin/zapret/WinDivert.dll"),("windivertsys","bin/zapret/WinDivert64.sys"),("pwsh","vendor/pwsh/pwsh.exe")): + h=hashlib.sha256((W/rel).read_bytes()).hexdigest().upper();D["hashes"][name]=h;check("runtime_"+name+"_pin",h==expected[name]) + +thirdparty_notice_hashes={ + "LICENSE.WinDivert.txt":"14A0CB5214D536E4FDAE6AA3F5696F981EEDA106CD026E9794BBA489EE79D628", + "LICENSE.Cygwin.txt":"794433752103CF4BBB4A84A1BDB8FBC150ABB1762704BB35FECC9F7F820BE984", + "LGPL3.Cygwin.txt":"DA7EABB7BAFDF7D3AE5E9F223AA5BDC1EECE45AC569DC21B3B037520B4464768", + "GPL3.Cygwin.txt":"8CEB4B9EE5ADEDDE47B31E975C1D90C73AD27B6B165A1DCD80C7C545EB65B903", +} +def checked_notice_mapping(filename): + src = r"..\bin\zapret" + "\\" + filename + dest = r"{commonpf}\DirectInternetMethod\Privileged\bin\zapret" + line = 'Source: "' + src + '"; DestDir: "' + dest + '"; Flags: ignoreversion notimestamp' + return iss.splitlines().count(line)==1 +def r549_inno_source_timestamp_contract(source): + try: + rows_section=source.split("[Files]\n",1)[1].split("[InstallDelete]",1)[0] + except IndexError: + return False + rows=[row.strip() for row in rows_section.splitlines() + if row.strip().startswith("Source: ")] + if len(rows)!=28 or len(set(rows))!=28: + return False + for row in rows: + if "Flags:" not in row: + return False + flags=row.split("Flags:",1)[1].strip().split() + if flags.count("notimestamp")!=1 or "touch" in flags or "comparetimestamp" in flags: + return False + return True + +# Negative-first source-time fixture: exactly 28 embedded records without +# stored input timestamps. No change to owned uninstall/upgrade file scope. +check("r549_inno_no_source_timestamps", + r549_inno_source_timestamp_contract(iss) + and not r549_inno_source_timestamp_contract(iss.replace(" notimestamp","",1)) + and not r549_inno_source_timestamp_contract( + iss.replace(" notimestamp"," notimestamp touch",1))) + +check("installer_four_upstream_license_notices_byte_pinned", + all( + (W/"bin"/"zapret"/filename).is_file() + and hashlib.sha256((W/"bin"/"zapret"/filename).read_bytes()).hexdigest().upper()==expected + and checked_notice_mapping(filename) + and "windows/bin/zapret/"+filename+" text eol=lf" in gitattributes + for filename,expected in thirdparty_notice_hashes.items() + )) + +print(json.dumps(D,indent=2)) +sys.exit(0 if D["status"]=="PASS" else 20) From 915407cfd4989a9bf1f4c94968dabae3bddf9475 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:24:47 +0330 Subject: [PATCH 175/188] fix(windows): report VPN route guard safely without route changes --- windows/service/Program.cs | 1294 +++++++++++++++++++----------------- 1 file changed, 667 insertions(+), 627 deletions(-) diff --git a/windows/service/Program.cs b/windows/service/Program.cs index 7571745d..34d1e272 100644 --- a/windows/service/Program.cs +++ b/windows/service/Program.cs @@ -1,627 +1,667 @@ -using System.Diagnostics; -using System.Net.Http; -using System.Reflection; -using Microsoft.Win32; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; - -namespace DirectInternetMethod.Service; - -internal static class Program -{ - const string ServiceName = "DirectInternetMethodSvc"; - const uint SERVICE_WIN32_OWN_PROCESS = 0x10; - const uint SERVICE_START_PENDING = 0x2; - const uint SERVICE_STOP_PENDING = 0x3; - const uint SERVICE_RUNNING = 0x4; - const uint SERVICE_STOPPED = 0x1; - const uint SERVICE_ACCEPT_STOP = 0x1; - const uint SERVICE_ACCEPT_SHUTDOWN = 0x4; - const uint SERVICE_CONTROL_STOP = 0x1; - const uint SERVICE_CONTROL_SHUTDOWN = 0x5; - const uint CTRL_START = 128; - const uint CTRL_STOP = 129; - const uint CTRL_RECOVERY = 130; - const uint CTRL_UPDATE = 131; - const string LatestReleaseApi = "https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest"; - - static IntPtr _statusHandle; - static readonly ManualResetEventSlim StopEvent = new(false); - static readonly SemaphoreSlim ActionGate = new(1, 1); - static ServiceMainDelegate? _serviceMain; - static HandlerExDelegate? _handler; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - struct SERVICE_TABLE_ENTRY - { - [MarshalAs(UnmanagedType.LPWStr)] public string? lpServiceName; - public ServiceMainDelegate? lpServiceProc; - } - - [StructLayout(LayoutKind.Sequential)] - struct SERVICE_STATUS - { - public uint dwServiceType; - public uint dwCurrentState; - public uint dwControlsAccepted; - public uint dwWin32ExitCode; - public uint dwServiceSpecificExitCode; - public uint dwCheckPoint; - public uint dwWaitHint; - } - - delegate void ServiceMainDelegate(int argc, IntPtr argv); - delegate uint HandlerExDelegate(uint control, uint eventType, IntPtr eventData, IntPtr context); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool StartServiceCtrlDispatcher([In] SERVICE_TABLE_ENTRY[] table); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern IntPtr RegisterServiceCtrlHandlerEx(string serviceName, HandlerExDelegate handler, IntPtr context); - - [DllImport("advapi32.dll", SetLastError = true)] - static extern bool SetServiceStatus(IntPtr serviceStatusHandle, ref SERVICE_STATUS status); - - static int Main(string[] args) - { - if (args.Length == 1 && args[0].Equals("--console-selftest", StringComparison.OrdinalIgnoreCase)) - { - return SelfTest() == "PASS" ? 0 : 20; - } - - _serviceMain = ServiceMain; - _handler = Handler; - var table = new[] - { - new SERVICE_TABLE_ENTRY { lpServiceName = ServiceName, lpServiceProc = _serviceMain }, - new SERVICE_TABLE_ENTRY { lpServiceName = null, lpServiceProc = null } - }; - return StartServiceCtrlDispatcher(table) ? 0 : Marshal.GetLastWin32Error(); - } - - static void ServiceMain(int argc, IntPtr argv) - { - _statusHandle = RegisterServiceCtrlHandlerEx(ServiceName, _handler!, IntPtr.Zero); - if (_statusHandle == IntPtr.Zero) return; - - SetState(SERVICE_START_PENDING, 0, 3000); - Directory.CreateDirectory(RuntimeDir); - Log("service-start"); - SetState(SERVICE_RUNNING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, 0); - // Post-upgrade cleanup must happen AFTER the new service has started. - // Inno Setup is not authorized to erase its own pending evidence. - _ = Task.Run(FinalizePendingAfterServiceRestartAsync); - StopEvent.Wait(); - SetState(SERVICE_STOP_PENDING, 0, 3000); - Log("service-stop"); - SetState(SERVICE_STOPPED, 0, 0); - } - - static uint Handler(uint control, uint eventType, IntPtr eventData, IntPtr context) - { - switch (control) - { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - StopEvent.Set(); - return 0; - case CTRL_START: - _ = Task.Run(() => RunActionAsync("Start-Direct.ps1", "start")); - return 0; - case CTRL_STOP: - _ = Task.Run(() => RunActionAsync("Stop-Direct.ps1", "stop")); - return 0; - case CTRL_RECOVERY: - _ = Task.Run(() => RunActionAsync("Recovery.ps1", "recovery")); - return 0; - case CTRL_UPDATE: - _ = Task.Run(RunUpdateAsync); - return 0; - default: - return 0; - } - } - - static string RuntimeDir => Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), - "DirectInternetMethod"); - - static string LogPath => Path.Combine(RuntimeDir, "service.log"); - static string ActionStatusPath => Path.Combine(RuntimeDir, "action-status.json"); - - static string PwshPath => - Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "runtime", "pwsh", "pwsh.exe")); - - - static string UpdatesDir => Path.Combine(RuntimeDir, "updates"); - static string PendingUpdatePath => Path.Combine(RuntimeDir, "pending-update.json"); - // Only one owned staging installer may exist for an active update. - static string OwnedUpdateInstallerPath => Path.Combine(UpdatesDir, "DirectInternetMethod-update-stage.exe"); - static string NetworkStatePath => Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), - "DirectDnsDpiHarness", "state.json"); - - static readonly HttpClient UpdateHttp = CreateUpdateHttp(); - - sealed record UpdatePackage( - Version Version, - string Tag, - string InstallerName, - string InstallerUrl, - string ChecksumsUrl, - string ApiSha256); - - static HttpClient CreateUpdateHttp() - { - var client = new HttpClient { Timeout = TimeSpan.FromSeconds(45) }; - client.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod.Service/1.2"); - client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json"); - return client; - } - - static Version CurrentVersion => - Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0, 0); - - static async Task GetLatestUpdateAsync(CancellationToken ct) - { - using var response = await UpdateHttp.GetAsync(LatestReleaseApi, ct); - response.EnsureSuccessStatusCode(); - using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync(ct)); - var root = doc.RootElement; - if (root.TryGetProperty("draft", out var draft) && draft.GetBoolean()) - throw new InvalidOperationException("Latest GitHub release is a draft."); - if (root.TryGetProperty("prerelease", out var pre) && pre.GetBoolean()) - throw new InvalidOperationException("Latest GitHub release is a prerelease."); - - var tag = root.GetProperty("tag_name").GetString() ?? ""; - var clean = tag.Trim().TrimStart('v', 'V'); - if (!Version.TryParse(clean, out var latest)) - throw new InvalidOperationException("Latest GitHub release has an invalid version tag."); - if (latest <= CurrentVersion) return null; - - var installers = new List<(string Name, string Url, string Digest)>(); - var sums = new List(); - foreach (var asset in root.GetProperty("assets").EnumerateArray()) - { - var name = asset.GetProperty("name").GetString() ?? ""; - var url = asset.GetProperty("browser_download_url").GetString() ?? ""; - if (name.EndsWith("_Windows_Setup.exe", StringComparison.OrdinalIgnoreCase)) - { - var digest = asset.TryGetProperty("digest", out var de) ? de.GetString() ?? "" : ""; - installers.Add((name, url, digest)); - } - else if (name.Equals("SHA256SUMS.txt", StringComparison.OrdinalIgnoreCase)) - sums.Add(url); - } - - if (installers.Count != 1 || sums.Count != 1) - throw new InvalidOperationException("Release must contain exactly one Windows installer and one SHA256SUMS.txt."); - - var apiDigest = installers[0].Digest; - if (!apiDigest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase) || apiDigest.Length != 71) - throw new InvalidOperationException("GitHub release asset is missing a valid SHA-256 digest."); - - return new UpdatePackage(latest, tag, installers[0].Name, installers[0].Url, sums[0], apiDigest[7..]); - } - - static string ParseExpectedSha256(string sumsText, string installerName) - { - var matches = new List(); - foreach (var raw in sumsText.Split('\n')) - { - var line = raw.Trim(); - if (line.Length < 64) continue; - var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length >= 2 && - parts[^1].TrimStart('*').Equals(installerName, StringComparison.OrdinalIgnoreCase)) - matches.Add(parts[0].Trim()); - } - if (matches.Count != 1 || matches[0].Length != 64 || matches[0].Any(c => !Uri.IsHexDigit(c))) - throw new InvalidOperationException("SHA256SUMS.txt must contain exactly one valid checksum for the Windows installer."); - return matches[0].ToUpperInvariant(); - } - - static async Task Sha256FileAsync(string path, CancellationToken ct) - { - await using var stream = File.OpenRead(path); - return Convert.ToHexString(await SHA256.HashDataAsync(stream, ct)); - } - - static void WriteJsonAtomic(string path, object payload) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - var temp = path + ".tmp"; - File.WriteAllText(temp, JsonSerializer.Serialize(payload), new UTF8Encoding(false)); - File.Move(temp, path, true); - } - - - static string GetRegisteredInstallDirectory() - { - const string keyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1"; - using var key = Registry.LocalMachine.OpenSubKey(keyPath, writable: false) - ?? throw new InvalidOperationException("Registered all-users installation was not found."); - var raw = key.GetValue("InstallLocation") as string; - if (string.IsNullOrWhiteSpace(raw)) - throw new InvalidOperationException("Registered installation directory is missing."); - - var full = Path.GetFullPath(raw.Trim()); - if (!Path.IsPathFullyQualified(full)) - throw new InvalidOperationException("Registered installation directory is not absolute."); - var appExe = Path.Combine(full, "app", "DirectInternetMethod.exe"); - var manifest = Path.Combine(full, "manifest.json"); - if (!File.Exists(appExe) || !File.Exists(manifest)) - throw new InvalidOperationException("Registered installation directory does not contain the expected application payload."); - return full.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - } - - static async Task DownloadWithSystemCurlAsync(string url, string destination, CancellationToken ct) - { - // Resolve the updater helper exclusively from the Windows-owned system directory. - // Never let caller PATH or a resolved folder string choose the executable. - var systemFolder = Environment.SystemDirectory; - if (string.IsNullOrWhiteSpace(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) - throw new InvalidOperationException("Trusted Windows system directory unavailable."); - var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); - if (!File.Exists(curl)) - throw new FileNotFoundException("Windows curl.exe is unavailable.", curl); - - var psi = new ProcessStartInfo(curl) - { - UseShellExecute = false, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden - }; - foreach (var arg in new[] - { - "-L", "--fail", "--silent", "--show-error", - "--connect-timeout", "15", "--max-time", "210", - "--retry", "2", "--retry-delay", "1", - "-o", destination, url - }) - psi.ArgumentList.Add(arg); - - using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start Windows curl.exe."); - using var reg = ct.Register(() => - { - try { if (!p.HasExited) p.Kill(entireProcessTree: true); } catch { } - }); - await p.WaitForExitAsync(ct); - if (p.ExitCode != 0) - throw new HttpRequestException($"curl.exe failed downloading release asset with exit code {p.ExitCode}."); - if (!File.Exists(destination) || new FileInfo(destination).Length == 0) - throw new IOException("Downloaded release asset is empty."); - } - - static async Task RunUpdateAsync() - { - const string action = "update"; - if (!await ActionGate.WaitAsync(0)) - { - Log("action-busy:update"); - WriteActionStatus(action, "busy", -1, "Another action is already running."); - return; - } - - try - { - if (File.Exists(NetworkStatePath)) - { - WriteActionStatus(action, "error", -10, "Direct Method must be OFF before update."); - return; - } - if (File.Exists(PendingUpdatePath)) - { - FinalizePendingUpdate(); - if (File.Exists(PendingUpdatePath)) - throw new IOException("Prior update still needs verification or cleanup; refusing a second installer."); - } - - Log("action-begin:update"); - WriteActionStatus(action, "running", null, "Checking official GitHub release."); - using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(4)); - var package = await GetLatestUpdateAsync(timeout.Token); - if (package is null) - { - Log("action-end:update:already-current"); - WriteActionStatus(action, "done", 0, "Already up to date."); - return; - } - - Directory.CreateDirectory(UpdatesDir); - var sumsText = await UpdateHttp.GetStringAsync(package.ChecksumsUrl, timeout.Token); - var expected = ParseExpectedSha256(sumsText, package.InstallerName); - if (!expected.Equals(package.ApiSha256, StringComparison.OrdinalIgnoreCase)) - throw new CryptographicException("GitHub asset digest does not match SHA256SUMS.txt."); - - var finalPath = OwnedUpdateInstallerPath; - var partPath = finalPath + ".part"; - if (File.Exists(finalPath) || File.Exists(partPath)) - throw new IOException("Single owned updater staging slot is occupied; no replacement permitted."); - - await DownloadWithSystemCurlAsync(package.InstallerUrl, partPath, timeout.Token); - - var actual = await Sha256FileAsync(partPath, timeout.Token); - if (!actual.Equals(expected, StringComparison.OrdinalIgnoreCase)) - { - File.Delete(partPath); - throw new CryptographicException("Downloaded installer failed SHA-256 verification."); - } - File.Move(partPath, finalPath); // Never overwrite a pending/foreign installer. - - var installDir = GetRegisteredInstallDirectory(); - - WriteJsonAtomic(PendingUpdatePath, new - { - schema = 1, - targetVersion = package.Version.ToString(3), - tag = package.Tag, - installer = finalPath, - sha256 = actual, - installDir, - preparedUtc = DateTimeOffset.UtcNow.ToString("O") - }); - - var logPath = Path.Combine(UpdatesDir, "install-" + package.Version.ToString(3) + ".log"); - var psi = new ProcessStartInfo(finalPath) - { - UseShellExecute = false, - CreateNoWindow = true, - WorkingDirectory = UpdatesDir - }; - psi.ArgumentList.Add("/VERYSILENT"); - psi.ArgumentList.Add("/SUPPRESSMSGBOXES"); - psi.ArgumentList.Add("/NORESTART"); - psi.ArgumentList.Add("/NORESTARTAPPLICATIONS"); - psi.ArgumentList.Add("/CLOSEAPPLICATIONS"); - psi.ArgumentList.Add("/SP-"); - psi.ArgumentList.Add("/DIR=" + installDir); - psi.ArgumentList.Add("/LOG=" + logPath); - - var installer = Process.Start(psi); - if (installer is null) - throw new InvalidOperationException("Unable to launch the verified installer."); - - Log($"update-handoff:{package.Tag}:pid={installer.Id}:sha256={actual}"); - WriteActionStatus(action, "handoff", 0, $"Verified {package.Tag}; installing directly without UAC."); - } - catch (Exception ex) - { - Log($"action-exception:update:{ex.GetType().Name}:{Compact(ex.Message)}"); - WriteActionStatus(action, "error", -1, ex.GetType().Name + ": " + ex.Message); - } - finally - { - ActionGate.Release(); - } - } - - static async Task FinalizePendingAfterServiceRestartAsync() - { - // The Setup executable may be locked until Inno closes. Recheck only - // the one SHA-pinned owned installer, never delete a foreign path. - for (var attempt = 0; attempt < 48 && !StopEvent.IsSet; attempt++) - { - if (!File.Exists(PendingUpdatePath)) return; - if (await ActionGate.WaitAsync(0)) - { - try - { - FinalizePendingUpdate(); - if (!File.Exists(PendingUpdatePath)) return; - } - finally { ActionGate.Release(); } - } - await Task.Delay(TimeSpan.FromSeconds(5)); - } - if (File.Exists(PendingUpdatePath)) - Log("pending-update-finalization-deferred-no-overwrite"); - } - - static void FinalizePendingUpdate() - { - try - { - if (!File.Exists(PendingUpdatePath)) return; - using var doc = JsonDocument.Parse(File.ReadAllText(PendingUpdatePath)); - var targetText = doc.RootElement.GetProperty("targetVersion").GetString() ?? ""; - if (doc.RootElement.GetProperty("schema").GetInt32() != 1 || - !Version.TryParse(targetText, out var target)) - throw new InvalidDataException("Pending update schema or version invalid; preserve record."); - var installed = CurrentVersion; - if (installed >= target) - { - // Setup can still be running from this EXE while the service restarts. - // Keep one pending record until the verified file can be removed. - var stage = doc.RootElement.GetProperty("installer").GetString() ?? ""; - var expectedSha = doc.RootElement.GetProperty("sha256").GetString() ?? ""; - // v1.5.1's official privileged updater used a versioned EXE name, - // while v1.5.2 uses one fixed owned update-stage.exe slot. Accept - // ONLY these two exact, non-symlink paths. A legacy name must - // match the pending target version AND the original release tag. - // Never accept an arbitrary path in the pending JSON, even when - // that file happens to have the same checksum. - var versionText = target.ToString(3); - var oldOfficialName = "DirectInternetMethod_" + versionText + "_Windows_Setup.exe"; - var oldOfficialPath = Path.Combine(UpdatesDir, oldOfficialName); - var tag = doc.RootElement.GetProperty("tag").GetString() ?? ""; - var canonicalStage = Path.GetFullPath(stage); - var fixedOwnedStage = canonicalStage.Equals(Path.GetFullPath(OwnedUpdateInstallerPath), - StringComparison.OrdinalIgnoreCase); - var pinnedLegacyStage = target >= new Version(1, 5, 2) - && tag.Equals("v" + versionText, StringComparison.OrdinalIgnoreCase) - && canonicalStage.Equals(Path.GetFullPath(oldOfficialPath), - StringComparison.OrdinalIgnoreCase); - if (!Path.IsPathFullyQualified(stage) || - (!fixedOwnedStage && !pinnedLegacyStage) || - expectedSha.Length != 64 || !expectedSha.All(Uri.IsHexDigit)) - throw new CryptographicException("Foreign staging path or invalid pending checksum."); - if (File.Exists(stage)) - { - if ((File.GetAttributes(stage) & FileAttributes.ReparsePoint) != 0 || - (File.GetAttributes(UpdatesDir) & FileAttributes.ReparsePoint) != 0) - throw new CryptographicException("Owned updater staging path is a reparse point."); - string observed; - using (var input = File.OpenRead(stage)) - observed = Convert.ToHexString(SHA256.HashData(input)); - if (!observed.Equals(expectedSha, StringComparison.OrdinalIgnoreCase)) - throw new CryptographicException("Owned staged installer changed; preserving for review."); - try { File.Delete(stage); } - catch (IOException) - { - WriteActionStatus("update", "running", null, "Installed; waiting for owned installer file to unlock."); - Log("update-stage-file-locked-preserving-single-pending-slot"); - return; - } - } - File.Delete(PendingUpdatePath); - WriteActionStatus("update", "done", 0, $"Updated to v{installed.ToString(3)}."); - Log($"update-complete:{installed.ToString(3)}:no-cache-duplicate"); - } - else - { - WriteActionStatus("update", "error", -3, $"Update target v{target.ToString(3)} was not installed; evidence preserved."); - Log($"update-incomplete-preserved:target={target.ToString(3)}:current={installed.ToString(3)}"); - } - } - catch (Exception ex) - { - Log($"update-finalize-failed:{ex.GetType().Name}:{Compact(ex.Message)}"); - } - } - - static async Task RunActionAsync(string scriptName, string actionName) - { - if (!await ActionGate.WaitAsync(0)) - { - Log($"action-busy:{actionName}"); - WriteActionStatus(actionName, "busy", -1, "Another action is already running."); - return; - } - - try - { - var script = Path.Combine(AppContext.BaseDirectory, scriptName); - if (!File.Exists(script)) - { - Log($"action-missing:{actionName}:{script}"); - WriteActionStatus(actionName, "error", -1, "Privileged lifecycle script is missing."); - return; - } - if (!File.Exists(PwshPath)) - { - Log($"runtime-missing:{actionName}:{PwshPath}"); - WriteActionStatus(actionName, "error", -1, "Bundled PowerShell runtime is missing."); - return; - } - - var psi = new ProcessStartInfo( - PwshPath, - $"-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"{script}\"") - { - UseShellExecute = false, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - WorkingDirectory = AppContext.BaseDirectory - }; - - Log($"action-begin:{actionName}"); - WriteActionStatus(actionName, "running", null, ""); - using var p = Process.Start(psi); - if (p is null) - { - Log($"action-start-failed:{actionName}"); - WriteActionStatus(actionName, "error", -1, "Unable to start PowerShell backend."); - return; - } - - if (!p.WaitForExit(120000)) - { - try { p.Kill(entireProcessTree: true); } catch { } - try { p.WaitForExit(5000); } catch { } - Log($"action-timeout:{actionName}"); - WriteActionStatus(actionName, "error", -2, "Privileged action exceeded the 120 second safety timeout and was terminated."); - return; - } - Log($"action-end:{actionName}:exit={p.ExitCode}"); - var message = p.ExitCode == 0 - ? "Action completed." - : $"Privileged action failed with exit code {p.ExitCode}. See lifecycle evidence/service log."; - WriteActionStatus(actionName, p.ExitCode == 0 ? "done" : "error", p.ExitCode, message); - } - catch (Exception ex) - { - Log($"action-exception:{actionName}:{ex.GetType().Name}:{Compact(ex.Message)}"); - WriteActionStatus(actionName, "error", -1, ex.GetType().Name + ": " + ex.Message); - } - finally - { - ActionGate.Release(); - } - } - - static void WriteActionStatus(string action, string phase, int? exitCode, string message) - { - try - { - Directory.CreateDirectory(RuntimeDir); - var payload = System.Text.Json.JsonSerializer.Serialize(new - { - schema = 1, - action, - phase, - exitCode, - message = Compact(message), - updatedUtc = DateTimeOffset.UtcNow.ToString("O") - }); - var temp = ActionStatusPath + ".tmp"; - File.WriteAllText(temp, payload, new UTF8Encoding(false)); - File.Move(temp, ActionStatusPath, true); - } - catch (Exception ex) - { - Log($"action-status-write-failed:{action}:{ex.GetType().Name}:{Compact(ex.Message)}"); - } - } - - static string Compact(string value) - { - if (string.IsNullOrWhiteSpace(value)) return "-"; - var s = value.Replace("\r", " ").Replace("\n", " "); - return s.Length <= 600 ? s : s[..600]; - } - - static void Log(string message) - { - try - { - Directory.CreateDirectory(RuntimeDir); - File.AppendAllText(LogPath, $"{DateTimeOffset.Now:O} {message}{Environment.NewLine}", Encoding.UTF8); - } - catch { } - } - - static void SetState(uint state, uint accepted, uint waitHint) - { - var s = new SERVICE_STATUS - { - dwServiceType = SERVICE_WIN32_OWN_PROCESS, - dwCurrentState = state, - dwControlsAccepted = accepted, - dwWin32ExitCode = 0, - dwServiceSpecificExitCode = 0, - dwCheckPoint = 0, - dwWaitHint = waitHint - }; - SetServiceStatus(_statusHandle, ref s); - } - - static string SelfTest() - { - var required = new[] { "Start-Direct.ps1", "Stop-Direct.ps1", "Recovery.ps1" }; - var missing = required.Where(x => !File.Exists(Path.Combine(AppContext.BaseDirectory, x))).ToArray(); - return missing.Length == 0 ? "PASS" : "MISSING:" + string.Join(",", missing); - } -} +using System.Diagnostics; +using System.Net.Http; +using System.Reflection; +using Microsoft.Win32; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace DirectInternetMethod.Service; + +internal static class Program +{ + const string ServiceName = "DirectInternetMethodSvc"; + const uint SERVICE_WIN32_OWN_PROCESS = 0x10; + const uint SERVICE_START_PENDING = 0x2; + const uint SERVICE_STOP_PENDING = 0x3; + const uint SERVICE_RUNNING = 0x4; + const uint SERVICE_STOPPED = 0x1; + const uint SERVICE_ACCEPT_STOP = 0x1; + const uint SERVICE_ACCEPT_SHUTDOWN = 0x4; + const uint SERVICE_CONTROL_STOP = 0x1; + const uint SERVICE_CONTROL_SHUTDOWN = 0x5; + const uint CTRL_START = 128; + const uint CTRL_STOP = 129; + const uint CTRL_RECOVERY = 130; + const uint CTRL_UPDATE = 131; + const string LatestReleaseApi = "https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest"; + + static IntPtr _statusHandle; + static readonly ManualResetEventSlim StopEvent = new(false); + static readonly SemaphoreSlim ActionGate = new(1, 1); + static ServiceMainDelegate? _serviceMain; + static HandlerExDelegate? _handler; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct SERVICE_TABLE_ENTRY + { + [MarshalAs(UnmanagedType.LPWStr)] public string? lpServiceName; + public ServiceMainDelegate? lpServiceProc; + } + + [StructLayout(LayoutKind.Sequential)] + struct SERVICE_STATUS + { + public uint dwServiceType; + public uint dwCurrentState; + public uint dwControlsAccepted; + public uint dwWin32ExitCode; + public uint dwServiceSpecificExitCode; + public uint dwCheckPoint; + public uint dwWaitHint; + } + + delegate void ServiceMainDelegate(int argc, IntPtr argv); + delegate uint HandlerExDelegate(uint control, uint eventType, IntPtr eventData, IntPtr context); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool StartServiceCtrlDispatcher([In] SERVICE_TABLE_ENTRY[] table); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern IntPtr RegisterServiceCtrlHandlerEx(string serviceName, HandlerExDelegate handler, IntPtr context); + + [DllImport("advapi32.dll", SetLastError = true)] + static extern bool SetServiceStatus(IntPtr serviceStatusHandle, ref SERVICE_STATUS status); + + static int Main(string[] args) + { + if (args.Length == 1 && args[0].Equals("--console-selftest", StringComparison.OrdinalIgnoreCase)) + { + return SelfTest() == "PASS" ? 0 : 20; + } + + _serviceMain = ServiceMain; + _handler = Handler; + var table = new[] + { + new SERVICE_TABLE_ENTRY { lpServiceName = ServiceName, lpServiceProc = _serviceMain }, + new SERVICE_TABLE_ENTRY { lpServiceName = null, lpServiceProc = null } + }; + return StartServiceCtrlDispatcher(table) ? 0 : Marshal.GetLastWin32Error(); + } + + static void ServiceMain(int argc, IntPtr argv) + { + _statusHandle = RegisterServiceCtrlHandlerEx(ServiceName, _handler!, IntPtr.Zero); + if (_statusHandle == IntPtr.Zero) return; + + SetState(SERVICE_START_PENDING, 0, 3000); + Directory.CreateDirectory(RuntimeDir); + Log("service-start"); + SetState(SERVICE_RUNNING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, 0); + // Post-upgrade cleanup must happen AFTER the new service has started. + // Inno Setup is not authorized to erase its own pending evidence. + _ = Task.Run(FinalizePendingAfterServiceRestartAsync); + StopEvent.Wait(); + SetState(SERVICE_STOP_PENDING, 0, 3000); + Log("service-stop"); + SetState(SERVICE_STOPPED, 0, 0); + } + + static uint Handler(uint control, uint eventType, IntPtr eventData, IntPtr context) + { + switch (control) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + StopEvent.Set(); + return 0; + case CTRL_START: + _ = Task.Run(() => RunActionAsync("Start-Direct.ps1", "start")); + return 0; + case CTRL_STOP: + _ = Task.Run(() => RunActionAsync("Stop-Direct.ps1", "stop")); + return 0; + case CTRL_RECOVERY: + _ = Task.Run(() => RunActionAsync("Recovery.ps1", "recovery")); + return 0; + case CTRL_UPDATE: + _ = Task.Run(RunUpdateAsync); + return 0; + default: + return 0; + } + } + + static string RuntimeDir => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "DirectInternetMethod"); + + static string LogPath => Path.Combine(RuntimeDir, "service.log"); + static string ActionStatusPath => Path.Combine(RuntimeDir, "action-status.json"); + + static string PwshPath => + Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "runtime", "pwsh", "pwsh.exe")); + + + static string UpdatesDir => Path.Combine(RuntimeDir, "updates"); + static string PendingUpdatePath => Path.Combine(RuntimeDir, "pending-update.json"); + // Only one owned staging installer may exist for an active update. + static string OwnedUpdateInstallerPath => Path.Combine(UpdatesDir, "DirectInternetMethod-update-stage.exe"); + static string NetworkStatePath => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "DirectDnsDpiHarness", "state.json"); + + static readonly HttpClient UpdateHttp = CreateUpdateHttp(); + + sealed record UpdatePackage( + Version Version, + string Tag, + string InstallerName, + string InstallerUrl, + string ChecksumsUrl, + string ApiSha256); + + static HttpClient CreateUpdateHttp() + { + var client = new HttpClient { Timeout = TimeSpan.FromSeconds(45) }; + client.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod.Service/1.2"); + client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json"); + return client; + } + + static Version CurrentVersion => + Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0, 0); + + static async Task GetLatestUpdateAsync(CancellationToken ct) + { + using var response = await UpdateHttp.GetAsync(LatestReleaseApi, ct); + response.EnsureSuccessStatusCode(); + using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync(ct)); + var root = doc.RootElement; + if (root.TryGetProperty("draft", out var draft) && draft.GetBoolean()) + throw new InvalidOperationException("Latest GitHub release is a draft."); + if (root.TryGetProperty("prerelease", out var pre) && pre.GetBoolean()) + throw new InvalidOperationException("Latest GitHub release is a prerelease."); + + var tag = root.GetProperty("tag_name").GetString() ?? ""; + var clean = tag.Trim().TrimStart('v', 'V'); + if (!Version.TryParse(clean, out var latest)) + throw new InvalidOperationException("Latest GitHub release has an invalid version tag."); + if (latest <= CurrentVersion) return null; + + var installers = new List<(string Name, string Url, string Digest)>(); + var sums = new List(); + foreach (var asset in root.GetProperty("assets").EnumerateArray()) + { + var name = asset.GetProperty("name").GetString() ?? ""; + var url = asset.GetProperty("browser_download_url").GetString() ?? ""; + if (name.EndsWith("_Windows_Setup.exe", StringComparison.OrdinalIgnoreCase)) + { + var digest = asset.TryGetProperty("digest", out var de) ? de.GetString() ?? "" : ""; + installers.Add((name, url, digest)); + } + else if (name.Equals("SHA256SUMS.txt", StringComparison.OrdinalIgnoreCase)) + sums.Add(url); + } + + if (installers.Count != 1 || sums.Count != 1) + throw new InvalidOperationException("Release must contain exactly one Windows installer and one SHA256SUMS.txt."); + + var apiDigest = installers[0].Digest; + if (!apiDigest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase) || apiDigest.Length != 71) + throw new InvalidOperationException("GitHub release asset is missing a valid SHA-256 digest."); + + return new UpdatePackage(latest, tag, installers[0].Name, installers[0].Url, sums[0], apiDigest[7..]); + } + + static string ParseExpectedSha256(string sumsText, string installerName) + { + var matches = new List(); + foreach (var raw in sumsText.Split('\n')) + { + var line = raw.Trim(); + if (line.Length < 64) continue; + var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length >= 2 && + parts[^1].TrimStart('*').Equals(installerName, StringComparison.OrdinalIgnoreCase)) + matches.Add(parts[0].Trim()); + } + if (matches.Count != 1 || matches[0].Length != 64 || matches[0].Any(c => !Uri.IsHexDigit(c))) + throw new InvalidOperationException("SHA256SUMS.txt must contain exactly one valid checksum for the Windows installer."); + return matches[0].ToUpperInvariant(); + } + + static async Task Sha256FileAsync(string path, CancellationToken ct) + { + await using var stream = File.OpenRead(path); + return Convert.ToHexString(await SHA256.HashDataAsync(stream, ct)); + } + + static void WriteJsonAtomic(string path, object payload) + { + Directory.CreateDirectory(Path.GetDirectoryName(path)!); + var temp = path + ".tmp"; + File.WriteAllText(temp, JsonSerializer.Serialize(payload), new UTF8Encoding(false)); + File.Move(temp, path, true); + } + + + static string GetRegisteredInstallDirectory() + { + const string keyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1"; + using var key = Registry.LocalMachine.OpenSubKey(keyPath, writable: false) + ?? throw new InvalidOperationException("Registered all-users installation was not found."); + var raw = key.GetValue("InstallLocation") as string; + if (string.IsNullOrWhiteSpace(raw)) + throw new InvalidOperationException("Registered installation directory is missing."); + + var full = Path.GetFullPath(raw.Trim()); + if (!Path.IsPathFullyQualified(full)) + throw new InvalidOperationException("Registered installation directory is not absolute."); + var appExe = Path.Combine(full, "app", "DirectInternetMethod.exe"); + var manifest = Path.Combine(full, "manifest.json"); + if (!File.Exists(appExe) || !File.Exists(manifest)) + throw new InvalidOperationException("Registered installation directory does not contain the expected application payload."); + return full.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + + static async Task DownloadWithSystemCurlAsync(string url, string destination, CancellationToken ct) + { + // Resolve the updater helper exclusively from the Windows-owned system directory. + // Never let caller PATH or a resolved folder string choose the executable. + var systemFolder = Environment.SystemDirectory; + if (string.IsNullOrWhiteSpace(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) + throw new InvalidOperationException("Trusted Windows system directory unavailable."); + var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); + if (!File.Exists(curl)) + throw new FileNotFoundException("Windows curl.exe is unavailable.", curl); + + var psi = new ProcessStartInfo(curl) + { + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + foreach (var arg in new[] + { + "-L", "--fail", "--silent", "--show-error", + "--connect-timeout", "15", "--max-time", "210", + "--retry", "2", "--retry-delay", "1", + "-o", destination, url + }) + psi.ArgumentList.Add(arg); + + using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start Windows curl.exe."); + using var reg = ct.Register(() => + { + try { if (!p.HasExited) p.Kill(entireProcessTree: true); } catch { } + }); + await p.WaitForExitAsync(ct); + if (p.ExitCode != 0) + throw new HttpRequestException($"curl.exe failed downloading release asset with exit code {p.ExitCode}."); + if (!File.Exists(destination) || new FileInfo(destination).Length == 0) + throw new IOException("Downloaded release asset is empty."); + } + + static async Task RunUpdateAsync() + { + const string action = "update"; + if (!await ActionGate.WaitAsync(0)) + { + Log("action-busy:update"); + WriteActionStatus(action, "busy", -1, "Another action is already running."); + return; + } + + try + { + if (File.Exists(NetworkStatePath)) + { + WriteActionStatus(action, "error", -10, "Direct Method must be OFF before update."); + return; + } + if (File.Exists(PendingUpdatePath)) + { + FinalizePendingUpdate(); + if (File.Exists(PendingUpdatePath)) + throw new IOException("Prior update still needs verification or cleanup; refusing a second installer."); + } + + Log("action-begin:update"); + WriteActionStatus(action, "running", null, "Checking official GitHub release."); + using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(4)); + var package = await GetLatestUpdateAsync(timeout.Token); + if (package is null) + { + Log("action-end:update:already-current"); + WriteActionStatus(action, "done", 0, "Already up to date."); + return; + } + + Directory.CreateDirectory(UpdatesDir); + var sumsText = await UpdateHttp.GetStringAsync(package.ChecksumsUrl, timeout.Token); + var expected = ParseExpectedSha256(sumsText, package.InstallerName); + if (!expected.Equals(package.ApiSha256, StringComparison.OrdinalIgnoreCase)) + throw new CryptographicException("GitHub asset digest does not match SHA256SUMS.txt."); + + var finalPath = OwnedUpdateInstallerPath; + var partPath = finalPath + ".part"; + if (File.Exists(finalPath) || File.Exists(partPath)) + throw new IOException("Single owned updater staging slot is occupied; no replacement permitted."); + + await DownloadWithSystemCurlAsync(package.InstallerUrl, partPath, timeout.Token); + + var actual = await Sha256FileAsync(partPath, timeout.Token); + if (!actual.Equals(expected, StringComparison.OrdinalIgnoreCase)) + { + File.Delete(partPath); + throw new CryptographicException("Downloaded installer failed SHA-256 verification."); + } + File.Move(partPath, finalPath); // Never overwrite a pending/foreign installer. + + var installDir = GetRegisteredInstallDirectory(); + + WriteJsonAtomic(PendingUpdatePath, new + { + schema = 1, + targetVersion = package.Version.ToString(3), + tag = package.Tag, + installer = finalPath, + sha256 = actual, + installDir, + preparedUtc = DateTimeOffset.UtcNow.ToString("O") + }); + + var logPath = Path.Combine(UpdatesDir, "install-" + package.Version.ToString(3) + ".log"); + var psi = new ProcessStartInfo(finalPath) + { + UseShellExecute = false, + CreateNoWindow = true, + WorkingDirectory = UpdatesDir + }; + psi.ArgumentList.Add("/VERYSILENT"); + psi.ArgumentList.Add("/SUPPRESSMSGBOXES"); + psi.ArgumentList.Add("/NORESTART"); + psi.ArgumentList.Add("/NORESTARTAPPLICATIONS"); + psi.ArgumentList.Add("/CLOSEAPPLICATIONS"); + psi.ArgumentList.Add("/SP-"); + psi.ArgumentList.Add("/DIR=" + installDir); + psi.ArgumentList.Add("/LOG=" + logPath); + + var installer = Process.Start(psi); + if (installer is null) + throw new InvalidOperationException("Unable to launch the verified installer."); + + Log($"update-handoff:{package.Tag}:pid={installer.Id}:sha256={actual}"); + WriteActionStatus(action, "handoff", 0, $"Verified {package.Tag}; installing directly without UAC."); + } + catch (Exception ex) + { + Log($"action-exception:update:{ex.GetType().Name}:{Compact(ex.Message)}"); + WriteActionStatus(action, "error", -1, ex.GetType().Name + ": " + ex.Message); + } + finally + { + ActionGate.Release(); + } + } + + static async Task FinalizePendingAfterServiceRestartAsync() + { + // The Setup executable may be locked until Inno closes. Recheck only + // the one SHA-pinned owned installer, never delete a foreign path. + for (var attempt = 0; attempt < 48 && !StopEvent.IsSet; attempt++) + { + if (!File.Exists(PendingUpdatePath)) return; + if (await ActionGate.WaitAsync(0)) + { + try + { + FinalizePendingUpdate(); + if (!File.Exists(PendingUpdatePath)) return; + } + finally { ActionGate.Release(); } + } + await Task.Delay(TimeSpan.FromSeconds(5)); + } + if (File.Exists(PendingUpdatePath)) + Log("pending-update-finalization-deferred-no-overwrite"); + } + + static void FinalizePendingUpdate() + { + try + { + if (!File.Exists(PendingUpdatePath)) return; + using var doc = JsonDocument.Parse(File.ReadAllText(PendingUpdatePath)); + var targetText = doc.RootElement.GetProperty("targetVersion").GetString() ?? ""; + if (doc.RootElement.GetProperty("schema").GetInt32() != 1 || + !Version.TryParse(targetText, out var target)) + throw new InvalidDataException("Pending update schema or version invalid; preserve record."); + var installed = CurrentVersion; + if (installed >= target) + { + // Setup can still be running from this EXE while the service restarts. + // Keep one pending record until the verified file can be removed. + var stage = doc.RootElement.GetProperty("installer").GetString() ?? ""; + var expectedSha = doc.RootElement.GetProperty("sha256").GetString() ?? ""; + // v1.5.1's official privileged updater used a versioned EXE name, + // while v1.5.2 uses one fixed owned update-stage.exe slot. Accept + // ONLY these two exact, non-symlink paths. A legacy name must + // match the pending target version AND the original release tag. + // Never accept an arbitrary path in the pending JSON, even when + // that file happens to have the same checksum. + var versionText = target.ToString(3); + var oldOfficialName = "DirectInternetMethod_" + versionText + "_Windows_Setup.exe"; + var oldOfficialPath = Path.Combine(UpdatesDir, oldOfficialName); + var tag = doc.RootElement.GetProperty("tag").GetString() ?? ""; + var canonicalStage = Path.GetFullPath(stage); + var fixedOwnedStage = canonicalStage.Equals(Path.GetFullPath(OwnedUpdateInstallerPath), + StringComparison.OrdinalIgnoreCase); + var pinnedLegacyStage = target >= new Version(1, 5, 2) + && tag.Equals("v" + versionText, StringComparison.OrdinalIgnoreCase) + && canonicalStage.Equals(Path.GetFullPath(oldOfficialPath), + StringComparison.OrdinalIgnoreCase); + if (!Path.IsPathFullyQualified(stage) || + (!fixedOwnedStage && !pinnedLegacyStage) || + expectedSha.Length != 64 || !expectedSha.All(Uri.IsHexDigit)) + throw new CryptographicException("Foreign staging path or invalid pending checksum."); + if (File.Exists(stage)) + { + if ((File.GetAttributes(stage) & FileAttributes.ReparsePoint) != 0 || + (File.GetAttributes(UpdatesDir) & FileAttributes.ReparsePoint) != 0) + throw new CryptographicException("Owned updater staging path is a reparse point."); + string observed; + using (var input = File.OpenRead(stage)) + observed = Convert.ToHexString(SHA256.HashData(input)); + if (!observed.Equals(expectedSha, StringComparison.OrdinalIgnoreCase)) + throw new CryptographicException("Owned staged installer changed; preserving for review."); + try { File.Delete(stage); } + catch (IOException) + { + WriteActionStatus("update", "running", null, "Installed; waiting for owned installer file to unlock."); + Log("update-stage-file-locked-preserving-single-pending-slot"); + return; + } + } + File.Delete(PendingUpdatePath); + WriteActionStatus("update", "done", 0, $"Updated to v{installed.ToString(3)}."); + Log($"update-complete:{installed.ToString(3)}:no-cache-duplicate"); + } + else + { + WriteActionStatus("update", "error", -3, $"Update target v{target.ToString(3)} was not installed; evidence preserved."); + Log($"update-incomplete-preserved:target={target.ToString(3)}:current={installed.ToString(3)}"); + } + } + catch (Exception ex) + { + Log($"update-finalize-failed:{ex.GetType().Name}:{Compact(ex.Message)}"); + } + } + + static async Task RunActionAsync(string scriptName, string actionName) + { + if (!await ActionGate.WaitAsync(0)) + { + Log($"action-busy:{actionName}"); + WriteActionStatus(actionName, "busy", -1, "Another action is already running."); + return; + } + + try + { + var script = Path.Combine(AppContext.BaseDirectory, scriptName); + if (!File.Exists(script)) + { + Log($"action-missing:{actionName}:{script}"); + WriteActionStatus(actionName, "error", -1, "Privileged lifecycle script is missing."); + return; + } + if (!File.Exists(PwshPath)) + { + Log($"runtime-missing:{actionName}:{PwshPath}"); + WriteActionStatus(actionName, "error", -1, "Bundled PowerShell runtime is missing."); + return; + } + + var psi = new ProcessStartInfo( + PwshPath, + $"-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"{script}\"") + { + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = AppContext.BaseDirectory, + // Fail-closed service errors are read, but raw output is never exposed. + RedirectStandardError = true + }; + + Log($"action-begin:{actionName}"); + WriteActionStatus(actionName, "running", null, ""); + using var p = Process.Start(psi); + if (p is null) + { + Log($"action-start-failed:{actionName}"); + WriteActionStatus(actionName, "error", -1, "Unable to start PowerShell backend."); + return; + } + + // Drain stderr concurrently; retain at most 8 KiB and never persist it. + var stderrTask = ReadActionErrorTailAsync(p.StandardError); + if (!p.WaitForExit(120000)) + { + try { p.Kill(entireProcessTree: true); } catch { } + try { p.WaitForExit(5000); } catch { } + Log($"action-timeout:{actionName}"); + WriteActionStatus(actionName, "error", -2, "Privileged action exceeded the 120 second safety timeout and was terminated."); + return; + } + var stderr = await stderrTask.WaitAsync(TimeSpan.FromSeconds(3)); + Log($"action-end:{actionName}:exit={p.ExitCode}"); + var message = p.ExitCode == 0 + ? "Action completed." + : DescribeActionFailure(p.ExitCode, stderr); + WriteActionStatus(actionName, p.ExitCode == 0 ? "done" : "error", p.ExitCode, message); + } + catch (Exception ex) + { + Log($"action-exception:{actionName}:{ex.GetType().Name}:{Compact(ex.Message)}"); + WriteActionStatus(actionName, "error", -1, ex.GetType().Name + ": " + ex.Message); + } + finally + { + ActionGate.Release(); + } + } + + static async Task ReadActionErrorTailAsync(StreamReader reader) + { + var tail = new StringBuilder(); + var buffer = new char[2048]; + int count; + while ((count = await reader.ReadAsync(buffer.AsMemory())) != 0) + { + tail.Append(buffer, 0, count); + if (tail.Length > 8192) + tail.Remove(0, tail.Length - 8192); + } + return tail.ToString(); + } + + static string DescribeActionFailure(int exitCode, string standardError) + { + // Never disclose arbitrary stderr: it can include paths, hosts or secrets. + if (standardError.Contains("DEFAULT_ROUTE_NOT_PHYSICAL", StringComparison.Ordinal)) + return "A VPN or virtual adapter is the preferred default route. Direct Internet Method kept the routes unchanged. Select a physical default route before starting."; + if (standardError.Contains("REFUSE_BROAD_TUNNEL_ROUTE", StringComparison.Ordinal)) + return "A broad VPN route is active. Direct Internet Method refused to change the current network configuration."; + if (standardError.Contains("HASH_MISMATCH_", StringComparison.Ordinal)) + return "Privileged runtime integrity check failed. Repair the verified installation before starting."; + return $"Privileged action failed with exit code {exitCode}. See lifecycle evidence/service log."; + } + + static void WriteActionStatus(string action, string phase, int? exitCode, string message) + { + try + { + Directory.CreateDirectory(RuntimeDir); + var payload = System.Text.Json.JsonSerializer.Serialize(new + { + schema = 1, + action, + phase, + exitCode, + message = Compact(message), + updatedUtc = DateTimeOffset.UtcNow.ToString("O") + }); + var temp = ActionStatusPath + ".tmp"; + File.WriteAllText(temp, payload, new UTF8Encoding(false)); + File.Move(temp, ActionStatusPath, true); + } + catch (Exception ex) + { + Log($"action-status-write-failed:{action}:{ex.GetType().Name}:{Compact(ex.Message)}"); + } + } + + static string Compact(string value) + { + if (string.IsNullOrWhiteSpace(value)) return "-"; + var s = value.Replace("\r", " ").Replace("\n", " "); + return s.Length <= 600 ? s : s[..600]; + } + + static void Log(string message) + { + try + { + Directory.CreateDirectory(RuntimeDir); + File.AppendAllText(LogPath, $"{DateTimeOffset.Now:O} {message}{Environment.NewLine}", Encoding.UTF8); + } + catch { } + } + + static void SetState(uint state, uint accepted, uint waitHint) + { + var s = new SERVICE_STATUS + { + dwServiceType = SERVICE_WIN32_OWN_PROCESS, + dwCurrentState = state, + dwControlsAccepted = accepted, + dwWin32ExitCode = 0, + dwServiceSpecificExitCode = 0, + dwCheckPoint = 0, + dwWaitHint = waitHint + }; + SetServiceStatus(_statusHandle, ref s); + } + + static string SelfTest() + { + var required = new[] { "Start-Direct.ps1", "Stop-Direct.ps1", "Recovery.ps1" }; + var missing = required.Where(x => !File.Exists(Path.Combine(AppContext.BaseDirectory, x))).ToArray(); + if (missing.Length != 0) return "MISSING:" + string.Join(",", missing); + if (!DescribeActionFailure(1, "START_FAIL_DEFAULT_ROUTE_NOT_PHYSICAL").Contains("VPN or virtual adapter", StringComparison.Ordinal)) + return "MISSING_ROUTE_GUARD_MESSAGE"; + if (!DescribeActionFailure(1, "REFUSE_BROAD_TUNNEL_ROUTE").Contains("broad VPN route", StringComparison.Ordinal)) + return "MISSING_BROAD_ROUTE_GUARD_MESSAGE"; + if (!DescribeActionFailure(1, "HASH_MISMATCH_ctrld.exe").Contains("integrity check failed", StringComparison.Ordinal)) + return "MISSING_RUNTIME_INTEGRITY_MESSAGE"; + if (DescribeActionFailure(1, "SECRET_SAMPLE private path").Contains("SECRET_SAMPLE", StringComparison.Ordinal)) + return "RAW_STDERR_LEAK"; + return "PASS"; + } +} From 42894464a29458143d1dac8fcd2abc94654e6c4d Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:26:20 +0330 Subject: [PATCH 176/188] chore(windows): preserve LF formatting in audit contract --- tests/windows_contract_audit.py | 800 ++++++++++++++++---------------- 1 file changed, 400 insertions(+), 400 deletions(-) diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 02a0e38b..4a1659b6 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -1,400 +1,400 @@ -#!/usr/bin/env python3 -from pathlib import Path -import hashlib,json,re,sys,subprocess -R=Path(__file__).resolve().parents[1] -W=R/"windows" -def txt(rel): return (W/rel).read_text(encoding="utf-8-sig") -start,stop,recovery,status,iss=map(txt,["app/Start-Direct.ps1","app/Stop-Direct.ps1","app/Recovery.ps1","app/Status.ps1","installer/DirectInternetMethod.iss"]) -legacy_provider_guard=txt("app/SafeLegacyProviderRollback.ps1") -gui=txt("gui/MainWindow.xaml.cs"); xaml=txt("gui/MainWindow.xaml") -svc=txt("service/Program.cs"); client=txt("gui/ServiceClient.cs"); updater=txt("gui/UpdateClient.cs") -manifest=json.loads(txt("manifest.json")); ctrldcfg=txt("bin/ctrld/ctrld.toml"); hostlist=txt("bin/zapret/hosts.txt"); adult_fallback=txt("bin/zapret/adult-fallback-hosts.txt"); strong_override=txt("bin/zapret/strong-override-hosts.txt") -payload_builder=(R/"tests"/"build_windows_152_payload.ps1").read_text(encoding="utf-8-sig") -installer_builder=(R/"tests"/"build_windows_152_installer.ps1").read_text(encoding="utf-8-sig") -ci_workflow=(R/".github"/"workflows"/"ci.yml").read_text(encoding="utf-8-sig") -signature_gate=(R/"tests"/"windows_distribution_signature_audit.ps1").read_text(encoding="utf-8-sig") -gitattributes=(R/".gitattributes").read_text(encoding="utf-8-sig") -install_registration_guard=(R/"tests"/"windows_install_registration_audit.ps1").read_text(encoding="utf-8-sig") -native_r535_uninstall=(R/"tests"/"r535_windows_native_rollback.ps1").read_text(encoding="utf-8-sig") -installed_release_verifier=(R/"tests"/"windows_installed_release_audit.ps1").read_text(encoding="utf-8-sig") -D={"schema":2,"status":"PASS","checks":{},"hashes":{}} -def check(name,cond): - D["checks"][name]=bool(cond) - if not cond:D["status"]="FAIL" -def idx(s,x): return s.find(x) - -check("r535_registered_uninstaller_suffix_agnostic", all(x in native_r535_uninstall for x in ( - "function ExactRegisteredUninstaller([object]$d)", - "unins[0-9]{3}[.](exe|dat|msg)", - "'ONE_UNINSTALLER'", "'MATCHED_UNINSTALL_LOG'", - "'EXACT_UNINSTALL_PATH_OWNED'", - "[void](ExactRegisteredUninstaller $d)", - "$uninstaller=ExactRegisteredUninstaller $d", -)) and "$uninstaller=Join-Path $App 'unins000.exe'" not in native_r535_uninstall) - -check("start_refuses_broad_tunnel_before_mutation", idx(start,"REFUSE_BROAD_TUNNEL_ROUTE")>=0 and idx(start,"REFUSE_BROAD_TUNNEL_ROUTE") < idx(start,"New-NetIPAddress")) -check("loopback_index_dynamic", all("DestinationPrefix '::1/128'" in x and "$LoopbackIndex=[int]$LoopbackRoute.InterfaceIndex" in x and "$LoopbackIndex=1" not in x for x in (start,stop,recovery)) and "DestinationPrefix '::1/128'" in status and "InterfaceIndex 1" not in status) -check("no_adapter_dns_set", "Set-DnsClientServerAddress" not in start+stop+recovery) -check("no_default_route_create", not re.search(r'(New-NetRoute|route\.exe\s+add|netsh\s+interface\s+ipv4\s+add\s+route)',start,re.I)) -check("direct_multiprotocol_engine", all(x in start for x in ( - "$DirectMethods=@('encrypted-dns-doh','http-host-split-tcp80','tls-sni-desync-tcp443','quic-desync-udp443','custom-hostlist','adult-catalog','strategy-profile')", - "'--wf-tcp=80,443'","'--wf-udp=443'","'--filter-tcp=80'","'--filter-tcp=443'","'--filter-udp=443'","'--filter-l7=quic'", - "'--dpi-desync-split-pos=method+2'","'--dpi-desync-split-pos=1,midsld'","'--dpi-desync-repeats=6'","'--new'" -))) -check("direct_multiprotocol_scope_controlled", "Add-ScopeHost" in start and "$scope='targeted'" in start and "all-sites" in start and "--hostlist-auto=" not in start) -check("secure_dns_no_os_leak", 'leak_on_upstream_failure = false' in ctrldcfg) -check("start_adapter_dns_regression", "ADAPTER_DNS_CHANGED" in start) -check("start_winhttp_regression", "WINHTTP_PROXY_CHANGED" in start) -check("start_ics_regression", "ICS_CHANGED_LIVE" in start and "ICS_CHANGED_DURING_DNS_STAGE" in start) -check("start_rollback", "Rollback $state" in start and "START_FAIL_" in start) -check("start_exact_process_argument_boundaries", "Start-ExactProcess" in start and "ProcessStartInfo" in start and "$psi.ArgumentList.Add" in start and "Start-Process -FilePath $Ctrld" not in start and "Start-Process -FilePath $Winws" not in start) -check("ctrld_scm_owned_service_architecture", "$CtrldServiceName='ctrld'" in start and "Start-OwnedCtrldService" in start and "CTRLD_SERVICE_PROCESS_OWNERSHIP_MISMATCH" in start and "$cp=Start-ExactProcess $Ctrld" not in start) -check("ctrld_external_service_fail_closed", "REFUSE_EXTERNAL_CTRLD_SERVICE" in start and "REFUSE_EXTERNAL_CTRLD_SERVICE" in stop and "REFUSE_EXTERNAL_CTRLD_SERVICE" in recovery) -check("ctrld_stop_recovery_service_managed", "Stop-OwnedCtrldService" in stop and "Stop-OwnedCtrldService" in recovery) -check("stop_preserves_external_routes", "STOP_MUTATED_EXTERNAL_ROUTES" in stop) -check("stop_preserves_external_nrpt", "STOP_MUTATED_EXTERNAL_NRPT" in stop) -check("stop_preserves_adapter_dns", "STOP_MUTATED_ADAPTER_DNSV4" in stop and "STOP_MUTATED_ADAPTER_DNSV6" in stop) -check("stop_no_state_does_not_remove_ula", "ULA is not removed without state ownership evidence." in stop) -check("recovery_state_root_guard", "STATE_ROOT_OWNERSHIP_MISMATCH" in recovery) -check("recovery_exact_orphan_ula_guard", all(x in recovery for x in ("ULA_OWNERSHIP_AMBIGUOUS","ULA_OWNERSHIP_MISMATCH","$ownedUla","Remove-NetIPAddress","RECOVERY_INCOMPLETE"))) -check("status_pid_name_ownership", "Get-PidHealth" in status and "ProcessName" in status) -check("status_external_conflict", all(x in status for x in ("CONFLICT","BLOCKED","VPN/tunnel"))) -check("status_fast_primitives", "Win32_SystemDriver" not in status and "Win32_Service" not in status) - -check("ui_native_exe_hidden_status_backend", "bundledPwshPath" in gui and '"Privileged", "runtime", "pwsh", "pwsh.exe"' in gui and "CreateNoWindow = true" in gui and "UseShellExecute = false" in gui) -check("ui_no_external_pwsh_dependency", 'ProcessStartInfo("pwsh.exe"' not in gui) -check("ui_no_direct_runas", 'Verb = "runas"' not in gui) -check("ui_uses_service_client", 'ServiceClient.Send(action)' in gui) -check("ui_waits_service_completion", "WaitForActionAsync" in gui and "action-status.json" in gui) -check("ui_blocked_state", '"BLOCKED"' in gui and "Disconnect external VPN/tunnel to Start" in gui) -check("ui_vector_hero", "' in xaml and 'TextWrapping="Wrap"' in xaml and '' in xaml) -check("target_hostlist_scope", "^gemini.google.com" in hostlist.splitlines() and all(x not in hostlist.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xnxx.com","xhamster.com")) and all(x in adult_fallback.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xvideos-cdn.com","xvcdn.com","xnxx.com","xnxx-cdn.com","xhamster.com","xhcdn.com","redtube.com","rdtcdn.com"))) -check("critical_global_services_hostlist", all(x in hostlist.splitlines() for x in ("openai.com","chatgpt.com","oaistatic.com","oaiusercontent.com","github.com","githubusercontent.com","githubassets.com","reddit.com","redditstatic.com","redditmedia.com","redd.it"))) -check("custom_site_hostlist", all(x in start for x in ("$CustomHosts=Join-Path $UserConfigDir 'custom-hosts.txt'","$RuntimeHostList=Join-Path $env:ProgramData 'DirectInternetMethod\\runtime-hosts.txt'","USER_CONFIG_INVALID","WriteAllLines($RuntimeHostList")) and all(x in gui for x in ("CustomSites_Click","NormalizeCustomSite","customHostsPath","Maximum 256 entries")) and 'Content="Custom Sites"' in xaml and 'DirectInternetMethod\\UserConfig' in iss and 'users-modify' in iss) -check("strategy_profiles", all(x in start for x in ("$StrategyFile=Join-Path $UserConfigDir 'strategy.txt'","balanced","compatibility","strong","fakedsplit","hostfakesplit","sniext+1","STRATEGY_INVALID")) and all(x in gui for x in ("Strategy_Click","strategyPath","Compatibility","Strong")) and 'Content="Strategy"' in xaml) -check("all_sites_scope_optional_default_targeted", all(x in start for x in ("$ScopeFile=Join-Path $UserConfigDir 'scope.txt'","$scope='targeted'","all-sites","SCOPE_INVALID","Add-ScopeHost")) and all(x in gui for x in ("scopePath","Apply DPI strategy to all web sites (experimental)","all-sites","targeted"))) -check("ui_gemini_adult_live_checks", all(x in xaml for x in ('Text="Gemini"','Text="Adult coverage"','x:Name="AdultSiteCheckToggle"','x:Name="GeminiText"','x:Name="AdultSiteText"')) and all(x in gui for x in ("https://www.pornhub.com/","https://www.xvideos.com/","https://www.xnxx.com/","https://xhamster.com/","FmtAdult")) and "pornhub" not in xaml.lower()) -check("ui_adult_check_default_off_persisted", all(x in gui for x in ("adultSiteLiveCheck","settings.json","AdultSiteCheckToggle_Changed")) and "bool adultCheckEnabled;" in gui) -check("adult_catalog_opt_in", all(x in gui for x in ("adultCoverageEnabled","adult-enabled.txt","adult-hosts.txt","nsfw-onlydomains.txt","EnsureAdultCatalogAsync","Adult catalog is unexpectedly small","xvideos.com","xnxx.com","xhamster.com")) and all(x in start for x in ("$AdultEnabledFile","$AdultHosts","$AdultFallback","adultHostCount","ADULT_CATALOG_INVALID")) and 'adult-fallback-hosts.txt' in iss) -check("startup_core_site_health_fields", all(x in start for x in ( - "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", - "required=2", - "passed=3-$failed.Count", - "allCoreSitesOk=($failed.Count -eq 0)", - "unreachableCoreSites=@($failed.ToArray())", - "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", - "$httpsProbePassCount=[int]$httpsHealth.passed", - "$ev.status='PASS_ACTIVE'", -))) -check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) -check("startup_true_youtube_page_plus_204_required", all(x in start for x in ( - "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", - "if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')}", - "$ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\\|' 8 2", - "$yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\\|' 8 2", - "$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh", - "youtubePage=$ytPage;youtube204=$yt", - "$httpsProbePassCount -lt 2", - "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", -)) and "function Get-CoreSiteHealth($yt,$oa,$gh)" not in start) -check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) -check("ui_openai_403_cannot_be_chatgpt_login_pass", - 'FmtOpenAiApiProbe(openAiProbe)' in gui - and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" })' in gui - and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401", "403" })' not in gui - and '"403" when p.Reached => "API HTTP 403 forbidden · login unverified"' in gui - and '"401" when p.Reached => "API HTTP 401 auth required · login unverified"' in gui - and '"200" when p.Reached => "API HTTP 200 reachable · login unverified"' in gui - and '"API transport unavailable · login unverified"' in gui) - -check("ui_live_probe_semantics", "FAIL · HTTP" in gui and 'https://gemini.google.com/' in gui and "adultTasks is not null" in gui and "full pages" in gui) -check("ui_partial_active_core_reachability", all(x in gui for x in ( - "bool coreReachabilityProven = false;", - "var youtubeProbe = await ytTask;", - "var openAiProbe = await oaTask;", - "var githubProbe = await ghTask;", - "var youtube204Probe = await yt204Task;", - "coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe);", - 'mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)', - 'StatusBadge.Text = "PARTIAL";', - "StatusBadge.Foreground = warn;", - "at least one core site unreachable", - 'StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED";', -))) - -check("ui_youtube_homepage_and_204_independent_health", all(x in gui for x in ( - 'var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });', - 'var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" });', - 'var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask };', - 'FullCoreReachability(ProbeResult youtubePage, ProbeResult youtube204, ProbeResult openai, ProbeResult github)', - 'youtubePage.Ok && youtube204.Ok && openai.Ok && github.Ok', - 'YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}";', - 'StatusBadge.Text = "PARTIAL";' -)) and "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;" not in gui) -check("ui_direct_update_service_handoff", 'ServiceClient.Send("update")' in gui and 'ServiceClient.Send("recovery")' in gui and '"handoff"' in gui and "300000" in gui and "DownloadVerifiedAsync" not in gui and "LaunchInstaller" not in gui) -check("ui_update_advisory_release_digest", "InstallerDigest" in updater and "digest" in updater and "SHA256SUMS.txt" in updater and "DownloadVerifiedAsync" not in updater and "Process.Start" not in updater) - -check("service_custom_controls", all(x in svc for x in ("CTRL_START = 128","CTRL_STOP = 129","CTRL_RECOVERY = 130","CTRL_UPDATE = 131")) and "case CTRL_UPDATE:" in svc and "_ = Task.Run(RunUpdateAsync)" in svc) -check("service_direct_update_security", all(x in svc for x in ("LatestReleaseApi","SHA256SUMS.txt","ApiSha256","SHA256.HashDataAsync","CryptographicException","GetLatestUpdateAsync","ParseExpectedSha256")) and "expected.Equals(package.ApiSha256" in svc and "actual.Equals(expected" in svc) -check("service_direct_update_atomic_download", '".part"' in svc and "DownloadWithSystemCurlAsync" in svc and '"-L", "--fail", "--silent", "--show-error"' in svc and '"--max-time", "210"' in svc and "File.Move(partPath, finalPath);" in svc and "OwnedUpdateInstallerPath" in svc and "Prior update still needs verification" in svc and "update-incomplete-preserved" in svc) -check("service_direct_update_silent_installer", all(x in svc for x in ('"/VERYSILENT"','"/SUPPRESSMSGBOXES"','"/NORESTART"','"/NORESTARTAPPLICATIONS"','"/CLOSEAPPLICATIONS"','"/SP-"')) and "UseShellExecute = false" in svc and 'Verb = "runas"' not in svc) -check("service_direct_update_requires_clean_off", "NetworkStatePath" in svc and "Direct Method must be OFF before update." in svc) -check("service_direct_update_post_restart_finalize", - all(x in svc for x in ("FinalizePendingAfterServiceRestartAsync", - "_ = Task.Run(FinalizePendingAfterServiceRestartAsync);", - "FinalizePendingUpdate();", - "pending-update-finalization-deferred-no-overwrite", - "update-stage-file-locked-preserving-single-pending-slot", - "pending-update.json", '"update", "done"', "targetVersion")) - and "StopEvent.IsSet" in svc) -check("service_direct_update_preserves_registered_user_install", "GetRegisteredInstallDirectory" in svc and "Registry.LocalMachine.OpenSubKey" in svc and "InstallLocation" in svc and '"/DIR=" + installDir' in svc and "DirectInternetMethod.exe" in svc and "manifest.json" in svc) -check("service_action_gate", "SemaphoreSlim ActionGate" in svc and "WaitAsync(0)" in svc) -check("service_action_status", "action-status.json" in svc and "WriteActionStatus" in svc and 'phase = "done"' not in svc) -check("service_fixed_scripts", all(x in svc for x in ('RunActionAsync("Start-Direct.ps1"','RunActionAsync("Stop-Direct.ps1"','RunActionAsync("Recovery.ps1"'))) -check("service_hidden_backend", "CreateNoWindow = true" in svc and "WindowStyle = ProcessWindowStyle.Hidden" in svc) -check("service_bundled_pwsh_only", '"runtime", "pwsh", "pwsh.exe"' in svc and 'return File.Exists(p) ? p : "pwsh.exe"' not in svc) -check("service_action_timeout_guard", "WaitForExit(120000)" in svc and "Kill(entireProcessTree: true)" in svc and "exceeded the 120 second safety timeout" in svc) -check("service_action_completion_no_inherited_pipe_deadlock", - "RedirectStandardOutput = true" not in svc - and "ReadToEndAsync" not in svc - and 'Log($"action-end:{actionName}:exit={p.ExitCode}")' in svc - and ( - ("RedirectStandardError = true" not in svc and "ReadActionErrorTailAsync" not in svc) - or ( - "RedirectStandardError = true" in svc - and "var stderrTask = ReadActionErrorTailAsync(p.StandardError);" in svc - and "while ((count = await reader.ReadAsync(buffer.AsMemory())) != 0)" in svc - and "tail.Length > 8192" in svc - and "await stderrTask.WaitAsync(TimeSpan.FromSeconds(3))" in svc - ) - )) -check("service_safe_action_errors_do_not_leak_backend_output", - "RedirectStandardError = true" not in svc - or ( - "DescribeActionFailure(p.ExitCode, stderr)" in svc - and 'standardError.Contains("DEFAULT_ROUTE_NOT_PHYSICAL"' in svc - and 'standardError.Contains("REFUSE_BROAD_TUNNEL_ROUTE"' in svc - and 'standardError.Contains("HASH_MISMATCH_"' in svc - and "WriteActionStatus(actionName, p.ExitCode == 0 ? \"done\" : \"error\", p.ExitCode, message)" in svc - and "RAW_STDERR_LEAK" in svc - )) -check("ui_wait_timeout_exceeds_service_timeout", "int timeoutMs = 130000" in gui) -check("client_minimum_access", "SERVICE_QUERY_STATUS | SERVICE_USER_DEFINED_CONTROL" in client and "SERVICE_CHANGE_CONFIG" not in client and "SERVICE_START" not in client and "SERVICE_STOP" not in client) -check("installer_service_acl_interactive_users_minimum", "(A;;LCCR;;;IU)" in iss and "sdset" in iss and "(A;;LCCR;;;AU)" not in iss) -check("installer_service_protected_root", "{commonpf}\\DirectInternetMethod\\Privileged" in iss) -check("single_registered_Windows_install_and_preserve_user_evidence", - 'AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}' in iss - and "UsePreviousAppDir=yes" in iss and "UninstallLogMode=overwrite" in iss - and "DIMExistingAppPathConflicts()" in iss - and "RegQueryStringValue(HKLM, Key, 'InstallLocation', OldDir)" in iss - and r'Type: filesandordirs; Name: "{app}\evidence"' not in iss - and r'Type: filesandordirs; Name: "{app}\bin"' not in iss - and r'Type: filesandordirs; Name: "{commonpf}\DirectInternetMethod\Privileged"' not in iss - and r'Type: dirifempty; Name: "{commonpf}\DirectInternetMethod\Privileged"' in iss) -check("owned_update_cache_never_duplicates_versioned_installers", - all(x in svc for x in ("OwnedUpdateInstallerPath", "DirectInternetMethod-update-stage.exe", - "Previous update is pending" if False else "Prior update still needs verification", - "File.Move(partPath, finalPath);", - "update-incomplete-preserved", - "update-stage-file-locked-preserving-single-pending-slot")) - and "File.Move(partPath, finalPath, true)" not in svc) - -check("legacy_provider_rollback_explicit_only_and_exact_SHA", - all(k in legacy_provider_guard for k in ( - "PUBLISHED_STABLE_151_INSTALLER_SHA_CHANGED", "PRESERVE_CUSTOM_PROVIDER_BEFORE_LEGACY_151_REINSTALL", - "PASS_LEGACY_151_PROVIDER_RESTORED_SHA", "FOREIGN_POST_STAGE_PROVIDER_EDIT_PRESERVED", - "PENDING_SLOT_EXISTS_NO_BLIND_REPLAY", "RESTORED_PROVIDER_SHA_FAILED", - "LEGACY_151_BINARY_POSTINSTALL_SHA_NOT_ACCEPTED")) - and "Remove-Item -Recurse" not in legacy_provider_guard - and 'Source: "..\\app\\SafeLegacyProviderRollback.ps1"; DestDir: "{commonpf}\\DirectInternetMethod\\Privileged\\app"; Flags: ignoreversion' in iss - and ("privileged","app/SafeLegacyProviderRollback.ps1") in - {(v["scope"],v["file"]) for v in manifest["files"]}) -check("legacy_151_rollback_preserves_custom_bin_tree", - all(x in legacy_provider_guard for x in ["binFiles","BIN_RESTORATION_SHA_FAILED","FOREIGN_BIN_EDIT_PRESERVED","BIN_SNAPSHOT_UNKNOWN_CONTENT","BIN_STAGED_FILE_CHANGED","FOREIGN_STAGE_SYMLINK"]) - and "Remove-Item -Recurse" not in legacy_provider_guard) -check("inno_preserves_custom_router_gateway_provider_on_upgrade", - 'Source: "..\\..\\router_gateway\\providers.json"; DestDir: "{app}\\app\\router_gateway"; Flags: ignoreversion onlyifdoesntexist' in iss) -check("installer_admin_only_install", "PrivilegesRequired=admin" in iss) -check("installer_service_recovery", 'failure "' in iss and "failureflag" in iss) -check("installer_preserves_pending_update_until_service_SHA_verifies", - "CompletePendingDirectUpdate" in iss and "pending-update.json" in iss - and "new service completes SHA-checked cleanup" in iss - and r"PendingPath := ExpandConstant('{commonappdata}\DirectInternetMethod\pending-update.json')" in iss - and "DeleteFile(PendingPath)" not in iss - and '"action":"update","phase":"done"' not in iss) -check("installer_ctrld_owned_demand_service", "CtrldServiceName = 'ctrld'" in iss and "InstallOrUpdateCtrldService" in iss and "start= demand" in iss and "CurrentControlSet\\Services\\" in iss) -check("installer_ctrld_collision_fail_closed", "A different Windows service named ctrld already exists" in iss and "CtrldServiceOwned()" in iss) -check("installer_ctrld_owned_uninstall", "StopCtrldServiceIfOwned" in iss and "delete \"" in iss and "CtrldServiceName" in iss) -check("installer_shortcuts_exe", 'DirectInternetMethod.exe' in iss and 'ControlPanel.cmd' not in "\n".join(x for x in iss.splitlines() if x.startswith("Name:"))) -check("installer_active_guard", "PrepareToInstall" in iss and "DirectDnsDpiHarness" in iss and "state.json" in iss) -check("installer_bundled_pwsh", '..\\vendor\\pwsh\\*' in iss and 'Privileged\\runtime\\pwsh' in iss and "PowerShell7Available" not in iss) -check("installer_uninstall_recovery_gate", "InitializeUninstall" in iss and "Recovery did not complete" in iss) -check("windows_install_registration_guard", all(x in install_registration_guard for x in ("HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","noSameAppIdPerUserDuplicate","registeredUninstallerPresent","installedReleaseMatches"))) -check("installed_release_verifier_delegates_registration_guard", "windows_install_registration_audit.ps1" in installed_release_verifier and "Where-Object DisplayName" not in installed_release_verifier and "pwsh.exe" in installed_release_verifier) -check("windows_build_shared_exclusive_lock", all(x in payload_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED")) and all(x in installer_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED"))) -check("windows_build_python_portability_failfast", all(x in payload_builder for x in ( - "param([switch]$PreflightOnly)", - "WINDOWS_BUILD_PYTHON_MISSING:", - "WINDOWS_BUILD_VENDOR_PWSH_SHA_INVALID", - "WINDOWS_BUILD_PORTABLE_PYTHON_SHA_INVALID", - "4942B86A6597E5AEE0128DAA00050ED79BC21F6E709A78EB19CBFEB0C2F39AC9", - r"& $PythonExe '.\tests\build_windows_manifest.py'", - r"& $PythonExe '.\tests\windows_contract_audit.py'", -)) and "& py " not in payload_builder and - payload_builder.index("WINDOWS_BUILD_PYTHON_MISSING:") < payload_builder.index("& dotnet publish")) -check("windows_installer_output_stability_guard", all(x in installer_builder for x in ("OUTPUT_STILL_LOCKED_AFTER_ISCC_EXIT","@($Sizes | Select-Object -Unique).Count","OUTPUT_SIZE_UNSTABLE_","outputStableSamples"))) -tracked_strong=subprocess.run(["git","ls-files","--error-unmatch","windows/bin/zapret/strong-override-hosts.txt"],cwd=R,text=True,capture_output=True).returncode==0 -check("windows_strong_override_source_tracked", tracked_strong) -check("windows_hash_pinned_text_eol_policy", all(x in gitattributes for x in ("windows/bin/zapret/hosts.txt text eol=crlf","windows/bin/zapret/adult-fallback-hosts.txt text eol=lf","windows/bin/zapret/strong-override-hosts.txt text eol=crlf"))) -check("optional_signed_diagnostic_fails_closed_but_is_not_public_gate", all(x in signature_gate for x in ( - "Get-AuthenticodeSignature", - "Assert-EligibleSignerForSmartAppControl -Signature $sig", - "if([string]$Signature.Status -ne 'Valid')", - "SELF_SIGNED_SIGNER_NOT_PUBLIC_TRUSTED", - "SMART_APP_CONTROL_RSA_SIGNER_REQUIRED", - "CODE_SIGNING_EKU_REQUIRED", - "PUBLIC_TRUST_CHAIN_UNVERIFIED", - "FAIL_CLOSED","exit 42" -))) -tag_job_source = ci_workflow.split(" windows-release-contract:",1)[1] if ci_workflow.count(" windows-release-contract:")==1 else "" -check("tag_release_invokes_real_unsigned_three_way_sha_and_hosted_rollback", - tag_job_source.count("python -B tests/unsigned_public_release_contract.py --public-tag")==1 - and "if: startsWith(github.ref, 'refs/tags/v')" in tag_job_source - and "GH_TOKEN:" in tag_job_source and "GITHUB_TOKEN:" in tag_job_source - and "actions: read" in tag_job_source - and "windows_distribution_signature_audit.ps1" not in tag_job_source - and "windows_release_draft_asset_stage.ps1" not in tag_job_source - and "continue-on-error: true" not in tag_job_source - and "if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }" in tag_job_source) - -draft_stage_source=(R/"tests"/"windows_release_draft_asset_stage.ps1").read_text(encoding="utf-8-sig") -draft_fixture_source=(R/"tests"/"windows_release_draft_asset_contract.ps1").read_text(encoding="utf-8-sig") -windows_source_section=ci_workflow.split(" windows-source:",1)[1].split(" windows-release-contract:",1)[0] -check("windows_ci_runs_offline_draft_staging_negative_regressions", - windows_source_section.count("pwsh.exe -NoProfile -NonInteractive -File tests/windows_release_draft_asset_contract.ps1")==1 - and "continue-on-error: true" not in windows_source_section) -check("windows_source_ci_runs_unsigned_release_negative_fixtures", - windows_source_section.count("python tests/unsigned_public_release_contract.py --fixtures")==1) -check("unsigned_publication_never_requires_private_signed_draft_staging", - "windows_release_draft_asset_stage.ps1" not in tag_job_source - and "windows_distribution_signature_audit.ps1" not in tag_job_source - and "unsigned_public_release_contract.py --fixtures" in tag_job_source - and "unsigned_public_release_contract.py --public-tag" in tag_job_source - and "continue-on-error: true" not in tag_job_source) -check("unsigned_verifier_checks_remote_sha_and_raw_hosted_receipts", - all(s in (R/"tests"/"unsigned_public_release_contract.py").read_text(encoding="utf-8-sig") for s in ( - "PUBLIC_THREE_WAY_SHA_DISAGREEMENT", "HOSTED_RAW_REPORT_SHA_MISMATCH", - "HOSTED_REPORT_UNRESOLVED_ERRORS", "GITHUB_RUN_NOT_SUCCESS", - "SIGNING_POLICY_MUST_NOT_BE_REQUIRED"))) - -check("draft_release_stager_fails_closed_on_identity_digest_and_rerun", - all(s in draft_stage_source for s in ( - "RELEASE_NOT_PRIVATE_DRAFT","RELEASE_TARGET_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DIGEST_MISMATCH", - "SIGNED_DRAFT_INSTALLER_ASSET_ORIGIN_MISMATCH","UNSAFE_RERUN_STAGING_PATH_OCCUPIED", - "TAG_CHECKOUT_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DOWNLOADED_SHA_MISMATCH","exit 42"))) -check("draft_release_negative_fixtures_present", - all(s in draft_fixture_source for s in ( - "Deny 'published'","Deny 'tag'","Deny 'commit'","Deny 'duplicate'", - "Deny 'digest'","Deny 'origin'","PASS 10/10"))) - -check("windows_ci_external_audits_fail_fast", ci_workflow.count("if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }") >= 7) - -files={(x["scope"],x["file"]) for x in manifest["files"]} -release_meta=json.loads(txt("RELEASE.json")) -allowed_status={ - "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), - "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), - "1.5.6":("PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), -} -current=release_meta.get("version") -check("manifest_version", current in allowed_status - and manifest.get("version")==current - and release_meta.get("status")==allowed_status[current][0] - and release_meta.get("previousAccepted",{}).get("version")==allowed_status[current][1]) - -check("manifest_direct_update", manifest.get("directUpdate",{}).get("userUacRequired") is False and "SHA256SUMS.txt" in manifest.get("directUpdate",{}).get("integrity","")) -check("manifest_native_exe", ("user","app/DirectInternetMethod.exe") in files) -check("manifest_router_gateway_data", ("user","app/router_gateway/providers.json") in files) -check("manifest_adult_fallback", ("privileged","bin/zapret/adult-fallback-hosts.txt") in files) -check("manifest_strong_override", ("privileged","bin/zapret/strong-override-hosts.txt") in files) -check("manifest_service", ("privileged","app/DirectInternetMethod.Service.exe") in files) -check("manifest_no_helper", not any(x[1].endswith("Helper.exe") for x in files)) -check("manifest_bundled_pwsh", manifest.get("bundledPowerShellVersion")=="7.6.6" and manifest.get("bundledPowerShellArchiveSha256")=="02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860" and ("privileged","runtime/pwsh/pwsh.exe") in files) -check("manifest_bundled_pwsh_tree", sum(1 for s,f in files if s=="privileged" and f.startswith("runtime/pwsh/")) >= 650) -check("manifest_no_legacy_launchers", not any(scope=="user" and (f.endswith(".cmd") or f in {"app/ControlPanel.ps1","app/Toggle.ps1"}) for scope,f in files)) - -pin_paths={ -"ctrld.exe":"bin/ctrld/ctrld.exe", -"ctrld.toml":"bin/ctrld/ctrld.toml", -"winws.exe":"bin/zapret/winws.exe", -"WinDivert.dll":"bin/zapret/WinDivert.dll", -"WinDivert64.sys":"bin/zapret/WinDivert64.sys", -"cygwin1.dll":"bin/zapret/cygwin1.dll", -"hosts.txt":"bin/zapret/hosts.txt", -"adult-fallback-hosts.txt":"bin/zapret/adult-fallback-hosts.txt", -"strong-override-hosts.txt":"bin/zapret/strong-override-hosts.txt", -} -start_pins=dict(re.findall(r"'([^']+)'='([0-9A-F]{64})'",start)) -pin_actual={k:hashlib.sha256((W/v).read_bytes()).hexdigest().upper() for k,v in pin_paths.items()} -D["selfIntegrityPins"]={"declared":{k:start_pins.get(k) for k in pin_paths},"actual":pin_actual} -check("start_self_integrity_pins",all(start_pins.get(k)==v for k,v in pin_actual.items())) - -expected={ -"ctrld":"FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD", -"winws":"A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8", -"windivertdll":"C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2", -"windivertsys":"8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2", -"pwsh":"BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285"} -for name,rel in (("ctrld","bin/ctrld/ctrld.exe"),("winws","bin/zapret/winws.exe"),("windivertdll","bin/zapret/WinDivert.dll"),("windivertsys","bin/zapret/WinDivert64.sys"),("pwsh","vendor/pwsh/pwsh.exe")): - h=hashlib.sha256((W/rel).read_bytes()).hexdigest().upper();D["hashes"][name]=h;check("runtime_"+name+"_pin",h==expected[name]) - -thirdparty_notice_hashes={ - "LICENSE.WinDivert.txt":"14A0CB5214D536E4FDAE6AA3F5696F981EEDA106CD026E9794BBA489EE79D628", - "LICENSE.Cygwin.txt":"794433752103CF4BBB4A84A1BDB8FBC150ABB1762704BB35FECC9F7F820BE984", - "LGPL3.Cygwin.txt":"DA7EABB7BAFDF7D3AE5E9F223AA5BDC1EECE45AC569DC21B3B037520B4464768", - "GPL3.Cygwin.txt":"8CEB4B9EE5ADEDDE47B31E975C1D90C73AD27B6B165A1DCD80C7C545EB65B903", -} -def checked_notice_mapping(filename): - src = r"..\bin\zapret" + "\\" + filename - dest = r"{commonpf}\DirectInternetMethod\Privileged\bin\zapret" - line = 'Source: "' + src + '"; DestDir: "' + dest + '"; Flags: ignoreversion notimestamp' - return iss.splitlines().count(line)==1 -def r549_inno_source_timestamp_contract(source): - try: - rows_section=source.split("[Files]\n",1)[1].split("[InstallDelete]",1)[0] - except IndexError: - return False - rows=[row.strip() for row in rows_section.splitlines() - if row.strip().startswith("Source: ")] - if len(rows)!=28 or len(set(rows))!=28: - return False - for row in rows: - if "Flags:" not in row: - return False - flags=row.split("Flags:",1)[1].strip().split() - if flags.count("notimestamp")!=1 or "touch" in flags or "comparetimestamp" in flags: - return False - return True - -# Negative-first source-time fixture: exactly 28 embedded records without -# stored input timestamps. No change to owned uninstall/upgrade file scope. -check("r549_inno_no_source_timestamps", - r549_inno_source_timestamp_contract(iss) - and not r549_inno_source_timestamp_contract(iss.replace(" notimestamp","",1)) - and not r549_inno_source_timestamp_contract( - iss.replace(" notimestamp"," notimestamp touch",1))) - -check("installer_four_upstream_license_notices_byte_pinned", - all( - (W/"bin"/"zapret"/filename).is_file() - and hashlib.sha256((W/"bin"/"zapret"/filename).read_bytes()).hexdigest().upper()==expected - and checked_notice_mapping(filename) - and "windows/bin/zapret/"+filename+" text eol=lf" in gitattributes - for filename,expected in thirdparty_notice_hashes.items() - )) - -print(json.dumps(D,indent=2)) -sys.exit(0 if D["status"]=="PASS" else 20) +#!/usr/bin/env python3 +from pathlib import Path +import hashlib,json,re,sys,subprocess +R=Path(__file__).resolve().parents[1] +W=R/"windows" +def txt(rel): return (W/rel).read_text(encoding="utf-8-sig") +start,stop,recovery,status,iss=map(txt,["app/Start-Direct.ps1","app/Stop-Direct.ps1","app/Recovery.ps1","app/Status.ps1","installer/DirectInternetMethod.iss"]) +legacy_provider_guard=txt("app/SafeLegacyProviderRollback.ps1") +gui=txt("gui/MainWindow.xaml.cs"); xaml=txt("gui/MainWindow.xaml") +svc=txt("service/Program.cs"); client=txt("gui/ServiceClient.cs"); updater=txt("gui/UpdateClient.cs") +manifest=json.loads(txt("manifest.json")); ctrldcfg=txt("bin/ctrld/ctrld.toml"); hostlist=txt("bin/zapret/hosts.txt"); adult_fallback=txt("bin/zapret/adult-fallback-hosts.txt"); strong_override=txt("bin/zapret/strong-override-hosts.txt") +payload_builder=(R/"tests"/"build_windows_152_payload.ps1").read_text(encoding="utf-8-sig") +installer_builder=(R/"tests"/"build_windows_152_installer.ps1").read_text(encoding="utf-8-sig") +ci_workflow=(R/".github"/"workflows"/"ci.yml").read_text(encoding="utf-8-sig") +signature_gate=(R/"tests"/"windows_distribution_signature_audit.ps1").read_text(encoding="utf-8-sig") +gitattributes=(R/".gitattributes").read_text(encoding="utf-8-sig") +install_registration_guard=(R/"tests"/"windows_install_registration_audit.ps1").read_text(encoding="utf-8-sig") +native_r535_uninstall=(R/"tests"/"r535_windows_native_rollback.ps1").read_text(encoding="utf-8-sig") +installed_release_verifier=(R/"tests"/"windows_installed_release_audit.ps1").read_text(encoding="utf-8-sig") +D={"schema":2,"status":"PASS","checks":{},"hashes":{}} +def check(name,cond): + D["checks"][name]=bool(cond) + if not cond:D["status"]="FAIL" +def idx(s,x): return s.find(x) + +check("r535_registered_uninstaller_suffix_agnostic", all(x in native_r535_uninstall for x in ( + "function ExactRegisteredUninstaller([object]$d)", + "unins[0-9]{3}[.](exe|dat|msg)", + "'ONE_UNINSTALLER'", "'MATCHED_UNINSTALL_LOG'", + "'EXACT_UNINSTALL_PATH_OWNED'", + "[void](ExactRegisteredUninstaller $d)", + "$uninstaller=ExactRegisteredUninstaller $d", +)) and "$uninstaller=Join-Path $App 'unins000.exe'" not in native_r535_uninstall) + +check("start_refuses_broad_tunnel_before_mutation", idx(start,"REFUSE_BROAD_TUNNEL_ROUTE")>=0 and idx(start,"REFUSE_BROAD_TUNNEL_ROUTE") < idx(start,"New-NetIPAddress")) +check("loopback_index_dynamic", all("DestinationPrefix '::1/128'" in x and "$LoopbackIndex=[int]$LoopbackRoute.InterfaceIndex" in x and "$LoopbackIndex=1" not in x for x in (start,stop,recovery)) and "DestinationPrefix '::1/128'" in status and "InterfaceIndex 1" not in status) +check("no_adapter_dns_set", "Set-DnsClientServerAddress" not in start+stop+recovery) +check("no_default_route_create", not re.search(r'(New-NetRoute|route\.exe\s+add|netsh\s+interface\s+ipv4\s+add\s+route)',start,re.I)) +check("direct_multiprotocol_engine", all(x in start for x in ( + "$DirectMethods=@('encrypted-dns-doh','http-host-split-tcp80','tls-sni-desync-tcp443','quic-desync-udp443','custom-hostlist','adult-catalog','strategy-profile')", + "'--wf-tcp=80,443'","'--wf-udp=443'","'--filter-tcp=80'","'--filter-tcp=443'","'--filter-udp=443'","'--filter-l7=quic'", + "'--dpi-desync-split-pos=method+2'","'--dpi-desync-split-pos=1,midsld'","'--dpi-desync-repeats=6'","'--new'" +))) +check("direct_multiprotocol_scope_controlled", "Add-ScopeHost" in start and "$scope='targeted'" in start and "all-sites" in start and "--hostlist-auto=" not in start) +check("secure_dns_no_os_leak", 'leak_on_upstream_failure = false' in ctrldcfg) +check("start_adapter_dns_regression", "ADAPTER_DNS_CHANGED" in start) +check("start_winhttp_regression", "WINHTTP_PROXY_CHANGED" in start) +check("start_ics_regression", "ICS_CHANGED_LIVE" in start and "ICS_CHANGED_DURING_DNS_STAGE" in start) +check("start_rollback", "Rollback $state" in start and "START_FAIL_" in start) +check("start_exact_process_argument_boundaries", "Start-ExactProcess" in start and "ProcessStartInfo" in start and "$psi.ArgumentList.Add" in start and "Start-Process -FilePath $Ctrld" not in start and "Start-Process -FilePath $Winws" not in start) +check("ctrld_scm_owned_service_architecture", "$CtrldServiceName='ctrld'" in start and "Start-OwnedCtrldService" in start and "CTRLD_SERVICE_PROCESS_OWNERSHIP_MISMATCH" in start and "$cp=Start-ExactProcess $Ctrld" not in start) +check("ctrld_external_service_fail_closed", "REFUSE_EXTERNAL_CTRLD_SERVICE" in start and "REFUSE_EXTERNAL_CTRLD_SERVICE" in stop and "REFUSE_EXTERNAL_CTRLD_SERVICE" in recovery) +check("ctrld_stop_recovery_service_managed", "Stop-OwnedCtrldService" in stop and "Stop-OwnedCtrldService" in recovery) +check("stop_preserves_external_routes", "STOP_MUTATED_EXTERNAL_ROUTES" in stop) +check("stop_preserves_external_nrpt", "STOP_MUTATED_EXTERNAL_NRPT" in stop) +check("stop_preserves_adapter_dns", "STOP_MUTATED_ADAPTER_DNSV4" in stop and "STOP_MUTATED_ADAPTER_DNSV6" in stop) +check("stop_no_state_does_not_remove_ula", "ULA is not removed without state ownership evidence." in stop) +check("recovery_state_root_guard", "STATE_ROOT_OWNERSHIP_MISMATCH" in recovery) +check("recovery_exact_orphan_ula_guard", all(x in recovery for x in ("ULA_OWNERSHIP_AMBIGUOUS","ULA_OWNERSHIP_MISMATCH","$ownedUla","Remove-NetIPAddress","RECOVERY_INCOMPLETE"))) +check("status_pid_name_ownership", "Get-PidHealth" in status and "ProcessName" in status) +check("status_external_conflict", all(x in status for x in ("CONFLICT","BLOCKED","VPN/tunnel"))) +check("status_fast_primitives", "Win32_SystemDriver" not in status and "Win32_Service" not in status) + +check("ui_native_exe_hidden_status_backend", "bundledPwshPath" in gui and '"Privileged", "runtime", "pwsh", "pwsh.exe"' in gui and "CreateNoWindow = true" in gui and "UseShellExecute = false" in gui) +check("ui_no_external_pwsh_dependency", 'ProcessStartInfo("pwsh.exe"' not in gui) +check("ui_no_direct_runas", 'Verb = "runas"' not in gui) +check("ui_uses_service_client", 'ServiceClient.Send(action)' in gui) +check("ui_waits_service_completion", "WaitForActionAsync" in gui and "action-status.json" in gui) +check("ui_blocked_state", '"BLOCKED"' in gui and "Disconnect external VPN/tunnel to Start" in gui) +check("ui_vector_hero", "' in xaml and 'TextWrapping="Wrap"' in xaml and '' in xaml) +check("target_hostlist_scope", "^gemini.google.com" in hostlist.splitlines() and all(x not in hostlist.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xnxx.com","xhamster.com")) and all(x in adult_fallback.splitlines() for x in ("pornhub.com","phncdn.com","xvideos.com","xvideos-cdn.com","xvcdn.com","xnxx.com","xnxx-cdn.com","xhamster.com","xhcdn.com","redtube.com","rdtcdn.com"))) +check("critical_global_services_hostlist", all(x in hostlist.splitlines() for x in ("openai.com","chatgpt.com","oaistatic.com","oaiusercontent.com","github.com","githubusercontent.com","githubassets.com","reddit.com","redditstatic.com","redditmedia.com","redd.it"))) +check("custom_site_hostlist", all(x in start for x in ("$CustomHosts=Join-Path $UserConfigDir 'custom-hosts.txt'","$RuntimeHostList=Join-Path $env:ProgramData 'DirectInternetMethod\\runtime-hosts.txt'","USER_CONFIG_INVALID","WriteAllLines($RuntimeHostList")) and all(x in gui for x in ("CustomSites_Click","NormalizeCustomSite","customHostsPath","Maximum 256 entries")) and 'Content="Custom Sites"' in xaml and 'DirectInternetMethod\\UserConfig' in iss and 'users-modify' in iss) +check("strategy_profiles", all(x in start for x in ("$StrategyFile=Join-Path $UserConfigDir 'strategy.txt'","balanced","compatibility","strong","fakedsplit","hostfakesplit","sniext+1","STRATEGY_INVALID")) and all(x in gui for x in ("Strategy_Click","strategyPath","Compatibility","Strong")) and 'Content="Strategy"' in xaml) +check("all_sites_scope_optional_default_targeted", all(x in start for x in ("$ScopeFile=Join-Path $UserConfigDir 'scope.txt'","$scope='targeted'","all-sites","SCOPE_INVALID","Add-ScopeHost")) and all(x in gui for x in ("scopePath","Apply DPI strategy to all web sites (experimental)","all-sites","targeted"))) +check("ui_gemini_adult_live_checks", all(x in xaml for x in ('Text="Gemini"','Text="Adult coverage"','x:Name="AdultSiteCheckToggle"','x:Name="GeminiText"','x:Name="AdultSiteText"')) and all(x in gui for x in ("https://www.pornhub.com/","https://www.xvideos.com/","https://www.xnxx.com/","https://xhamster.com/","FmtAdult")) and "pornhub" not in xaml.lower()) +check("ui_adult_check_default_off_persisted", all(x in gui for x in ("adultSiteLiveCheck","settings.json","AdultSiteCheckToggle_Changed")) and "bool adultCheckEnabled;" in gui) +check("adult_catalog_opt_in", all(x in gui for x in ("adultCoverageEnabled","adult-enabled.txt","adult-hosts.txt","nsfw-onlydomains.txt","EnsureAdultCatalogAsync","Adult catalog is unexpectedly small","xvideos.com","xnxx.com","xhamster.com")) and all(x in start for x in ("$AdultEnabledFile","$AdultHosts","$AdultFallback","adultHostCount","ADULT_CATALOG_INVALID")) and 'adult-fallback-hosts.txt' in iss) +check("startup_core_site_health_fields", all(x in start for x in ( + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", + "required=2", + "passed=3-$failed.Count", + "allCoreSitesOk=($failed.Count -eq 0)", + "unreachableCoreSites=@($failed.ToArray())", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", + "$httpsProbePassCount=[int]$httpsHealth.passed", + "$ev.status='PASS_ACTIVE'", +))) +check("startup_https_health_quorum", all(x in start for x in ("Curl-Probe-Retry","HTTPS_HEALTH_QUORUM_FAIL","$httpsProbePassCount -lt 2","$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh","httpsHealth=$httpsHealth")) and all(x not in start for x in ("throw 'YOUTUBE_HTTP80_FAIL'","throw 'YOUTUBE_HTTPS_FAIL'","throw 'OPENAI_HTTPS_FAIL'","throw 'GITHUB_HTTPS_FAIL'"))) +check("startup_true_youtube_page_plus_204_required", all(x in start for x in ( + "function Get-CoreSiteHealth($ytPage,$yt204,$oa,$gh)", + "if(-not ([bool]$ytPage.ok -and [bool]$yt204.ok)){[void]$failed.Add('youtube')}", + "$ytPage=Curl-Probe-Retry 'https://www.youtube.com/' $route.LocalIp '^200\\|' 8 2", + "$yt=Curl-Probe-Retry 'https://www.youtube.com/generate_204' $route.LocalIp '^204\\|' 8 2", + "$httpsHealth=Get-CoreSiteHealth $ytPage $yt $oa $gh", + "youtubePage=$ytPage;youtube204=$yt", + "$httpsProbePassCount -lt 2", + "reachability=if($failed.Count -eq 0){'HEALTHY'}else{'PARTIAL'}", +)) and "function Get-CoreSiteHealth($yt,$oa,$gh)" not in start) +check("adult_strong_override_profile", all(x in strong_override.splitlines() for x in ("xhamster.com","xhamsterlive.com","xhcdn.com","reddit.com","redditstatic.com","redditmedia.com","redd.it")) and all(x in start for x in ("$StrongOverrideHostList","strong-override-hosts.txt","--hostlist='+$StrongOverrideHostList","--dpi-desync=fake,hostfakesplit","--dpi-desync-repeats=11")) and 'strong-override-hosts.txt' in iss) +check("ui_openai_403_cannot_be_chatgpt_login_pass", + 'FmtOpenAiApiProbe(openAiProbe)' in gui + and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401" })' in gui + and 'ProbeAsync("https://api.openai.com/v1/models", IpText.Text, new[] { "200", "401", "403" })' not in gui + and '"403" when p.Reached => "API HTTP 403 forbidden · login unverified"' in gui + and '"401" when p.Reached => "API HTTP 401 auth required · login unverified"' in gui + and '"200" when p.Reached => "API HTTP 200 reachable · login unverified"' in gui + and '"API transport unavailable · login unverified"' in gui) + +check("ui_live_probe_semantics", "FAIL · HTTP" in gui and 'https://gemini.google.com/' in gui and "adultTasks is not null" in gui and "full pages" in gui) +check("ui_partial_active_core_reachability", all(x in gui for x in ( + "bool coreReachabilityProven = false;", + "var youtubeProbe = await ytTask;", + "var openAiProbe = await oaTask;", + "var githubProbe = await ghTask;", + "var youtube204Probe = await yt204Task;", + "coreReachabilityProven = FullCoreReachability(youtubeProbe, youtube204Probe, openAiProbe, githubProbe);", + 'mode == "ACTIVE" && (!ownedHealthy || !coreReachabilityProven)', + 'StatusBadge.Text = "PARTIAL";', + "StatusBadge.Foreground = warn;", + "at least one core site unreachable", + 'StopButton.IsEnabled = mode is "ACTIVE" or "CONFLICT" or "DEGRADED";', +))) + +check("ui_youtube_homepage_and_204_independent_health", all(x in gui for x in ( + 'var ytTask = ProbeAsync("https://www.youtube.com/", IpText.Text, new[] { "200" });', + 'var yt204Task = ProbeAsync("https://www.youtube.com/generate_204", IpText.Text, new[] { "204" });', + 'var tasks = new List { ytTask, yt204Task, oaTask, ghTask, geminiTask };', + 'FullCoreReachability(ProbeResult youtubePage, ProbeResult youtube204, ProbeResult openai, ProbeResult github)', + 'youtubePage.Ok && youtube204.Ok && openai.Ok && github.Ok', + 'YoutubeText.Text = $"Page {FmtHttp(youtubeProbe)} | 204 {FmtHttp(youtube204Probe)}";', + 'StatusBadge.Text = "PARTIAL";' +)) and "coreReachabilityProven = youtubeProbe.Ok && openAiProbe.Ok && githubProbe.Ok;" not in gui) +check("ui_direct_update_service_handoff", 'ServiceClient.Send("update")' in gui and 'ServiceClient.Send("recovery")' in gui and '"handoff"' in gui and "300000" in gui and "DownloadVerifiedAsync" not in gui and "LaunchInstaller" not in gui) +check("ui_update_advisory_release_digest", "InstallerDigest" in updater and "digest" in updater and "SHA256SUMS.txt" in updater and "DownloadVerifiedAsync" not in updater and "Process.Start" not in updater) + +check("service_custom_controls", all(x in svc for x in ("CTRL_START = 128","CTRL_STOP = 129","CTRL_RECOVERY = 130","CTRL_UPDATE = 131")) and "case CTRL_UPDATE:" in svc and "_ = Task.Run(RunUpdateAsync)" in svc) +check("service_direct_update_security", all(x in svc for x in ("LatestReleaseApi","SHA256SUMS.txt","ApiSha256","SHA256.HashDataAsync","CryptographicException","GetLatestUpdateAsync","ParseExpectedSha256")) and "expected.Equals(package.ApiSha256" in svc and "actual.Equals(expected" in svc) +check("service_direct_update_atomic_download", '".part"' in svc and "DownloadWithSystemCurlAsync" in svc and '"-L", "--fail", "--silent", "--show-error"' in svc and '"--max-time", "210"' in svc and "File.Move(partPath, finalPath);" in svc and "OwnedUpdateInstallerPath" in svc and "Prior update still needs verification" in svc and "update-incomplete-preserved" in svc) +check("service_direct_update_silent_installer", all(x in svc for x in ('"/VERYSILENT"','"/SUPPRESSMSGBOXES"','"/NORESTART"','"/NORESTARTAPPLICATIONS"','"/CLOSEAPPLICATIONS"','"/SP-"')) and "UseShellExecute = false" in svc and 'Verb = "runas"' not in svc) +check("service_direct_update_requires_clean_off", "NetworkStatePath" in svc and "Direct Method must be OFF before update." in svc) +check("service_direct_update_post_restart_finalize", + all(x in svc for x in ("FinalizePendingAfterServiceRestartAsync", + "_ = Task.Run(FinalizePendingAfterServiceRestartAsync);", + "FinalizePendingUpdate();", + "pending-update-finalization-deferred-no-overwrite", + "update-stage-file-locked-preserving-single-pending-slot", + "pending-update.json", '"update", "done"', "targetVersion")) + and "StopEvent.IsSet" in svc) +check("service_direct_update_preserves_registered_user_install", "GetRegisteredInstallDirectory" in svc and "Registry.LocalMachine.OpenSubKey" in svc and "InstallLocation" in svc and '"/DIR=" + installDir' in svc and "DirectInternetMethod.exe" in svc and "manifest.json" in svc) +check("service_action_gate", "SemaphoreSlim ActionGate" in svc and "WaitAsync(0)" in svc) +check("service_action_status", "action-status.json" in svc and "WriteActionStatus" in svc and 'phase = "done"' not in svc) +check("service_fixed_scripts", all(x in svc for x in ('RunActionAsync("Start-Direct.ps1"','RunActionAsync("Stop-Direct.ps1"','RunActionAsync("Recovery.ps1"'))) +check("service_hidden_backend", "CreateNoWindow = true" in svc and "WindowStyle = ProcessWindowStyle.Hidden" in svc) +check("service_bundled_pwsh_only", '"runtime", "pwsh", "pwsh.exe"' in svc and 'return File.Exists(p) ? p : "pwsh.exe"' not in svc) +check("service_action_timeout_guard", "WaitForExit(120000)" in svc and "Kill(entireProcessTree: true)" in svc and "exceeded the 120 second safety timeout" in svc) +check("service_action_completion_no_inherited_pipe_deadlock", + "RedirectStandardOutput = true" not in svc + and "ReadToEndAsync" not in svc + and 'Log($"action-end:{actionName}:exit={p.ExitCode}")' in svc + and ( + ("RedirectStandardError = true" not in svc and "ReadActionErrorTailAsync" not in svc) + or ( + "RedirectStandardError = true" in svc + and "var stderrTask = ReadActionErrorTailAsync(p.StandardError);" in svc + and "while ((count = await reader.ReadAsync(buffer.AsMemory())) != 0)" in svc + and "tail.Length > 8192" in svc + and "await stderrTask.WaitAsync(TimeSpan.FromSeconds(3))" in svc + ) + )) +check("service_safe_action_errors_do_not_leak_backend_output", + "RedirectStandardError = true" not in svc + or ( + "DescribeActionFailure(p.ExitCode, stderr)" in svc + and 'standardError.Contains("DEFAULT_ROUTE_NOT_PHYSICAL"' in svc + and 'standardError.Contains("REFUSE_BROAD_TUNNEL_ROUTE"' in svc + and 'standardError.Contains("HASH_MISMATCH_"' in svc + and "WriteActionStatus(actionName, p.ExitCode == 0 ? \"done\" : \"error\", p.ExitCode, message)" in svc + and "RAW_STDERR_LEAK" in svc + )) +check("ui_wait_timeout_exceeds_service_timeout", "int timeoutMs = 130000" in gui) +check("client_minimum_access", "SERVICE_QUERY_STATUS | SERVICE_USER_DEFINED_CONTROL" in client and "SERVICE_CHANGE_CONFIG" not in client and "SERVICE_START" not in client and "SERVICE_STOP" not in client) +check("installer_service_acl_interactive_users_minimum", "(A;;LCCR;;;IU)" in iss and "sdset" in iss and "(A;;LCCR;;;AU)" not in iss) +check("installer_service_protected_root", "{commonpf}\\DirectInternetMethod\\Privileged" in iss) +check("single_registered_Windows_install_and_preserve_user_evidence", + 'AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}' in iss + and "UsePreviousAppDir=yes" in iss and "UninstallLogMode=overwrite" in iss + and "DIMExistingAppPathConflicts()" in iss + and "RegQueryStringValue(HKLM, Key, 'InstallLocation', OldDir)" in iss + and r'Type: filesandordirs; Name: "{app}\evidence"' not in iss + and r'Type: filesandordirs; Name: "{app}\bin"' not in iss + and r'Type: filesandordirs; Name: "{commonpf}\DirectInternetMethod\Privileged"' not in iss + and r'Type: dirifempty; Name: "{commonpf}\DirectInternetMethod\Privileged"' in iss) +check("owned_update_cache_never_duplicates_versioned_installers", + all(x in svc for x in ("OwnedUpdateInstallerPath", "DirectInternetMethod-update-stage.exe", + "Previous update is pending" if False else "Prior update still needs verification", + "File.Move(partPath, finalPath);", + "update-incomplete-preserved", + "update-stage-file-locked-preserving-single-pending-slot")) + and "File.Move(partPath, finalPath, true)" not in svc) + +check("legacy_provider_rollback_explicit_only_and_exact_SHA", + all(k in legacy_provider_guard for k in ( + "PUBLISHED_STABLE_151_INSTALLER_SHA_CHANGED", "PRESERVE_CUSTOM_PROVIDER_BEFORE_LEGACY_151_REINSTALL", + "PASS_LEGACY_151_PROVIDER_RESTORED_SHA", "FOREIGN_POST_STAGE_PROVIDER_EDIT_PRESERVED", + "PENDING_SLOT_EXISTS_NO_BLIND_REPLAY", "RESTORED_PROVIDER_SHA_FAILED", + "LEGACY_151_BINARY_POSTINSTALL_SHA_NOT_ACCEPTED")) + and "Remove-Item -Recurse" not in legacy_provider_guard + and 'Source: "..\\app\\SafeLegacyProviderRollback.ps1"; DestDir: "{commonpf}\\DirectInternetMethod\\Privileged\\app"; Flags: ignoreversion' in iss + and ("privileged","app/SafeLegacyProviderRollback.ps1") in + {(v["scope"],v["file"]) for v in manifest["files"]}) +check("legacy_151_rollback_preserves_custom_bin_tree", + all(x in legacy_provider_guard for x in ["binFiles","BIN_RESTORATION_SHA_FAILED","FOREIGN_BIN_EDIT_PRESERVED","BIN_SNAPSHOT_UNKNOWN_CONTENT","BIN_STAGED_FILE_CHANGED","FOREIGN_STAGE_SYMLINK"]) + and "Remove-Item -Recurse" not in legacy_provider_guard) +check("inno_preserves_custom_router_gateway_provider_on_upgrade", + 'Source: "..\\..\\router_gateway\\providers.json"; DestDir: "{app}\\app\\router_gateway"; Flags: ignoreversion onlyifdoesntexist' in iss) +check("installer_admin_only_install", "PrivilegesRequired=admin" in iss) +check("installer_service_recovery", 'failure "' in iss and "failureflag" in iss) +check("installer_preserves_pending_update_until_service_SHA_verifies", + "CompletePendingDirectUpdate" in iss and "pending-update.json" in iss + and "new service completes SHA-checked cleanup" in iss + and r"PendingPath := ExpandConstant('{commonappdata}\DirectInternetMethod\pending-update.json')" in iss + and "DeleteFile(PendingPath)" not in iss + and '"action":"update","phase":"done"' not in iss) +check("installer_ctrld_owned_demand_service", "CtrldServiceName = 'ctrld'" in iss and "InstallOrUpdateCtrldService" in iss and "start= demand" in iss and "CurrentControlSet\\Services\\" in iss) +check("installer_ctrld_collision_fail_closed", "A different Windows service named ctrld already exists" in iss and "CtrldServiceOwned()" in iss) +check("installer_ctrld_owned_uninstall", "StopCtrldServiceIfOwned" in iss and "delete \"" in iss and "CtrldServiceName" in iss) +check("installer_shortcuts_exe", 'DirectInternetMethod.exe' in iss and 'ControlPanel.cmd' not in "\n".join(x for x in iss.splitlines() if x.startswith("Name:"))) +check("installer_active_guard", "PrepareToInstall" in iss and "DirectDnsDpiHarness" in iss and "state.json" in iss) +check("installer_bundled_pwsh", '..\\vendor\\pwsh\\*' in iss and 'Privileged\\runtime\\pwsh' in iss and "PowerShell7Available" not in iss) +check("installer_uninstall_recovery_gate", "InitializeUninstall" in iss and "Recovery did not complete" in iss) +check("windows_install_registration_guard", all(x in install_registration_guard for x in ("HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall","noSameAppIdPerUserDuplicate","registeredUninstallerPresent","installedReleaseMatches"))) +check("installed_release_verifier_delegates_registration_guard", "windows_install_registration_audit.ps1" in installed_release_verifier and "Where-Object DisplayName" not in installed_release_verifier and "pwsh.exe" in installed_release_verifier) +check("windows_build_shared_exclusive_lock", all(x in payload_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED")) and all(x in installer_builder for x in ("windows-152-build.lock","FileShare]::None","WINDOWS_152_BUILD_LOCKED"))) +check("windows_build_python_portability_failfast", all(x in payload_builder for x in ( + "param([switch]$PreflightOnly)", + "WINDOWS_BUILD_PYTHON_MISSING:", + "WINDOWS_BUILD_VENDOR_PWSH_SHA_INVALID", + "WINDOWS_BUILD_PORTABLE_PYTHON_SHA_INVALID", + "4942B86A6597E5AEE0128DAA00050ED79BC21F6E709A78EB19CBFEB0C2F39AC9", + r"& $PythonExe '.\tests\build_windows_manifest.py'", + r"& $PythonExe '.\tests\windows_contract_audit.py'", +)) and "& py " not in payload_builder and + payload_builder.index("WINDOWS_BUILD_PYTHON_MISSING:") < payload_builder.index("& dotnet publish")) +check("windows_installer_output_stability_guard", all(x in installer_builder for x in ("OUTPUT_STILL_LOCKED_AFTER_ISCC_EXIT","@($Sizes | Select-Object -Unique).Count","OUTPUT_SIZE_UNSTABLE_","outputStableSamples"))) +tracked_strong=subprocess.run(["git","ls-files","--error-unmatch","windows/bin/zapret/strong-override-hosts.txt"],cwd=R,text=True,capture_output=True).returncode==0 +check("windows_strong_override_source_tracked", tracked_strong) +check("windows_hash_pinned_text_eol_policy", all(x in gitattributes for x in ("windows/bin/zapret/hosts.txt text eol=crlf","windows/bin/zapret/adult-fallback-hosts.txt text eol=lf","windows/bin/zapret/strong-override-hosts.txt text eol=crlf"))) +check("optional_signed_diagnostic_fails_closed_but_is_not_public_gate", all(x in signature_gate for x in ( + "Get-AuthenticodeSignature", + "Assert-EligibleSignerForSmartAppControl -Signature $sig", + "if([string]$Signature.Status -ne 'Valid')", + "SELF_SIGNED_SIGNER_NOT_PUBLIC_TRUSTED", + "SMART_APP_CONTROL_RSA_SIGNER_REQUIRED", + "CODE_SIGNING_EKU_REQUIRED", + "PUBLIC_TRUST_CHAIN_UNVERIFIED", + "FAIL_CLOSED","exit 42" +))) +tag_job_source = ci_workflow.split(" windows-release-contract:",1)[1] if ci_workflow.count(" windows-release-contract:")==1 else "" +check("tag_release_invokes_real_unsigned_three_way_sha_and_hosted_rollback", + tag_job_source.count("python -B tests/unsigned_public_release_contract.py --public-tag")==1 + and "if: startsWith(github.ref, 'refs/tags/v')" in tag_job_source + and "GH_TOKEN:" in tag_job_source and "GITHUB_TOKEN:" in tag_job_source + and "actions: read" in tag_job_source + and "windows_distribution_signature_audit.ps1" not in tag_job_source + and "windows_release_draft_asset_stage.ps1" not in tag_job_source + and "continue-on-error: true" not in tag_job_source + and "if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }" in tag_job_source) + +draft_stage_source=(R/"tests"/"windows_release_draft_asset_stage.ps1").read_text(encoding="utf-8-sig") +draft_fixture_source=(R/"tests"/"windows_release_draft_asset_contract.ps1").read_text(encoding="utf-8-sig") +windows_source_section=ci_workflow.split(" windows-source:",1)[1].split(" windows-release-contract:",1)[0] +check("windows_ci_runs_offline_draft_staging_negative_regressions", + windows_source_section.count("pwsh.exe -NoProfile -NonInteractive -File tests/windows_release_draft_asset_contract.ps1")==1 + and "continue-on-error: true" not in windows_source_section) +check("windows_source_ci_runs_unsigned_release_negative_fixtures", + windows_source_section.count("python tests/unsigned_public_release_contract.py --fixtures")==1) +check("unsigned_publication_never_requires_private_signed_draft_staging", + "windows_release_draft_asset_stage.ps1" not in tag_job_source + and "windows_distribution_signature_audit.ps1" not in tag_job_source + and "unsigned_public_release_contract.py --fixtures" in tag_job_source + and "unsigned_public_release_contract.py --public-tag" in tag_job_source + and "continue-on-error: true" not in tag_job_source) +check("unsigned_verifier_checks_remote_sha_and_raw_hosted_receipts", + all(s in (R/"tests"/"unsigned_public_release_contract.py").read_text(encoding="utf-8-sig") for s in ( + "PUBLIC_THREE_WAY_SHA_DISAGREEMENT", "HOSTED_RAW_REPORT_SHA_MISMATCH", + "HOSTED_REPORT_UNRESOLVED_ERRORS", "GITHUB_RUN_NOT_SUCCESS", + "SIGNING_POLICY_MUST_NOT_BE_REQUIRED"))) + +check("draft_release_stager_fails_closed_on_identity_digest_and_rerun", + all(s in draft_stage_source for s in ( + "RELEASE_NOT_PRIVATE_DRAFT","RELEASE_TARGET_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DIGEST_MISMATCH", + "SIGNED_DRAFT_INSTALLER_ASSET_ORIGIN_MISMATCH","UNSAFE_RERUN_STAGING_PATH_OCCUPIED", + "TAG_CHECKOUT_COMMIT_MISMATCH","SIGNED_DRAFT_INSTALLER_DOWNLOADED_SHA_MISMATCH","exit 42"))) +check("draft_release_negative_fixtures_present", + all(s in draft_fixture_source for s in ( + "Deny 'published'","Deny 'tag'","Deny 'commit'","Deny 'duplicate'", + "Deny 'digest'","Deny 'origin'","PASS 10/10"))) + +check("windows_ci_external_audits_fail_fast", ci_workflow.count("if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE }") >= 7) + +files={(x["scope"],x["file"]) for x in manifest["files"]} +release_meta=json.loads(txt("RELEASE.json")) +allowed_status={ + "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), + "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), + "1.5.6":("PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), +} +current=release_meta.get("version") +check("manifest_version", current in allowed_status + and manifest.get("version")==current + and release_meta.get("status")==allowed_status[current][0] + and release_meta.get("previousAccepted",{}).get("version")==allowed_status[current][1]) + +check("manifest_direct_update", manifest.get("directUpdate",{}).get("userUacRequired") is False and "SHA256SUMS.txt" in manifest.get("directUpdate",{}).get("integrity","")) +check("manifest_native_exe", ("user","app/DirectInternetMethod.exe") in files) +check("manifest_router_gateway_data", ("user","app/router_gateway/providers.json") in files) +check("manifest_adult_fallback", ("privileged","bin/zapret/adult-fallback-hosts.txt") in files) +check("manifest_strong_override", ("privileged","bin/zapret/strong-override-hosts.txt") in files) +check("manifest_service", ("privileged","app/DirectInternetMethod.Service.exe") in files) +check("manifest_no_helper", not any(x[1].endswith("Helper.exe") for x in files)) +check("manifest_bundled_pwsh", manifest.get("bundledPowerShellVersion")=="7.6.6" and manifest.get("bundledPowerShellArchiveSha256")=="02FE458BE20493FBDF43F61EA20610B811EE6C738AB1676C61B9CFCD1A33C860" and ("privileged","runtime/pwsh/pwsh.exe") in files) +check("manifest_bundled_pwsh_tree", sum(1 for s,f in files if s=="privileged" and f.startswith("runtime/pwsh/")) >= 650) +check("manifest_no_legacy_launchers", not any(scope=="user" and (f.endswith(".cmd") or f in {"app/ControlPanel.ps1","app/Toggle.ps1"}) for scope,f in files)) + +pin_paths={ +"ctrld.exe":"bin/ctrld/ctrld.exe", +"ctrld.toml":"bin/ctrld/ctrld.toml", +"winws.exe":"bin/zapret/winws.exe", +"WinDivert.dll":"bin/zapret/WinDivert.dll", +"WinDivert64.sys":"bin/zapret/WinDivert64.sys", +"cygwin1.dll":"bin/zapret/cygwin1.dll", +"hosts.txt":"bin/zapret/hosts.txt", +"adult-fallback-hosts.txt":"bin/zapret/adult-fallback-hosts.txt", +"strong-override-hosts.txt":"bin/zapret/strong-override-hosts.txt", +} +start_pins=dict(re.findall(r"'([^']+)'='([0-9A-F]{64})'",start)) +pin_actual={k:hashlib.sha256((W/v).read_bytes()).hexdigest().upper() for k,v in pin_paths.items()} +D["selfIntegrityPins"]={"declared":{k:start_pins.get(k) for k in pin_paths},"actual":pin_actual} +check("start_self_integrity_pins",all(start_pins.get(k)==v for k,v in pin_actual.items())) + +expected={ +"ctrld":"FC966FD7DD5EE850A9709F632789CFB5BBC06C45D903D24B8ECFCE3306B658CD", +"winws":"A14BFF1DF6234EA555D2E0C61B589F0707C0B12D6C9B7EECCDA76012154996E8", +"windivertdll":"C1E060EE19444A259B2162F8AF0F3FE8C4428A1C6F694DCE20DE194AC8D7D9A2", +"windivertsys":"8DA085332782708D8767BCACE5327A6EC7283C17CFB85E40B03CD2323A90DDC2", +"pwsh":"BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285"} +for name,rel in (("ctrld","bin/ctrld/ctrld.exe"),("winws","bin/zapret/winws.exe"),("windivertdll","bin/zapret/WinDivert.dll"),("windivertsys","bin/zapret/WinDivert64.sys"),("pwsh","vendor/pwsh/pwsh.exe")): + h=hashlib.sha256((W/rel).read_bytes()).hexdigest().upper();D["hashes"][name]=h;check("runtime_"+name+"_pin",h==expected[name]) + +thirdparty_notice_hashes={ + "LICENSE.WinDivert.txt":"14A0CB5214D536E4FDAE6AA3F5696F981EEDA106CD026E9794BBA489EE79D628", + "LICENSE.Cygwin.txt":"794433752103CF4BBB4A84A1BDB8FBC150ABB1762704BB35FECC9F7F820BE984", + "LGPL3.Cygwin.txt":"DA7EABB7BAFDF7D3AE5E9F223AA5BDC1EECE45AC569DC21B3B037520B4464768", + "GPL3.Cygwin.txt":"8CEB4B9EE5ADEDDE47B31E975C1D90C73AD27B6B165A1DCD80C7C545EB65B903", +} +def checked_notice_mapping(filename): + src = r"..\bin\zapret" + "\\" + filename + dest = r"{commonpf}\DirectInternetMethod\Privileged\bin\zapret" + line = 'Source: "' + src + '"; DestDir: "' + dest + '"; Flags: ignoreversion notimestamp' + return iss.splitlines().count(line)==1 +def r549_inno_source_timestamp_contract(source): + try: + rows_section=source.split("[Files]\n",1)[1].split("[InstallDelete]",1)[0] + except IndexError: + return False + rows=[row.strip() for row in rows_section.splitlines() + if row.strip().startswith("Source: ")] + if len(rows)!=28 or len(set(rows))!=28: + return False + for row in rows: + if "Flags:" not in row: + return False + flags=row.split("Flags:",1)[1].strip().split() + if flags.count("notimestamp")!=1 or "touch" in flags or "comparetimestamp" in flags: + return False + return True + +# Negative-first source-time fixture: exactly 28 embedded records without +# stored input timestamps. No change to owned uninstall/upgrade file scope. +check("r549_inno_no_source_timestamps", + r549_inno_source_timestamp_contract(iss) + and not r549_inno_source_timestamp_contract(iss.replace(" notimestamp","",1)) + and not r549_inno_source_timestamp_contract( + iss.replace(" notimestamp"," notimestamp touch",1))) + +check("installer_four_upstream_license_notices_byte_pinned", + all( + (W/"bin"/"zapret"/filename).is_file() + and hashlib.sha256((W/"bin"/"zapret"/filename).read_bytes()).hexdigest().upper()==expected + and checked_notice_mapping(filename) + and "windows/bin/zapret/"+filename+" text eol=lf" in gitattributes + for filename,expected in thirdparty_notice_hashes.items() + )) + +print(json.dumps(D,indent=2)) +sys.exit(0 if D["status"]=="PASS" else 20) From 44e8d9b419c852293301fd818e4742bd3f8cf287 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:26:38 +0330 Subject: [PATCH 177/188] chore(windows): preserve LF formatting in service source --- windows/service/Program.cs | 1334 ++++++++++++++++++------------------ 1 file changed, 667 insertions(+), 667 deletions(-) diff --git a/windows/service/Program.cs b/windows/service/Program.cs index 34d1e272..00c5548f 100644 --- a/windows/service/Program.cs +++ b/windows/service/Program.cs @@ -1,667 +1,667 @@ -using System.Diagnostics; -using System.Net.Http; -using System.Reflection; -using Microsoft.Win32; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; - -namespace DirectInternetMethod.Service; - -internal static class Program -{ - const string ServiceName = "DirectInternetMethodSvc"; - const uint SERVICE_WIN32_OWN_PROCESS = 0x10; - const uint SERVICE_START_PENDING = 0x2; - const uint SERVICE_STOP_PENDING = 0x3; - const uint SERVICE_RUNNING = 0x4; - const uint SERVICE_STOPPED = 0x1; - const uint SERVICE_ACCEPT_STOP = 0x1; - const uint SERVICE_ACCEPT_SHUTDOWN = 0x4; - const uint SERVICE_CONTROL_STOP = 0x1; - const uint SERVICE_CONTROL_SHUTDOWN = 0x5; - const uint CTRL_START = 128; - const uint CTRL_STOP = 129; - const uint CTRL_RECOVERY = 130; - const uint CTRL_UPDATE = 131; - const string LatestReleaseApi = "https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest"; - - static IntPtr _statusHandle; - static readonly ManualResetEventSlim StopEvent = new(false); - static readonly SemaphoreSlim ActionGate = new(1, 1); - static ServiceMainDelegate? _serviceMain; - static HandlerExDelegate? _handler; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - struct SERVICE_TABLE_ENTRY - { - [MarshalAs(UnmanagedType.LPWStr)] public string? lpServiceName; - public ServiceMainDelegate? lpServiceProc; - } - - [StructLayout(LayoutKind.Sequential)] - struct SERVICE_STATUS - { - public uint dwServiceType; - public uint dwCurrentState; - public uint dwControlsAccepted; - public uint dwWin32ExitCode; - public uint dwServiceSpecificExitCode; - public uint dwCheckPoint; - public uint dwWaitHint; - } - - delegate void ServiceMainDelegate(int argc, IntPtr argv); - delegate uint HandlerExDelegate(uint control, uint eventType, IntPtr eventData, IntPtr context); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool StartServiceCtrlDispatcher([In] SERVICE_TABLE_ENTRY[] table); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern IntPtr RegisterServiceCtrlHandlerEx(string serviceName, HandlerExDelegate handler, IntPtr context); - - [DllImport("advapi32.dll", SetLastError = true)] - static extern bool SetServiceStatus(IntPtr serviceStatusHandle, ref SERVICE_STATUS status); - - static int Main(string[] args) - { - if (args.Length == 1 && args[0].Equals("--console-selftest", StringComparison.OrdinalIgnoreCase)) - { - return SelfTest() == "PASS" ? 0 : 20; - } - - _serviceMain = ServiceMain; - _handler = Handler; - var table = new[] - { - new SERVICE_TABLE_ENTRY { lpServiceName = ServiceName, lpServiceProc = _serviceMain }, - new SERVICE_TABLE_ENTRY { lpServiceName = null, lpServiceProc = null } - }; - return StartServiceCtrlDispatcher(table) ? 0 : Marshal.GetLastWin32Error(); - } - - static void ServiceMain(int argc, IntPtr argv) - { - _statusHandle = RegisterServiceCtrlHandlerEx(ServiceName, _handler!, IntPtr.Zero); - if (_statusHandle == IntPtr.Zero) return; - - SetState(SERVICE_START_PENDING, 0, 3000); - Directory.CreateDirectory(RuntimeDir); - Log("service-start"); - SetState(SERVICE_RUNNING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, 0); - // Post-upgrade cleanup must happen AFTER the new service has started. - // Inno Setup is not authorized to erase its own pending evidence. - _ = Task.Run(FinalizePendingAfterServiceRestartAsync); - StopEvent.Wait(); - SetState(SERVICE_STOP_PENDING, 0, 3000); - Log("service-stop"); - SetState(SERVICE_STOPPED, 0, 0); - } - - static uint Handler(uint control, uint eventType, IntPtr eventData, IntPtr context) - { - switch (control) - { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - StopEvent.Set(); - return 0; - case CTRL_START: - _ = Task.Run(() => RunActionAsync("Start-Direct.ps1", "start")); - return 0; - case CTRL_STOP: - _ = Task.Run(() => RunActionAsync("Stop-Direct.ps1", "stop")); - return 0; - case CTRL_RECOVERY: - _ = Task.Run(() => RunActionAsync("Recovery.ps1", "recovery")); - return 0; - case CTRL_UPDATE: - _ = Task.Run(RunUpdateAsync); - return 0; - default: - return 0; - } - } - - static string RuntimeDir => Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), - "DirectInternetMethod"); - - static string LogPath => Path.Combine(RuntimeDir, "service.log"); - static string ActionStatusPath => Path.Combine(RuntimeDir, "action-status.json"); - - static string PwshPath => - Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "runtime", "pwsh", "pwsh.exe")); - - - static string UpdatesDir => Path.Combine(RuntimeDir, "updates"); - static string PendingUpdatePath => Path.Combine(RuntimeDir, "pending-update.json"); - // Only one owned staging installer may exist for an active update. - static string OwnedUpdateInstallerPath => Path.Combine(UpdatesDir, "DirectInternetMethod-update-stage.exe"); - static string NetworkStatePath => Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), - "DirectDnsDpiHarness", "state.json"); - - static readonly HttpClient UpdateHttp = CreateUpdateHttp(); - - sealed record UpdatePackage( - Version Version, - string Tag, - string InstallerName, - string InstallerUrl, - string ChecksumsUrl, - string ApiSha256); - - static HttpClient CreateUpdateHttp() - { - var client = new HttpClient { Timeout = TimeSpan.FromSeconds(45) }; - client.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod.Service/1.2"); - client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json"); - return client; - } - - static Version CurrentVersion => - Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0, 0); - - static async Task GetLatestUpdateAsync(CancellationToken ct) - { - using var response = await UpdateHttp.GetAsync(LatestReleaseApi, ct); - response.EnsureSuccessStatusCode(); - using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync(ct)); - var root = doc.RootElement; - if (root.TryGetProperty("draft", out var draft) && draft.GetBoolean()) - throw new InvalidOperationException("Latest GitHub release is a draft."); - if (root.TryGetProperty("prerelease", out var pre) && pre.GetBoolean()) - throw new InvalidOperationException("Latest GitHub release is a prerelease."); - - var tag = root.GetProperty("tag_name").GetString() ?? ""; - var clean = tag.Trim().TrimStart('v', 'V'); - if (!Version.TryParse(clean, out var latest)) - throw new InvalidOperationException("Latest GitHub release has an invalid version tag."); - if (latest <= CurrentVersion) return null; - - var installers = new List<(string Name, string Url, string Digest)>(); - var sums = new List(); - foreach (var asset in root.GetProperty("assets").EnumerateArray()) - { - var name = asset.GetProperty("name").GetString() ?? ""; - var url = asset.GetProperty("browser_download_url").GetString() ?? ""; - if (name.EndsWith("_Windows_Setup.exe", StringComparison.OrdinalIgnoreCase)) - { - var digest = asset.TryGetProperty("digest", out var de) ? de.GetString() ?? "" : ""; - installers.Add((name, url, digest)); - } - else if (name.Equals("SHA256SUMS.txt", StringComparison.OrdinalIgnoreCase)) - sums.Add(url); - } - - if (installers.Count != 1 || sums.Count != 1) - throw new InvalidOperationException("Release must contain exactly one Windows installer and one SHA256SUMS.txt."); - - var apiDigest = installers[0].Digest; - if (!apiDigest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase) || apiDigest.Length != 71) - throw new InvalidOperationException("GitHub release asset is missing a valid SHA-256 digest."); - - return new UpdatePackage(latest, tag, installers[0].Name, installers[0].Url, sums[0], apiDigest[7..]); - } - - static string ParseExpectedSha256(string sumsText, string installerName) - { - var matches = new List(); - foreach (var raw in sumsText.Split('\n')) - { - var line = raw.Trim(); - if (line.Length < 64) continue; - var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length >= 2 && - parts[^1].TrimStart('*').Equals(installerName, StringComparison.OrdinalIgnoreCase)) - matches.Add(parts[0].Trim()); - } - if (matches.Count != 1 || matches[0].Length != 64 || matches[0].Any(c => !Uri.IsHexDigit(c))) - throw new InvalidOperationException("SHA256SUMS.txt must contain exactly one valid checksum for the Windows installer."); - return matches[0].ToUpperInvariant(); - } - - static async Task Sha256FileAsync(string path, CancellationToken ct) - { - await using var stream = File.OpenRead(path); - return Convert.ToHexString(await SHA256.HashDataAsync(stream, ct)); - } - - static void WriteJsonAtomic(string path, object payload) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - var temp = path + ".tmp"; - File.WriteAllText(temp, JsonSerializer.Serialize(payload), new UTF8Encoding(false)); - File.Move(temp, path, true); - } - - - static string GetRegisteredInstallDirectory() - { - const string keyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1"; - using var key = Registry.LocalMachine.OpenSubKey(keyPath, writable: false) - ?? throw new InvalidOperationException("Registered all-users installation was not found."); - var raw = key.GetValue("InstallLocation") as string; - if (string.IsNullOrWhiteSpace(raw)) - throw new InvalidOperationException("Registered installation directory is missing."); - - var full = Path.GetFullPath(raw.Trim()); - if (!Path.IsPathFullyQualified(full)) - throw new InvalidOperationException("Registered installation directory is not absolute."); - var appExe = Path.Combine(full, "app", "DirectInternetMethod.exe"); - var manifest = Path.Combine(full, "manifest.json"); - if (!File.Exists(appExe) || !File.Exists(manifest)) - throw new InvalidOperationException("Registered installation directory does not contain the expected application payload."); - return full.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - } - - static async Task DownloadWithSystemCurlAsync(string url, string destination, CancellationToken ct) - { - // Resolve the updater helper exclusively from the Windows-owned system directory. - // Never let caller PATH or a resolved folder string choose the executable. - var systemFolder = Environment.SystemDirectory; - if (string.IsNullOrWhiteSpace(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) - throw new InvalidOperationException("Trusted Windows system directory unavailable."); - var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); - if (!File.Exists(curl)) - throw new FileNotFoundException("Windows curl.exe is unavailable.", curl); - - var psi = new ProcessStartInfo(curl) - { - UseShellExecute = false, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden - }; - foreach (var arg in new[] - { - "-L", "--fail", "--silent", "--show-error", - "--connect-timeout", "15", "--max-time", "210", - "--retry", "2", "--retry-delay", "1", - "-o", destination, url - }) - psi.ArgumentList.Add(arg); - - using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start Windows curl.exe."); - using var reg = ct.Register(() => - { - try { if (!p.HasExited) p.Kill(entireProcessTree: true); } catch { } - }); - await p.WaitForExitAsync(ct); - if (p.ExitCode != 0) - throw new HttpRequestException($"curl.exe failed downloading release asset with exit code {p.ExitCode}."); - if (!File.Exists(destination) || new FileInfo(destination).Length == 0) - throw new IOException("Downloaded release asset is empty."); - } - - static async Task RunUpdateAsync() - { - const string action = "update"; - if (!await ActionGate.WaitAsync(0)) - { - Log("action-busy:update"); - WriteActionStatus(action, "busy", -1, "Another action is already running."); - return; - } - - try - { - if (File.Exists(NetworkStatePath)) - { - WriteActionStatus(action, "error", -10, "Direct Method must be OFF before update."); - return; - } - if (File.Exists(PendingUpdatePath)) - { - FinalizePendingUpdate(); - if (File.Exists(PendingUpdatePath)) - throw new IOException("Prior update still needs verification or cleanup; refusing a second installer."); - } - - Log("action-begin:update"); - WriteActionStatus(action, "running", null, "Checking official GitHub release."); - using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(4)); - var package = await GetLatestUpdateAsync(timeout.Token); - if (package is null) - { - Log("action-end:update:already-current"); - WriteActionStatus(action, "done", 0, "Already up to date."); - return; - } - - Directory.CreateDirectory(UpdatesDir); - var sumsText = await UpdateHttp.GetStringAsync(package.ChecksumsUrl, timeout.Token); - var expected = ParseExpectedSha256(sumsText, package.InstallerName); - if (!expected.Equals(package.ApiSha256, StringComparison.OrdinalIgnoreCase)) - throw new CryptographicException("GitHub asset digest does not match SHA256SUMS.txt."); - - var finalPath = OwnedUpdateInstallerPath; - var partPath = finalPath + ".part"; - if (File.Exists(finalPath) || File.Exists(partPath)) - throw new IOException("Single owned updater staging slot is occupied; no replacement permitted."); - - await DownloadWithSystemCurlAsync(package.InstallerUrl, partPath, timeout.Token); - - var actual = await Sha256FileAsync(partPath, timeout.Token); - if (!actual.Equals(expected, StringComparison.OrdinalIgnoreCase)) - { - File.Delete(partPath); - throw new CryptographicException("Downloaded installer failed SHA-256 verification."); - } - File.Move(partPath, finalPath); // Never overwrite a pending/foreign installer. - - var installDir = GetRegisteredInstallDirectory(); - - WriteJsonAtomic(PendingUpdatePath, new - { - schema = 1, - targetVersion = package.Version.ToString(3), - tag = package.Tag, - installer = finalPath, - sha256 = actual, - installDir, - preparedUtc = DateTimeOffset.UtcNow.ToString("O") - }); - - var logPath = Path.Combine(UpdatesDir, "install-" + package.Version.ToString(3) + ".log"); - var psi = new ProcessStartInfo(finalPath) - { - UseShellExecute = false, - CreateNoWindow = true, - WorkingDirectory = UpdatesDir - }; - psi.ArgumentList.Add("/VERYSILENT"); - psi.ArgumentList.Add("/SUPPRESSMSGBOXES"); - psi.ArgumentList.Add("/NORESTART"); - psi.ArgumentList.Add("/NORESTARTAPPLICATIONS"); - psi.ArgumentList.Add("/CLOSEAPPLICATIONS"); - psi.ArgumentList.Add("/SP-"); - psi.ArgumentList.Add("/DIR=" + installDir); - psi.ArgumentList.Add("/LOG=" + logPath); - - var installer = Process.Start(psi); - if (installer is null) - throw new InvalidOperationException("Unable to launch the verified installer."); - - Log($"update-handoff:{package.Tag}:pid={installer.Id}:sha256={actual}"); - WriteActionStatus(action, "handoff", 0, $"Verified {package.Tag}; installing directly without UAC."); - } - catch (Exception ex) - { - Log($"action-exception:update:{ex.GetType().Name}:{Compact(ex.Message)}"); - WriteActionStatus(action, "error", -1, ex.GetType().Name + ": " + ex.Message); - } - finally - { - ActionGate.Release(); - } - } - - static async Task FinalizePendingAfterServiceRestartAsync() - { - // The Setup executable may be locked until Inno closes. Recheck only - // the one SHA-pinned owned installer, never delete a foreign path. - for (var attempt = 0; attempt < 48 && !StopEvent.IsSet; attempt++) - { - if (!File.Exists(PendingUpdatePath)) return; - if (await ActionGate.WaitAsync(0)) - { - try - { - FinalizePendingUpdate(); - if (!File.Exists(PendingUpdatePath)) return; - } - finally { ActionGate.Release(); } - } - await Task.Delay(TimeSpan.FromSeconds(5)); - } - if (File.Exists(PendingUpdatePath)) - Log("pending-update-finalization-deferred-no-overwrite"); - } - - static void FinalizePendingUpdate() - { - try - { - if (!File.Exists(PendingUpdatePath)) return; - using var doc = JsonDocument.Parse(File.ReadAllText(PendingUpdatePath)); - var targetText = doc.RootElement.GetProperty("targetVersion").GetString() ?? ""; - if (doc.RootElement.GetProperty("schema").GetInt32() != 1 || - !Version.TryParse(targetText, out var target)) - throw new InvalidDataException("Pending update schema or version invalid; preserve record."); - var installed = CurrentVersion; - if (installed >= target) - { - // Setup can still be running from this EXE while the service restarts. - // Keep one pending record until the verified file can be removed. - var stage = doc.RootElement.GetProperty("installer").GetString() ?? ""; - var expectedSha = doc.RootElement.GetProperty("sha256").GetString() ?? ""; - // v1.5.1's official privileged updater used a versioned EXE name, - // while v1.5.2 uses one fixed owned update-stage.exe slot. Accept - // ONLY these two exact, non-symlink paths. A legacy name must - // match the pending target version AND the original release tag. - // Never accept an arbitrary path in the pending JSON, even when - // that file happens to have the same checksum. - var versionText = target.ToString(3); - var oldOfficialName = "DirectInternetMethod_" + versionText + "_Windows_Setup.exe"; - var oldOfficialPath = Path.Combine(UpdatesDir, oldOfficialName); - var tag = doc.RootElement.GetProperty("tag").GetString() ?? ""; - var canonicalStage = Path.GetFullPath(stage); - var fixedOwnedStage = canonicalStage.Equals(Path.GetFullPath(OwnedUpdateInstallerPath), - StringComparison.OrdinalIgnoreCase); - var pinnedLegacyStage = target >= new Version(1, 5, 2) - && tag.Equals("v" + versionText, StringComparison.OrdinalIgnoreCase) - && canonicalStage.Equals(Path.GetFullPath(oldOfficialPath), - StringComparison.OrdinalIgnoreCase); - if (!Path.IsPathFullyQualified(stage) || - (!fixedOwnedStage && !pinnedLegacyStage) || - expectedSha.Length != 64 || !expectedSha.All(Uri.IsHexDigit)) - throw new CryptographicException("Foreign staging path or invalid pending checksum."); - if (File.Exists(stage)) - { - if ((File.GetAttributes(stage) & FileAttributes.ReparsePoint) != 0 || - (File.GetAttributes(UpdatesDir) & FileAttributes.ReparsePoint) != 0) - throw new CryptographicException("Owned updater staging path is a reparse point."); - string observed; - using (var input = File.OpenRead(stage)) - observed = Convert.ToHexString(SHA256.HashData(input)); - if (!observed.Equals(expectedSha, StringComparison.OrdinalIgnoreCase)) - throw new CryptographicException("Owned staged installer changed; preserving for review."); - try { File.Delete(stage); } - catch (IOException) - { - WriteActionStatus("update", "running", null, "Installed; waiting for owned installer file to unlock."); - Log("update-stage-file-locked-preserving-single-pending-slot"); - return; - } - } - File.Delete(PendingUpdatePath); - WriteActionStatus("update", "done", 0, $"Updated to v{installed.ToString(3)}."); - Log($"update-complete:{installed.ToString(3)}:no-cache-duplicate"); - } - else - { - WriteActionStatus("update", "error", -3, $"Update target v{target.ToString(3)} was not installed; evidence preserved."); - Log($"update-incomplete-preserved:target={target.ToString(3)}:current={installed.ToString(3)}"); - } - } - catch (Exception ex) - { - Log($"update-finalize-failed:{ex.GetType().Name}:{Compact(ex.Message)}"); - } - } - - static async Task RunActionAsync(string scriptName, string actionName) - { - if (!await ActionGate.WaitAsync(0)) - { - Log($"action-busy:{actionName}"); - WriteActionStatus(actionName, "busy", -1, "Another action is already running."); - return; - } - - try - { - var script = Path.Combine(AppContext.BaseDirectory, scriptName); - if (!File.Exists(script)) - { - Log($"action-missing:{actionName}:{script}"); - WriteActionStatus(actionName, "error", -1, "Privileged lifecycle script is missing."); - return; - } - if (!File.Exists(PwshPath)) - { - Log($"runtime-missing:{actionName}:{PwshPath}"); - WriteActionStatus(actionName, "error", -1, "Bundled PowerShell runtime is missing."); - return; - } - - var psi = new ProcessStartInfo( - PwshPath, - $"-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"{script}\"") - { - UseShellExecute = false, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - WorkingDirectory = AppContext.BaseDirectory, - // Fail-closed service errors are read, but raw output is never exposed. - RedirectStandardError = true - }; - - Log($"action-begin:{actionName}"); - WriteActionStatus(actionName, "running", null, ""); - using var p = Process.Start(psi); - if (p is null) - { - Log($"action-start-failed:{actionName}"); - WriteActionStatus(actionName, "error", -1, "Unable to start PowerShell backend."); - return; - } - - // Drain stderr concurrently; retain at most 8 KiB and never persist it. - var stderrTask = ReadActionErrorTailAsync(p.StandardError); - if (!p.WaitForExit(120000)) - { - try { p.Kill(entireProcessTree: true); } catch { } - try { p.WaitForExit(5000); } catch { } - Log($"action-timeout:{actionName}"); - WriteActionStatus(actionName, "error", -2, "Privileged action exceeded the 120 second safety timeout and was terminated."); - return; - } - var stderr = await stderrTask.WaitAsync(TimeSpan.FromSeconds(3)); - Log($"action-end:{actionName}:exit={p.ExitCode}"); - var message = p.ExitCode == 0 - ? "Action completed." - : DescribeActionFailure(p.ExitCode, stderr); - WriteActionStatus(actionName, p.ExitCode == 0 ? "done" : "error", p.ExitCode, message); - } - catch (Exception ex) - { - Log($"action-exception:{actionName}:{ex.GetType().Name}:{Compact(ex.Message)}"); - WriteActionStatus(actionName, "error", -1, ex.GetType().Name + ": " + ex.Message); - } - finally - { - ActionGate.Release(); - } - } - - static async Task ReadActionErrorTailAsync(StreamReader reader) - { - var tail = new StringBuilder(); - var buffer = new char[2048]; - int count; - while ((count = await reader.ReadAsync(buffer.AsMemory())) != 0) - { - tail.Append(buffer, 0, count); - if (tail.Length > 8192) - tail.Remove(0, tail.Length - 8192); - } - return tail.ToString(); - } - - static string DescribeActionFailure(int exitCode, string standardError) - { - // Never disclose arbitrary stderr: it can include paths, hosts or secrets. - if (standardError.Contains("DEFAULT_ROUTE_NOT_PHYSICAL", StringComparison.Ordinal)) - return "A VPN or virtual adapter is the preferred default route. Direct Internet Method kept the routes unchanged. Select a physical default route before starting."; - if (standardError.Contains("REFUSE_BROAD_TUNNEL_ROUTE", StringComparison.Ordinal)) - return "A broad VPN route is active. Direct Internet Method refused to change the current network configuration."; - if (standardError.Contains("HASH_MISMATCH_", StringComparison.Ordinal)) - return "Privileged runtime integrity check failed. Repair the verified installation before starting."; - return $"Privileged action failed with exit code {exitCode}. See lifecycle evidence/service log."; - } - - static void WriteActionStatus(string action, string phase, int? exitCode, string message) - { - try - { - Directory.CreateDirectory(RuntimeDir); - var payload = System.Text.Json.JsonSerializer.Serialize(new - { - schema = 1, - action, - phase, - exitCode, - message = Compact(message), - updatedUtc = DateTimeOffset.UtcNow.ToString("O") - }); - var temp = ActionStatusPath + ".tmp"; - File.WriteAllText(temp, payload, new UTF8Encoding(false)); - File.Move(temp, ActionStatusPath, true); - } - catch (Exception ex) - { - Log($"action-status-write-failed:{action}:{ex.GetType().Name}:{Compact(ex.Message)}"); - } - } - - static string Compact(string value) - { - if (string.IsNullOrWhiteSpace(value)) return "-"; - var s = value.Replace("\r", " ").Replace("\n", " "); - return s.Length <= 600 ? s : s[..600]; - } - - static void Log(string message) - { - try - { - Directory.CreateDirectory(RuntimeDir); - File.AppendAllText(LogPath, $"{DateTimeOffset.Now:O} {message}{Environment.NewLine}", Encoding.UTF8); - } - catch { } - } - - static void SetState(uint state, uint accepted, uint waitHint) - { - var s = new SERVICE_STATUS - { - dwServiceType = SERVICE_WIN32_OWN_PROCESS, - dwCurrentState = state, - dwControlsAccepted = accepted, - dwWin32ExitCode = 0, - dwServiceSpecificExitCode = 0, - dwCheckPoint = 0, - dwWaitHint = waitHint - }; - SetServiceStatus(_statusHandle, ref s); - } - - static string SelfTest() - { - var required = new[] { "Start-Direct.ps1", "Stop-Direct.ps1", "Recovery.ps1" }; - var missing = required.Where(x => !File.Exists(Path.Combine(AppContext.BaseDirectory, x))).ToArray(); - if (missing.Length != 0) return "MISSING:" + string.Join(",", missing); - if (!DescribeActionFailure(1, "START_FAIL_DEFAULT_ROUTE_NOT_PHYSICAL").Contains("VPN or virtual adapter", StringComparison.Ordinal)) - return "MISSING_ROUTE_GUARD_MESSAGE"; - if (!DescribeActionFailure(1, "REFUSE_BROAD_TUNNEL_ROUTE").Contains("broad VPN route", StringComparison.Ordinal)) - return "MISSING_BROAD_ROUTE_GUARD_MESSAGE"; - if (!DescribeActionFailure(1, "HASH_MISMATCH_ctrld.exe").Contains("integrity check failed", StringComparison.Ordinal)) - return "MISSING_RUNTIME_INTEGRITY_MESSAGE"; - if (DescribeActionFailure(1, "SECRET_SAMPLE private path").Contains("SECRET_SAMPLE", StringComparison.Ordinal)) - return "RAW_STDERR_LEAK"; - return "PASS"; - } -} +using System.Diagnostics; +using System.Net.Http; +using System.Reflection; +using Microsoft.Win32; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace DirectInternetMethod.Service; + +internal static class Program +{ + const string ServiceName = "DirectInternetMethodSvc"; + const uint SERVICE_WIN32_OWN_PROCESS = 0x10; + const uint SERVICE_START_PENDING = 0x2; + const uint SERVICE_STOP_PENDING = 0x3; + const uint SERVICE_RUNNING = 0x4; + const uint SERVICE_STOPPED = 0x1; + const uint SERVICE_ACCEPT_STOP = 0x1; + const uint SERVICE_ACCEPT_SHUTDOWN = 0x4; + const uint SERVICE_CONTROL_STOP = 0x1; + const uint SERVICE_CONTROL_SHUTDOWN = 0x5; + const uint CTRL_START = 128; + const uint CTRL_STOP = 129; + const uint CTRL_RECOVERY = 130; + const uint CTRL_UPDATE = 131; + const string LatestReleaseApi = "https://api.github.com/repos/GOD13emad/DirectInternetMethod/releases/latest"; + + static IntPtr _statusHandle; + static readonly ManualResetEventSlim StopEvent = new(false); + static readonly SemaphoreSlim ActionGate = new(1, 1); + static ServiceMainDelegate? _serviceMain; + static HandlerExDelegate? _handler; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct SERVICE_TABLE_ENTRY + { + [MarshalAs(UnmanagedType.LPWStr)] public string? lpServiceName; + public ServiceMainDelegate? lpServiceProc; + } + + [StructLayout(LayoutKind.Sequential)] + struct SERVICE_STATUS + { + public uint dwServiceType; + public uint dwCurrentState; + public uint dwControlsAccepted; + public uint dwWin32ExitCode; + public uint dwServiceSpecificExitCode; + public uint dwCheckPoint; + public uint dwWaitHint; + } + + delegate void ServiceMainDelegate(int argc, IntPtr argv); + delegate uint HandlerExDelegate(uint control, uint eventType, IntPtr eventData, IntPtr context); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool StartServiceCtrlDispatcher([In] SERVICE_TABLE_ENTRY[] table); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern IntPtr RegisterServiceCtrlHandlerEx(string serviceName, HandlerExDelegate handler, IntPtr context); + + [DllImport("advapi32.dll", SetLastError = true)] + static extern bool SetServiceStatus(IntPtr serviceStatusHandle, ref SERVICE_STATUS status); + + static int Main(string[] args) + { + if (args.Length == 1 && args[0].Equals("--console-selftest", StringComparison.OrdinalIgnoreCase)) + { + return SelfTest() == "PASS" ? 0 : 20; + } + + _serviceMain = ServiceMain; + _handler = Handler; + var table = new[] + { + new SERVICE_TABLE_ENTRY { lpServiceName = ServiceName, lpServiceProc = _serviceMain }, + new SERVICE_TABLE_ENTRY { lpServiceName = null, lpServiceProc = null } + }; + return StartServiceCtrlDispatcher(table) ? 0 : Marshal.GetLastWin32Error(); + } + + static void ServiceMain(int argc, IntPtr argv) + { + _statusHandle = RegisterServiceCtrlHandlerEx(ServiceName, _handler!, IntPtr.Zero); + if (_statusHandle == IntPtr.Zero) return; + + SetState(SERVICE_START_PENDING, 0, 3000); + Directory.CreateDirectory(RuntimeDir); + Log("service-start"); + SetState(SERVICE_RUNNING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, 0); + // Post-upgrade cleanup must happen AFTER the new service has started. + // Inno Setup is not authorized to erase its own pending evidence. + _ = Task.Run(FinalizePendingAfterServiceRestartAsync); + StopEvent.Wait(); + SetState(SERVICE_STOP_PENDING, 0, 3000); + Log("service-stop"); + SetState(SERVICE_STOPPED, 0, 0); + } + + static uint Handler(uint control, uint eventType, IntPtr eventData, IntPtr context) + { + switch (control) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + StopEvent.Set(); + return 0; + case CTRL_START: + _ = Task.Run(() => RunActionAsync("Start-Direct.ps1", "start")); + return 0; + case CTRL_STOP: + _ = Task.Run(() => RunActionAsync("Stop-Direct.ps1", "stop")); + return 0; + case CTRL_RECOVERY: + _ = Task.Run(() => RunActionAsync("Recovery.ps1", "recovery")); + return 0; + case CTRL_UPDATE: + _ = Task.Run(RunUpdateAsync); + return 0; + default: + return 0; + } + } + + static string RuntimeDir => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "DirectInternetMethod"); + + static string LogPath => Path.Combine(RuntimeDir, "service.log"); + static string ActionStatusPath => Path.Combine(RuntimeDir, "action-status.json"); + + static string PwshPath => + Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "runtime", "pwsh", "pwsh.exe")); + + + static string UpdatesDir => Path.Combine(RuntimeDir, "updates"); + static string PendingUpdatePath => Path.Combine(RuntimeDir, "pending-update.json"); + // Only one owned staging installer may exist for an active update. + static string OwnedUpdateInstallerPath => Path.Combine(UpdatesDir, "DirectInternetMethod-update-stage.exe"); + static string NetworkStatePath => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "DirectDnsDpiHarness", "state.json"); + + static readonly HttpClient UpdateHttp = CreateUpdateHttp(); + + sealed record UpdatePackage( + Version Version, + string Tag, + string InstallerName, + string InstallerUrl, + string ChecksumsUrl, + string ApiSha256); + + static HttpClient CreateUpdateHttp() + { + var client = new HttpClient { Timeout = TimeSpan.FromSeconds(45) }; + client.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod.Service/1.2"); + client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json"); + return client; + } + + static Version CurrentVersion => + Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0, 0); + + static async Task GetLatestUpdateAsync(CancellationToken ct) + { + using var response = await UpdateHttp.GetAsync(LatestReleaseApi, ct); + response.EnsureSuccessStatusCode(); + using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync(ct)); + var root = doc.RootElement; + if (root.TryGetProperty("draft", out var draft) && draft.GetBoolean()) + throw new InvalidOperationException("Latest GitHub release is a draft."); + if (root.TryGetProperty("prerelease", out var pre) && pre.GetBoolean()) + throw new InvalidOperationException("Latest GitHub release is a prerelease."); + + var tag = root.GetProperty("tag_name").GetString() ?? ""; + var clean = tag.Trim().TrimStart('v', 'V'); + if (!Version.TryParse(clean, out var latest)) + throw new InvalidOperationException("Latest GitHub release has an invalid version tag."); + if (latest <= CurrentVersion) return null; + + var installers = new List<(string Name, string Url, string Digest)>(); + var sums = new List(); + foreach (var asset in root.GetProperty("assets").EnumerateArray()) + { + var name = asset.GetProperty("name").GetString() ?? ""; + var url = asset.GetProperty("browser_download_url").GetString() ?? ""; + if (name.EndsWith("_Windows_Setup.exe", StringComparison.OrdinalIgnoreCase)) + { + var digest = asset.TryGetProperty("digest", out var de) ? de.GetString() ?? "" : ""; + installers.Add((name, url, digest)); + } + else if (name.Equals("SHA256SUMS.txt", StringComparison.OrdinalIgnoreCase)) + sums.Add(url); + } + + if (installers.Count != 1 || sums.Count != 1) + throw new InvalidOperationException("Release must contain exactly one Windows installer and one SHA256SUMS.txt."); + + var apiDigest = installers[0].Digest; + if (!apiDigest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase) || apiDigest.Length != 71) + throw new InvalidOperationException("GitHub release asset is missing a valid SHA-256 digest."); + + return new UpdatePackage(latest, tag, installers[0].Name, installers[0].Url, sums[0], apiDigest[7..]); + } + + static string ParseExpectedSha256(string sumsText, string installerName) + { + var matches = new List(); + foreach (var raw in sumsText.Split('\n')) + { + var line = raw.Trim(); + if (line.Length < 64) continue; + var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length >= 2 && + parts[^1].TrimStart('*').Equals(installerName, StringComparison.OrdinalIgnoreCase)) + matches.Add(parts[0].Trim()); + } + if (matches.Count != 1 || matches[0].Length != 64 || matches[0].Any(c => !Uri.IsHexDigit(c))) + throw new InvalidOperationException("SHA256SUMS.txt must contain exactly one valid checksum for the Windows installer."); + return matches[0].ToUpperInvariant(); + } + + static async Task Sha256FileAsync(string path, CancellationToken ct) + { + await using var stream = File.OpenRead(path); + return Convert.ToHexString(await SHA256.HashDataAsync(stream, ct)); + } + + static void WriteJsonAtomic(string path, object payload) + { + Directory.CreateDirectory(Path.GetDirectoryName(path)!); + var temp = path + ".tmp"; + File.WriteAllText(temp, JsonSerializer.Serialize(payload), new UTF8Encoding(false)); + File.Move(temp, path, true); + } + + + static string GetRegisteredInstallDirectory() + { + const string keyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1"; + using var key = Registry.LocalMachine.OpenSubKey(keyPath, writable: false) + ?? throw new InvalidOperationException("Registered all-users installation was not found."); + var raw = key.GetValue("InstallLocation") as string; + if (string.IsNullOrWhiteSpace(raw)) + throw new InvalidOperationException("Registered installation directory is missing."); + + var full = Path.GetFullPath(raw.Trim()); + if (!Path.IsPathFullyQualified(full)) + throw new InvalidOperationException("Registered installation directory is not absolute."); + var appExe = Path.Combine(full, "app", "DirectInternetMethod.exe"); + var manifest = Path.Combine(full, "manifest.json"); + if (!File.Exists(appExe) || !File.Exists(manifest)) + throw new InvalidOperationException("Registered installation directory does not contain the expected application payload."); + return full.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + + static async Task DownloadWithSystemCurlAsync(string url, string destination, CancellationToken ct) + { + // Resolve the updater helper exclusively from the Windows-owned system directory. + // Never let caller PATH or a resolved folder string choose the executable. + var systemFolder = Environment.SystemDirectory; + if (string.IsNullOrWhiteSpace(systemFolder) || !Path.IsPathFullyQualified(systemFolder)) + throw new InvalidOperationException("Trusted Windows system directory unavailable."); + var curl = Path.GetFullPath(Path.Combine(systemFolder, "curl.exe")); + if (!File.Exists(curl)) + throw new FileNotFoundException("Windows curl.exe is unavailable.", curl); + + var psi = new ProcessStartInfo(curl) + { + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + foreach (var arg in new[] + { + "-L", "--fail", "--silent", "--show-error", + "--connect-timeout", "15", "--max-time", "210", + "--retry", "2", "--retry-delay", "1", + "-o", destination, url + }) + psi.ArgumentList.Add(arg); + + using var p = Process.Start(psi) ?? throw new InvalidOperationException("Unable to start Windows curl.exe."); + using var reg = ct.Register(() => + { + try { if (!p.HasExited) p.Kill(entireProcessTree: true); } catch { } + }); + await p.WaitForExitAsync(ct); + if (p.ExitCode != 0) + throw new HttpRequestException($"curl.exe failed downloading release asset with exit code {p.ExitCode}."); + if (!File.Exists(destination) || new FileInfo(destination).Length == 0) + throw new IOException("Downloaded release asset is empty."); + } + + static async Task RunUpdateAsync() + { + const string action = "update"; + if (!await ActionGate.WaitAsync(0)) + { + Log("action-busy:update"); + WriteActionStatus(action, "busy", -1, "Another action is already running."); + return; + } + + try + { + if (File.Exists(NetworkStatePath)) + { + WriteActionStatus(action, "error", -10, "Direct Method must be OFF before update."); + return; + } + if (File.Exists(PendingUpdatePath)) + { + FinalizePendingUpdate(); + if (File.Exists(PendingUpdatePath)) + throw new IOException("Prior update still needs verification or cleanup; refusing a second installer."); + } + + Log("action-begin:update"); + WriteActionStatus(action, "running", null, "Checking official GitHub release."); + using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(4)); + var package = await GetLatestUpdateAsync(timeout.Token); + if (package is null) + { + Log("action-end:update:already-current"); + WriteActionStatus(action, "done", 0, "Already up to date."); + return; + } + + Directory.CreateDirectory(UpdatesDir); + var sumsText = await UpdateHttp.GetStringAsync(package.ChecksumsUrl, timeout.Token); + var expected = ParseExpectedSha256(sumsText, package.InstallerName); + if (!expected.Equals(package.ApiSha256, StringComparison.OrdinalIgnoreCase)) + throw new CryptographicException("GitHub asset digest does not match SHA256SUMS.txt."); + + var finalPath = OwnedUpdateInstallerPath; + var partPath = finalPath + ".part"; + if (File.Exists(finalPath) || File.Exists(partPath)) + throw new IOException("Single owned updater staging slot is occupied; no replacement permitted."); + + await DownloadWithSystemCurlAsync(package.InstallerUrl, partPath, timeout.Token); + + var actual = await Sha256FileAsync(partPath, timeout.Token); + if (!actual.Equals(expected, StringComparison.OrdinalIgnoreCase)) + { + File.Delete(partPath); + throw new CryptographicException("Downloaded installer failed SHA-256 verification."); + } + File.Move(partPath, finalPath); // Never overwrite a pending/foreign installer. + + var installDir = GetRegisteredInstallDirectory(); + + WriteJsonAtomic(PendingUpdatePath, new + { + schema = 1, + targetVersion = package.Version.ToString(3), + tag = package.Tag, + installer = finalPath, + sha256 = actual, + installDir, + preparedUtc = DateTimeOffset.UtcNow.ToString("O") + }); + + var logPath = Path.Combine(UpdatesDir, "install-" + package.Version.ToString(3) + ".log"); + var psi = new ProcessStartInfo(finalPath) + { + UseShellExecute = false, + CreateNoWindow = true, + WorkingDirectory = UpdatesDir + }; + psi.ArgumentList.Add("/VERYSILENT"); + psi.ArgumentList.Add("/SUPPRESSMSGBOXES"); + psi.ArgumentList.Add("/NORESTART"); + psi.ArgumentList.Add("/NORESTARTAPPLICATIONS"); + psi.ArgumentList.Add("/CLOSEAPPLICATIONS"); + psi.ArgumentList.Add("/SP-"); + psi.ArgumentList.Add("/DIR=" + installDir); + psi.ArgumentList.Add("/LOG=" + logPath); + + var installer = Process.Start(psi); + if (installer is null) + throw new InvalidOperationException("Unable to launch the verified installer."); + + Log($"update-handoff:{package.Tag}:pid={installer.Id}:sha256={actual}"); + WriteActionStatus(action, "handoff", 0, $"Verified {package.Tag}; installing directly without UAC."); + } + catch (Exception ex) + { + Log($"action-exception:update:{ex.GetType().Name}:{Compact(ex.Message)}"); + WriteActionStatus(action, "error", -1, ex.GetType().Name + ": " + ex.Message); + } + finally + { + ActionGate.Release(); + } + } + + static async Task FinalizePendingAfterServiceRestartAsync() + { + // The Setup executable may be locked until Inno closes. Recheck only + // the one SHA-pinned owned installer, never delete a foreign path. + for (var attempt = 0; attempt < 48 && !StopEvent.IsSet; attempt++) + { + if (!File.Exists(PendingUpdatePath)) return; + if (await ActionGate.WaitAsync(0)) + { + try + { + FinalizePendingUpdate(); + if (!File.Exists(PendingUpdatePath)) return; + } + finally { ActionGate.Release(); } + } + await Task.Delay(TimeSpan.FromSeconds(5)); + } + if (File.Exists(PendingUpdatePath)) + Log("pending-update-finalization-deferred-no-overwrite"); + } + + static void FinalizePendingUpdate() + { + try + { + if (!File.Exists(PendingUpdatePath)) return; + using var doc = JsonDocument.Parse(File.ReadAllText(PendingUpdatePath)); + var targetText = doc.RootElement.GetProperty("targetVersion").GetString() ?? ""; + if (doc.RootElement.GetProperty("schema").GetInt32() != 1 || + !Version.TryParse(targetText, out var target)) + throw new InvalidDataException("Pending update schema or version invalid; preserve record."); + var installed = CurrentVersion; + if (installed >= target) + { + // Setup can still be running from this EXE while the service restarts. + // Keep one pending record until the verified file can be removed. + var stage = doc.RootElement.GetProperty("installer").GetString() ?? ""; + var expectedSha = doc.RootElement.GetProperty("sha256").GetString() ?? ""; + // v1.5.1's official privileged updater used a versioned EXE name, + // while v1.5.2 uses one fixed owned update-stage.exe slot. Accept + // ONLY these two exact, non-symlink paths. A legacy name must + // match the pending target version AND the original release tag. + // Never accept an arbitrary path in the pending JSON, even when + // that file happens to have the same checksum. + var versionText = target.ToString(3); + var oldOfficialName = "DirectInternetMethod_" + versionText + "_Windows_Setup.exe"; + var oldOfficialPath = Path.Combine(UpdatesDir, oldOfficialName); + var tag = doc.RootElement.GetProperty("tag").GetString() ?? ""; + var canonicalStage = Path.GetFullPath(stage); + var fixedOwnedStage = canonicalStage.Equals(Path.GetFullPath(OwnedUpdateInstallerPath), + StringComparison.OrdinalIgnoreCase); + var pinnedLegacyStage = target >= new Version(1, 5, 2) + && tag.Equals("v" + versionText, StringComparison.OrdinalIgnoreCase) + && canonicalStage.Equals(Path.GetFullPath(oldOfficialPath), + StringComparison.OrdinalIgnoreCase); + if (!Path.IsPathFullyQualified(stage) || + (!fixedOwnedStage && !pinnedLegacyStage) || + expectedSha.Length != 64 || !expectedSha.All(Uri.IsHexDigit)) + throw new CryptographicException("Foreign staging path or invalid pending checksum."); + if (File.Exists(stage)) + { + if ((File.GetAttributes(stage) & FileAttributes.ReparsePoint) != 0 || + (File.GetAttributes(UpdatesDir) & FileAttributes.ReparsePoint) != 0) + throw new CryptographicException("Owned updater staging path is a reparse point."); + string observed; + using (var input = File.OpenRead(stage)) + observed = Convert.ToHexString(SHA256.HashData(input)); + if (!observed.Equals(expectedSha, StringComparison.OrdinalIgnoreCase)) + throw new CryptographicException("Owned staged installer changed; preserving for review."); + try { File.Delete(stage); } + catch (IOException) + { + WriteActionStatus("update", "running", null, "Installed; waiting for owned installer file to unlock."); + Log("update-stage-file-locked-preserving-single-pending-slot"); + return; + } + } + File.Delete(PendingUpdatePath); + WriteActionStatus("update", "done", 0, $"Updated to v{installed.ToString(3)}."); + Log($"update-complete:{installed.ToString(3)}:no-cache-duplicate"); + } + else + { + WriteActionStatus("update", "error", -3, $"Update target v{target.ToString(3)} was not installed; evidence preserved."); + Log($"update-incomplete-preserved:target={target.ToString(3)}:current={installed.ToString(3)}"); + } + } + catch (Exception ex) + { + Log($"update-finalize-failed:{ex.GetType().Name}:{Compact(ex.Message)}"); + } + } + + static async Task RunActionAsync(string scriptName, string actionName) + { + if (!await ActionGate.WaitAsync(0)) + { + Log($"action-busy:{actionName}"); + WriteActionStatus(actionName, "busy", -1, "Another action is already running."); + return; + } + + try + { + var script = Path.Combine(AppContext.BaseDirectory, scriptName); + if (!File.Exists(script)) + { + Log($"action-missing:{actionName}:{script}"); + WriteActionStatus(actionName, "error", -1, "Privileged lifecycle script is missing."); + return; + } + if (!File.Exists(PwshPath)) + { + Log($"runtime-missing:{actionName}:{PwshPath}"); + WriteActionStatus(actionName, "error", -1, "Bundled PowerShell runtime is missing."); + return; + } + + var psi = new ProcessStartInfo( + PwshPath, + $"-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"{script}\"") + { + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = AppContext.BaseDirectory, + // Fail-closed service errors are read, but raw output is never exposed. + RedirectStandardError = true + }; + + Log($"action-begin:{actionName}"); + WriteActionStatus(actionName, "running", null, ""); + using var p = Process.Start(psi); + if (p is null) + { + Log($"action-start-failed:{actionName}"); + WriteActionStatus(actionName, "error", -1, "Unable to start PowerShell backend."); + return; + } + + // Drain stderr concurrently; retain at most 8 KiB and never persist it. + var stderrTask = ReadActionErrorTailAsync(p.StandardError); + if (!p.WaitForExit(120000)) + { + try { p.Kill(entireProcessTree: true); } catch { } + try { p.WaitForExit(5000); } catch { } + Log($"action-timeout:{actionName}"); + WriteActionStatus(actionName, "error", -2, "Privileged action exceeded the 120 second safety timeout and was terminated."); + return; + } + var stderr = await stderrTask.WaitAsync(TimeSpan.FromSeconds(3)); + Log($"action-end:{actionName}:exit={p.ExitCode}"); + var message = p.ExitCode == 0 + ? "Action completed." + : DescribeActionFailure(p.ExitCode, stderr); + WriteActionStatus(actionName, p.ExitCode == 0 ? "done" : "error", p.ExitCode, message); + } + catch (Exception ex) + { + Log($"action-exception:{actionName}:{ex.GetType().Name}:{Compact(ex.Message)}"); + WriteActionStatus(actionName, "error", -1, ex.GetType().Name + ": " + ex.Message); + } + finally + { + ActionGate.Release(); + } + } + + static async Task ReadActionErrorTailAsync(StreamReader reader) + { + var tail = new StringBuilder(); + var buffer = new char[2048]; + int count; + while ((count = await reader.ReadAsync(buffer.AsMemory())) != 0) + { + tail.Append(buffer, 0, count); + if (tail.Length > 8192) + tail.Remove(0, tail.Length - 8192); + } + return tail.ToString(); + } + + static string DescribeActionFailure(int exitCode, string standardError) + { + // Never disclose arbitrary stderr: it can include paths, hosts or secrets. + if (standardError.Contains("DEFAULT_ROUTE_NOT_PHYSICAL", StringComparison.Ordinal)) + return "A VPN or virtual adapter is the preferred default route. Direct Internet Method kept the routes unchanged. Select a physical default route before starting."; + if (standardError.Contains("REFUSE_BROAD_TUNNEL_ROUTE", StringComparison.Ordinal)) + return "A broad VPN route is active. Direct Internet Method refused to change the current network configuration."; + if (standardError.Contains("HASH_MISMATCH_", StringComparison.Ordinal)) + return "Privileged runtime integrity check failed. Repair the verified installation before starting."; + return $"Privileged action failed with exit code {exitCode}. See lifecycle evidence/service log."; + } + + static void WriteActionStatus(string action, string phase, int? exitCode, string message) + { + try + { + Directory.CreateDirectory(RuntimeDir); + var payload = System.Text.Json.JsonSerializer.Serialize(new + { + schema = 1, + action, + phase, + exitCode, + message = Compact(message), + updatedUtc = DateTimeOffset.UtcNow.ToString("O") + }); + var temp = ActionStatusPath + ".tmp"; + File.WriteAllText(temp, payload, new UTF8Encoding(false)); + File.Move(temp, ActionStatusPath, true); + } + catch (Exception ex) + { + Log($"action-status-write-failed:{action}:{ex.GetType().Name}:{Compact(ex.Message)}"); + } + } + + static string Compact(string value) + { + if (string.IsNullOrWhiteSpace(value)) return "-"; + var s = value.Replace("\r", " ").Replace("\n", " "); + return s.Length <= 600 ? s : s[..600]; + } + + static void Log(string message) + { + try + { + Directory.CreateDirectory(RuntimeDir); + File.AppendAllText(LogPath, $"{DateTimeOffset.Now:O} {message}{Environment.NewLine}", Encoding.UTF8); + } + catch { } + } + + static void SetState(uint state, uint accepted, uint waitHint) + { + var s = new SERVICE_STATUS + { + dwServiceType = SERVICE_WIN32_OWN_PROCESS, + dwCurrentState = state, + dwControlsAccepted = accepted, + dwWin32ExitCode = 0, + dwServiceSpecificExitCode = 0, + dwCheckPoint = 0, + dwWaitHint = waitHint + }; + SetServiceStatus(_statusHandle, ref s); + } + + static string SelfTest() + { + var required = new[] { "Start-Direct.ps1", "Stop-Direct.ps1", "Recovery.ps1" }; + var missing = required.Where(x => !File.Exists(Path.Combine(AppContext.BaseDirectory, x))).ToArray(); + if (missing.Length != 0) return "MISSING:" + string.Join(",", missing); + if (!DescribeActionFailure(1, "START_FAIL_DEFAULT_ROUTE_NOT_PHYSICAL").Contains("VPN or virtual adapter", StringComparison.Ordinal)) + return "MISSING_ROUTE_GUARD_MESSAGE"; + if (!DescribeActionFailure(1, "REFUSE_BROAD_TUNNEL_ROUTE").Contains("broad VPN route", StringComparison.Ordinal)) + return "MISSING_BROAD_ROUTE_GUARD_MESSAGE"; + if (!DescribeActionFailure(1, "HASH_MISMATCH_ctrld.exe").Contains("integrity check failed", StringComparison.Ordinal)) + return "MISSING_RUNTIME_INTEGRITY_MESSAGE"; + if (DescribeActionFailure(1, "SECRET_SAMPLE private path").Contains("SECRET_SAMPLE", StringComparison.Ordinal)) + return "RAW_STDERR_LEAK"; + return "PASS"; + } +} From b65cc437dd6a4d827eae2169f994d99f1c5562fb Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:31:13 +0330 Subject: [PATCH 178/188] ci(windows): authorize exact PR64 branch in hosted native build guard --- tests/r535_windows_candidate_hosted_build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r535_windows_candidate_hosted_build.ps1 b/tests/r535_windows_candidate_hosted_build.ps1 index 3dd4027b..57bf095f 100644 --- a/tests/r535_windows_candidate_hosted_build.ps1 +++ b/tests/r535_windows_candidate_hosted_build.ps1 @@ -4,7 +4,7 @@ $ErrorActionPreference='Stop' Set-StrictMode -Version Latest $Root=$env:GITHUB_WORKSPACE if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_GITHUB_HOST_ONLY'} -if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156','refs/heads/prep/r803-forward-156-native-gates') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_AUTHORITY'} $r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json if($r.version -cne '1.5.6' -or $r.status -cne 'PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R535_RC1_DECLARATION'} $qa=Join-Path $env:RUNNER_TEMP 'DIM_R535_NATIVE' From cd9a206a113659c38ea09323bccf50053be58f56 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:31:31 +0330 Subject: [PATCH 179/188] ci(windows): authorize exact PR64 branch in hosted rollback guard --- tests/r535_windows_native_rollback.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/r535_windows_native_rollback.ps1 b/tests/r535_windows_native_rollback.ps1 index 8d73e878..a5ea6e7b 100644 --- a/tests/r535_windows_native_rollback.ps1 +++ b/tests/r535_windows_native_rollback.ps1 @@ -20,7 +20,7 @@ $PublicSha='FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133' $PublicGui='BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F' $PublicSvc='2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R535_HOSTED_VM_ONLY'} -if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} +if($env:GITHUB_REF -cnotin @('refs/heads/feature/r535-windows-155rc1','refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156','refs/heads/prep/r803-forward-156-native-gates') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R535_BRANCH_HEAD_DRIFT'} $admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R535_ADMIN_TOKEN_REQUIRED'} if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R535_UNSAFE_REPLAY_OR_MISSING_BUILD'} From bbad8eec2b7dd7c65686e167682b7ff5a81ea891 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:31:52 +0330 Subject: [PATCH 180/188] ci(windows): run hosted native installer and rollback on exact PR64 head --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d5b7d1f..dccebc87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -571,7 +571,7 @@ jobs: windows-private-r535-native: name: windows-private-r535-native - if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci' || github.ref == 'refs/heads/security/r776-pinned-root-read-qa' || github.ref == 'refs/heads/prep/r805-windows-156') + if: github.event_name == 'push' && (github.ref == 'refs/heads/feature/r535-windows-155rc1' || github.ref == 'refs/heads/feature/r520-linux-155rc1-ci' || github.ref == 'refs/heads/security/r776-pinned-root-read-qa' || github.ref == 'refs/heads/prep/r805-windows-156' || github.ref == 'refs/heads/prep/r803-forward-156-native-gates') needs: windows-source runs-on: windows-2025 timeout-minutes: 35 From da2c81cacd2ffb72e198fcd3b56f9355fedb022a Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:37:35 +0330 Subject: [PATCH 181/188] ci(windows): allow R600 exact-head attestation on PR64 hosted branch --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dccebc87..1fdcc686 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -604,7 +604,7 @@ jobs: $installer=Join-Path $root 'delivery\DirectInternetMethod_1.5.6_Windows_Setup.exe' if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -cne 'github-hosted' -or - $env:GITHUB_REF -cnotin @('refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156') -or + $env:GITHUB_REF -cnotin @('refs/heads/feature/r520-linux-155rc1-ci','refs/heads/security/r776-pinned-root-read-qa','refs/heads/prep/r805-windows-156','refs/heads/prep/r803-forward-156-native-gates') -or (git -C $root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R600_HOST_AUTHORITY'} if($report.status -cne 'PASS_ISOLATED_HOSTED_V156_BUILD' -or $native.status -cne 'PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154' -or From 20673417c3db93503fcb5c143eba9e6961bafa83 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 14:23:28 +0330 Subject: [PATCH 182/188] feat(r846): prepare private monotonic v1.5.7 source and hosted native validation (NO RELEASE) --- .github/workflows/ci.yml | 83 +++++++++--- build_linux_package.py | 10 +- docs/README_PACKAGE_157.md | 56 ++++++++ linux/app/direct_internet_method.py | 6 +- linux/app/user_uninstall_preserve.py | 2 +- linux/app/user_update_transaction.py | 16 +-- linux/install.sh | 6 +- linux/system/install_system.sh | 4 +- linux/system/protected_upgrade_transaction.py | 4 +- linux/system/upgrade_guard.py | 6 +- linux/system/upgrade_system.sh | 12 +- tests/dotnet10_target_contract.py | 9 +- ...pted_selective_dns_integration_contract.py | 2 +- .../linux_user_update_transaction_contract.py | 12 +- .../r519_user_update_transaction_contract.py | 12 +- tests/r519_versioned_package_contract.py | 18 +-- tests/r526_installer_metadata_contract.py | 6 +- ...664_direct_update_monotonicity_contract.py | 4 +- ...9_linux_update_cache_lifecycle_contract.py | 2 +- ...oot_guard_source_copy_identity_contract.py | 2 +- tests/r781_privileged_entry_contract.py | 2 +- tests/r803_user_from_rc1_forward_contract.py | 8 +- ...rivate_156_to_157_root_forward_contract.py | 59 +++++++++ ...846_windows_157_forward_source_contract.py | 62 +++++++++ tests/r846_windows_candidate_hosted_build.ps1 | 62 +++++++++ tests/r846_windows_native_rollback.ps1 | 125 ++++++++++++++++++ tests/release_metadata_audit.py | 15 ++- tests/tailscale_gui_scope_guard_contract.py | 6 +- tests/windows_contract_audit.py | 1 + tools/security/privileged_entry.py | 4 +- windows/RELEASE.json | 8 +- windows/gui/DirectInternetMethod.csproj | 2 +- windows/gui/MainWindow.xaml.cs | 2 +- windows/installer/DirectInternetMethod.iss | 6 +- windows/manifest.json | 18 +-- .../DirectInternetMethod.Service.csproj | 6 +- 36 files changed, 540 insertions(+), 118 deletions(-) create mode 100644 docs/README_PACKAGE_157.md create mode 100644 tests/r845_private_156_to_157_root_forward_contract.py create mode 100644 tests/r846_windows_157_forward_source_contract.py create mode 100644 tests/r846_windows_candidate_hosted_build.ps1 create mode 100644 tests/r846_windows_native_rollback.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fdcc686..3889f20d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: Source and Release Contracts on: push: - branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa', 'prep/r803-forward-156-native-gates', 'prep/r805-windows-156'] + branches: [main, 'feature/**', 'hotfix/**', 'security/r776-pinned-root-read-qa', 'prep/r803-forward-156-native-gates', 'prep/r805-windows-156', 'prep/r846-forward-157-native-gates'] tags: ['v*'] pull_request: branches: [main] @@ -30,7 +30,7 @@ jobs: fi bash -n "$path" done < <(find linux -type f -name '*.sh' | sort) - - name: Build deterministic Linux 1.5.6 package + - name: Build deterministic Linux 1.5.7 private source package run: python3 build_linux_package.py - name: Exact candidate SHA and non-release authority gate shell: bash @@ -38,17 +38,17 @@ jobs: set -euo pipefail python3 - <<'PY' import hashlib,json,pathlib,zipfile - p=pathlib.Path("delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip") - expected="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" - assert p.is_file() and p.stat().st_size==8427001 + p=pathlib.Path("delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip") + expected="16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF" + assert p.is_file() and p.stat().st_size==8427156 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) assert meta.get("version")=="1.5.4","PUBLISHED_RELEASE_METADATA_MUST_REMAIN_STABLE" with zipfile.ZipFile(p) as z: - root="DirectInternetMethod_1.5.6_Linux_x86_64/" + root="DirectInternetMethod_1.5.7_Linux_x86_64/" assert len(z.namelist())==30 and len(z.namelist())==len(set(z.namelist())) j=json.loads(z.read(root+"PACKAGE_MANIFEST.json")) - assert j["version"]=="1.5.6" and len(j["files"])==28 + assert j["version"]=="1.5.7" and len(j["files"])==28 for x in j["files"]: data=z.read(root+x["file"]) assert len(data)==x["bytes"] @@ -126,7 +126,7 @@ jobs: set -euo pipefail d="$(mktemp -d)" trap 'rm -rf "$d"' EXIT - unzip -q delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip -d "$d" + unzip -q delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip -d "$d" while IFS= read -r f; do bash -n "$f" if grep -q $'\r' "$f"; then @@ -181,7 +181,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -210,7 +210,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -243,7 +243,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -272,7 +272,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -301,7 +301,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -331,7 +331,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -376,7 +376,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -402,7 +402,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -467,8 +467,8 @@ jobs: $ErrorActionPreference='Stop' python -B tests/dotnet10_target_contract.py if($LASTEXITCODE -ne 0){throw 'R482_NET10_FRAMEWORK_CONTRACT_FAILED'} - python -B tests/r805_windows_156_forward_contract.py - if($LASTEXITCODE -ne 0){throw 'R805_WINDOWS_156_SOURCE_CONTRACT_FAILED'} + python -B tests/r846_windows_157_forward_source_contract.py + if($LASTEXITCODE -ne 0){throw 'R846_WINDOWS_157_SOURCE_CONTRACT_FAILED'} - name: Verify published v1.5.4 documentation is current shell: pwsh run: | @@ -653,7 +653,7 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' 'A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D' 'delivery/DirectInternetMethod_1.5.6_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status printf '%s %s\n' 'D0181C15C54B90A3B6E03FBFBE32148341037AB60CF4D6788B6A72140304340F' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash @@ -662,6 +662,51 @@ jobs: # Test only; PKEXEC_UID is simulated, and no product install runs. sudo -n /usr/bin/env CI=true GITHUB_ACTIONS=true GITHUB_REPOSITORY="$GITHUB_REPOSITORY" GITHUB_REF="$GITHUB_REF" GITHUB_SHA="$GITHUB_SHA" RUNNER_ENVIRONMENT="$RUNNER_ENVIRONMENT" GITHUB_WORKSPACE="$GITHUB_WORKSPACE" RUNNER_TEMP="$RUNNER_TEMP" /usr/bin/python3 -B tests/r782_hosted_trusted_entry_ephemeral.py + windows-private-r846-native: + name: windows-private-r846-native + if: github.event_name == 'push' && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/prep/r846-forward-157-native-gates' + needs: windows-source + runs-on: windows-2025 + timeout-minutes: 75 + permissions: + contents: read + actions: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6 + with: + dotnet-version: '10.0.x' + - name: R846 exact private v1.5.7 build and unsigned installer + shell: pwsh + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/r846_windows_candidate_hosted_build.ps1 + if($LASTEXITCODE -ne 0){ throw 'R846_PRIVATE_WINDOWS_BUILD_FAILED' } + - name: R846 hosted v1.5.4 to v1.5.7 in-place, preserve, uninstall and rollback + shell: pwsh + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/r846_windows_native_rollback.ps1 + if($LASTEXITCODE -ne 0){ throw 'R846_PRIVATE_WINDOWS_ROLLBACK_FAILED' } + - name: Retain v1.5.7 private test receipts + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: r846-windows-private-test-${{ github.run_id }} + path: | + ${{ runner.temp }}/DIM_R846_NATIVE/BUILD_R846.json + ${{ runner.temp }}/DIM_R846_NATIVE/NATIVE_R846.json + if-no-files-found: warn + retention-days: 7 + - name: Retain tested private v1.5.7 setup (never public release) + if: success() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: r846-windows-v157-private-verified-${{ github.run_id }} + path: delivery/DirectInternetMethod_1.5.7_Windows_Setup.exe + if-no-files-found: error + retention-days: 7 + windows-release-contract: name: windows-release-contract-unsigned if: startsWith(github.ref, 'refs/tags/v') diff --git a/build_linux_package.py b/build_linux_package.py index e77daede..97c7a179 100644 --- a/build_linux_package.py +++ b/build_linux_package.py @@ -2,10 +2,10 @@ from __future__ import annotations import hashlib,json,pathlib,zipfile,tempfile,os R=pathlib.Path(__file__).resolve().parent -OUT=R/"delivery"/"DirectInternetMethod_1.5.6_Linux_x86_64.zip" -ROOT="DirectInternetMethod_1.5.6_Linux_x86_64" +OUT=R/"delivery"/"DirectInternetMethod_1.5.7_Linux_x86_64.zip" +ROOT="DirectInternetMethod_1.5.7_Linux_x86_64" FILES=[ - ("docs/README_PACKAGE_156.md","README.md"), + ("docs/README_PACKAGE_157.md","README.md"), ("linux/install.sh","install.sh"), ("linux/uninstall.sh","uninstall.sh"), ("linux/system/install_system.sh","system/install_system.sh"), @@ -42,7 +42,7 @@ def payload_bytes(src:str,dst:str)->bytes: data=data.replace(b"\r\n",b"\n").replace(b"\r",b"\n") return data # Frozen originally shipped documentation; root README may describe current publication. -if sha_bytes(payload_bytes("docs/README_PACKAGE_156.md","README.md"))!="C836C617B05FCC01FD384BDF8A31BE5F51DF0335734A22C3953BEF8D594299D5": +if sha_bytes(payload_bytes("docs/README_PACKAGE_157.md","README.md"))!="9F7266E65D31DE84EE62A541FF35F9621E5009BF097C256D75FD2DFC407F21FF": raise SystemExit("R519_PACKAGE_README_SHA_CHANGED") rows=[] for src,dst in FILES: @@ -50,7 +50,7 @@ def payload_bytes(src:str,dst:str)->bytes: if not p.is_file(): raise SystemExit("MISSING:"+src) data=payload_bytes(src,dst) rows.append({"file":dst,"bytes":len(data),"sha256":sha_bytes(data)}) -manifest={"schema":1,"product":"Direct Internet Method","version":"1.5.6","platform":"linux-x86_64","files":rows} +manifest={"schema":1,"product":"Direct Internet Method","version":"1.5.7","platform":"linux-x86_64","files":rows} OUT.parent.mkdir(parents=True,exist_ok=True) fd,tmp_name=tempfile.mkstemp(prefix="dim-build-",suffix=".zip",dir=OUT.parent) os.close(fd) diff --git a/docs/README_PACKAGE_157.md b/docs/README_PACKAGE_157.md new file mode 100644 index 00000000..5679a603 --- /dev/null +++ b/docs/README_PACKAGE_157.md @@ -0,0 +1,56 @@ +# Direct Internet Method v1.5.7 — Linux release-preparation candidate (NOT PUBLISHED) + +This package is a **private validation candidate**, not a second public product +line. The current public Stable remains v1.5.4 until a separate authorized +GitHub release establishes exact asset identities and accepted test receipts. + +## Supported single-install upgrade paths + +- Public Linux User 1.5.4 with protected Root backend 1.5.2. +- Previously owner-accepted private Linux User 1.5.5-rc1 with protected Root + backend 1.5.5-rc1. +- No mixed, unknown, modified, symlinked, or partially installed Root/User pair + is silently upgraded. Existing custom Router Gateway data, settings, + Tailscale, DNS/physical route, and one original installation must survive. +- Windows private installations exist with FileVersion 1.5.5.0 and 1.5.6.0. +Publishing v1.5.6 will NOT offer an update to existing private v1.5.6. +Strictly newer v1.5.7 is required after full security and native acceptance. + +## Security and recovery + +Privileged protected-file reads accept only literal approved filenames with +descriptor-based no-symlink traversal, pinned SHA-256 payloads, regular-file +identity, and verified original ownership. A per-transaction random acceptance +nonce prevents replay of previous Root acceptance receipts. The new +version-specific trusted privileged bootstrap must be installed only through +normal native administrator authorization after verifying its reviewed digest. + +The Root+User maintenance transaction has one private rollback slot; a separate +Root-owned watchdog is armed before protected mutation and must be settled with +real Start, Stop, Recovery, original DNS/Tailscale checks and user settings +hashes before release. A preliminary staged/apply or hosted CI PASS alone does +not authorize physical deployment or publication. Do not disable host security +policies, reboot, modify default routes, reset DNS, or automatically install +a VPN or global tunnel. + +## Known acceptance limits + +Some physical networks reset YouTube TLS depending on SNI even when +public Google IPs are reachable. Site acceptance must be verified on the +intended network; no universal-connectivity claim is made. The current +physical network is not a verified universal connectivity target. Historical +plain `curl` HTTP403 and isolated headless browser timeout do **not** prove +ChatGPT account sign-in. Optional encrypted selective-DNS needs explicit, +separate user consent, and its controller remains restricted to safe native +rollback and Tailscale coexistence. + +Current exact source starting point: primary PR #63 commit +`b38aa94b67fb102dd24359184abaa324cd29a3ad`, plus separately scoped +R803 forward-upgrade migration work. The new candidate's final Git SHA, +package digest, hosted Linux/Windows acceptance and release decision remain +UNVERIFIED until recorded on the exact generated artifacts. + +Public v1.5.4 release and protected `main` remain unchanged. This document is +a package-level statement of intended behavior, not a claim of publication. + +R845 engineering draft: source preflight ONLY; native installation, CI and release are NOT ACCEPTED. diff --git a/linux/app/direct_internet_method.py b/linux/app/direct_internet_method.py index 6e768bb1..f6df2e12 100644 --- a/linux/app/direct_internet_method.py +++ b/linux/app/direct_internet_method.py @@ -25,7 +25,7 @@ from socket_fallback import LocalSocketFallback APP_ID="io.github.god13emad.DirectInternetMethod" -VERSION="1.5.6" +VERSION="1.5.7" GLib.set_prgname("DirectInternetMethod") GLib.set_application_name("Direct Internet Method") try: @@ -184,7 +184,7 @@ def sync_adult_catalog(force=False): last_error=None for source_url in ADULT_CATALOG_URLS: try: - req=urllib.request.Request(source_url,headers={"User-Agent":"DirectInternetMethod/1.5.6"}) + req=urllib.request.Request(source_url,headers={"User-Agent":"DirectInternetMethod/1.5.7"}) with urllib.request.urlopen(req,timeout=20) as resp: raw=resp.read(4*1024*1024+1) if len(raw)<100000 or len(raw)>4*1024*1024: @@ -338,7 +338,7 @@ def _coexistence_backend_ready(): not helper.is_file() or not meta.is_file()): return False installed=json.loads(meta.read_text(encoding="utf-8")) - return (installed.get("version") == "1.5.6" and + return (installed.get("version") == "1.5.7" and installed.get("uid")==os.getuid() and hashlib.sha256(helper.read_bytes()).hexdigest()==COEXIST_HELPER_SHA256) except Exception: diff --git a/linux/app/user_uninstall_preserve.py b/linux/app/user_uninstall_preserve.py index 7d07e88d..4a3680f5 100644 --- a/linux/app/user_uninstall_preserve.py +++ b/linux/app/user_uninstall_preserve.py @@ -51,7 +51,7 @@ def load_owned(): meta = app / "INSTALL.json" ensure(meta.is_file() and not meta.is_symlink(), "UNINSTALL_INSTALL_METADATA_MISSING") doc = json.loads(meta.read_text(encoding="utf8")) - ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.6", + ensure(doc.get("schema") == 2 and doc.get("version") == "1.5.7", "UNINSTALL_MANIFEST_OWNER_VERSION_UNVERIFIED") codes = doc.get("managedCodeSha256") launchers = doc.get("managedLaunchersSha256") diff --git a/linux/app/user_update_transaction.py b/linux/app/user_update_transaction.py index cb85c0f5..c55e751c 100644 --- a/linux/app/user_update_transaction.py +++ b/linux/app/user_update_transaction.py @@ -137,7 +137,7 @@ def source_files(root: pathlib.Path) -> dict: """ guard(root.is_absolute() and root.is_dir() and not root.is_symlink() and not root.parent.is_symlink(), "SOURCE_PACKAGE_INVALID") - archive_layout = root.name == "DirectInternetMethod_1.5.6_Linux_x86_64" + archive_layout = root.name == "DirectInternetMethod_1.5.7_Linux_x86_64" source_layout = root.name == "linux" guard(archive_layout or source_layout, "SOURCE_PACKAGE_LAYOUT_NOT_LINUX_OR_EXTRACTED_ZIP") if archive_layout: @@ -152,7 +152,7 @@ def source_files(root: pathlib.Path) -> dict: raise RuntimeError("EXTRACTED_PACKAGE_MANIFEST_INVALID_JSON") from ex guard(isinstance(package_doc, dict) and package_doc.get("schema") == 1 and package_doc.get("product") == "Direct Internet Method" and - package_doc.get("version") == "1.5.6" and + package_doc.get("version") == "1.5.7" and package_doc.get("platform") == "linux-x86_64" and isinstance(package_doc.get("files"), list) and len(package_doc["files"]) == 28, "EXTRACTED_PACKAGE_IDENTITY_WRONG") @@ -277,7 +277,7 @@ def stage(src: pathlib.Path): try: (pending / "old").mkdir(mode=0o700) manifest = { - "schema": 1, "version": "1.5.6", + "schema": 1, "version": "1.5.7", "managed": { rel: {"sha": row["sha"], "mode": row["mode"]} for rel, row in payload.items() @@ -312,7 +312,7 @@ def load_pending(): not manifest_file.is_symlink(), "PENDING_TRANSACTION_NOT_VALID") guard(pending.stat().st_uid == os.getuid(), "PENDING_TRANSACTION_NOT_OWNED") data = json.loads(manifest_file.read_text(encoding="utf8")) - guard(data.get("schema") == 1 and data.get("version") == "1.5.6", "PENDING_TRANSACTION_VERSION_WRONG") + guard(data.get("schema") == 1 and data.get("version") == "1.5.7", "PENDING_TRANSACTION_VERSION_WRONG") allowed = {METADATA, *(rel for _, rel, _ in FILES), PROVIDERS} guard(set(data["old"]) == set(data["managed"]) | {METADATA} and set(data["old"]) <= allowed, "PENDING_FILESET_FOREIGN") @@ -378,7 +378,7 @@ def check_metadata(app, data): path = app / METADATA guard(path.is_file() and not path.is_symlink(), "INSTALLED_METADATA_MISSING") doc = json.loads(path.read_text(encoding="utf8")) - guard(doc.get("version") == "1.5.6" and doc.get("schema") == 2, "INSTALLED_VERSION_METADATA_NOT_VALID") + guard(doc.get("version") == "1.5.7" and doc.get("schema") == 2, "INSTALLED_VERSION_METADATA_NOT_VALID") guard(doc.get("managedProviderSha256") == data["providerSha"], "PROVIDER_MANAGED_OWNERSHIP_METADATA_CHANGED") expected = {rel: row["sha"] for rel, row in data["managed"].items() if rel != PROVIDERS} @@ -421,7 +421,7 @@ def rollback(): if rel == METADATA and observed not in (info["sha"], None): try: doc = json.loads(dest.read_text(encoding="utf8")) - guard(doc.get("schema") == 2 and doc.get("version") == "1.5.6", + guard(doc.get("schema") == 2 and doc.get("version") == "1.5.7", "FOREIGN_INSTALL_METADATA_PRESERVED") except (UnicodeError, ValueError) as ex: raise RuntimeError("UNKNOWN_INSTALL_METADATA_ON_ROLLBACK") from ex @@ -515,11 +515,11 @@ def recover_metadata_stage(expected_sha256: str): } guard(set(doc) == expected_keys and doc["schema"] == 2 and doc["product"] == "Direct Internet Method" and - doc["version"] == "1.5.6" and doc["platform"] == "linux-x86_64" and + doc["version"] == "1.5.7" and doc["platform"] == "linux-x86_64" and doc["networkMutationOnInstall"] is False and doc["normalRunRequiresAdmin"] is False and doc["systemBackend"] == "/usr/lib/directinternetmethod" and - doc["protectedBackendVersion"] == "1.5.6" and + doc["protectedBackendVersion"] == "1.5.7" and doc["protectedBackendByteCompatible"] is True and doc["onlineUpdate"] == "GitHub latest release + SHA256SUMS verification" and doc["updateMode"] == "IN_PLACE_SINGLE_INSTALL_DATA_PRESERVED", diff --git a/linux/install.sh b/linux/install.sh index d687917c..8f070b3d 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -75,7 +75,7 @@ backend_matches_current() { python3 - /var/lib/directinternetmethod/install.json "$uid" <<'PY' >/dev/null 2>&1 || return 1 import json,sys d=json.load(open(sys.argv[1],encoding="utf-8")) -raise SystemExit(0 if d.get("version")=="1.5.6" and str(d.get("uid"))==sys.argv[2] else 1) +raise SystemExit(0 if d.get("version")=="1.5.7" and str(d.get("uid"))==sys.argv[2] else 1) PY [ "$(cat /etc/directinternetmethod.uid 2>/dev/null || true)" = "$uid" ] || return 1 for action in start stop recovery; do @@ -229,11 +229,11 @@ for name, expected in expected_launchers.items(): launchers[name]=hashlib.sha256(expected).hexdigest() transaction=json.loads((pathlib.Path.home()/".local/state/DirectInternetMethod/update-pending/manifest.json").read_text(encoding="utf8")) protected=json.loads(pathlib.Path("/var/lib/directinternetmethod/install.json").read_text(encoding="utf-8")) -assert protected.get("version") == "1.5.6" +assert protected.get("version") == "1.5.7" metadata=json.dumps({ "schema":2, "product":"Direct Internet Method", - "version":"1.5.6", + "version":"1.5.7", "platform":"linux-x86_64", "installedUtc":time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime()), "networkMutationOnInstall":False, diff --git a/linux/system/install_system.sh b/linux/system/install_system.sh index 4610bed8..3d8713a9 100644 --- a/linux/system/install_system.sh +++ b/linux/system/install_system.sh @@ -108,7 +108,7 @@ test -f /etc/polkit-1/rules.d/49-directinternetmethod.rules test "$(cat /etc/directinternetmethod.uid)" = "$USER_UID" install -d -m 0755 /var/lib/directinternetmethod cat > /var/lib/directinternetmethod/install.json <&2; exit 77;; esac [ -d "$SRC" ] && [ ! -L "$SRC" ] && [ -f "$SRC/system/protected_upgrade_transaction.py" ] || { echo "UPGRADE_SOURCE_MISSING_OR_SYMLINK" >&2; exit 77; } -[ "$(basename "$SRC")" = "DirectInternetMethod_1.5.6_Linux_x86_64" ] || { +[ "$(basename "$SRC")" = "DirectInternetMethod_1.5.7_Linux_x86_64" ] || { echo "WRONG_PACKAGE_LAYOUT" >&2; exit 77; } owner_uid="$(stat -c %u -- "$SRC")" @@ -34,7 +34,7 @@ assert p.is_file() and not p.is_symlink() s=p.stat() assert s.st_uid==0 and stat.S_IMODE(s.st_mode)==0o644 m=json.loads(p.read_text('utf8')) -assert m['schema']==1 and m['version'] in ('1.5.2','1.5.5-rc1') and m['uid']==expected +assert m['schema']==1 and m['version'] in ('1.5.2','1.5.5-rc1','1.5.7') and m['uid']==expected assert m['home'].startswith('/home/') PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any @@ -101,9 +101,9 @@ try: assert data["schema"]==1 and data["revision"]=="R552" assert data["uid"]==uid and metadata["uid"]==uid and data["home"]==str(home) assert (data["versionFromRoot"],data["versionFromUser"]) in ( - ("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1")) + ("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"),("1.5.7","1.5.7")) assert metadata["version"]==data["versionFromRoot"] - assert data["versionTo"]=="1.5.6" + assert data["versionTo"]=="1.5.7" assert data["oldRootMetaSha"]==sha(root) assert data["oldUserMetaSha"]==sha(home/".local/share/DirectInternetMethod/INSTALL.json") assert data["rootTransactionSha"]==root_sha @@ -116,7 +116,7 @@ try: pm=pending/"manifest.json" assert pm.is_file() and not pm.is_symlink() and pm.stat().st_uid==uid stage=json.loads(pm.read_text("utf8")) - assert stage["schema"]==1 and stage["version"]=="1.5.6" + assert stage["schema"]==1 and stage["version"]=="1.5.7" assert stage["previousVersion"]==data["versionFromUser"] old=stage["old"]["INSTALL.json"] assert old["exists"] is True and str(old["sha"]).upper()==data["oldUserMetaSha"] @@ -141,5 +141,5 @@ GUARD_ROOT_TX="/var/lib/directinternetmethod/upgrade-guard/root_tx.py" python3 -B "$GUARD_ROOT_TX" stage --source "$SRC" python3 -B "$GUARD_ROOT_TX" apply python3 -B "$GUARD_ROOT_TX" status | - python3 -c 'import json,sys;j=json.load(sys.stdin);assert j["status"]=="APPLIED" and j["version"]=="1.5.6"' + python3 -c 'import json,sys;j=json.load(sys.stdin);assert j["status"]=="APPLIED" and j["version"]=="1.5.7"' echo '{"status":"ROOT_R519_APPLIED_UNCOMMITTED","needsRealNativeAcceptance":true}' diff --git a/tests/dotnet10_target_contract.py b/tests/dotnet10_target_contract.py index 6eac789c..34ff74b6 100644 --- a/tests/dotnet10_target_contract.py +++ b/tests/dotnet10_target_contract.py @@ -15,9 +15,10 @@ CURRENT=REL.get("version") if CURRENT=="1.5.4": assert REL.get("status")=="CANDIDATE_V154_UNPUBLISHED" and REL.get("previousAccepted",{}).get("version")=="1.5.3" -elif CURRENT in ("1.5.5-rc1","1.5.6"): +elif CURRENT in ("1.5.5-rc1","1.5.6","1.5.7"): accepted={"1.5.5-rc1":"PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", - "1.5.6":"PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN"} + "1.5.6":"PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", + "1.5.7":"PRIVATE_WINDOWS_V157_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN"} assert REL.get("status")==accepted[CURRENT] assert REL.get("previousAccepted",{}).get("version")=="1.5.4" and REL.get("previousAccepted",{}).get("tag")=="v1.5.4" assert REL.get("publication",{}).get("publishedAt") is None @@ -62,9 +63,9 @@ def negative_first(): def main(): negative_first() ci=CI.read_text("utf-8") - expected_dotnet_jobs = 2 if CURRENT in ("1.5.5-rc1","1.5.6") else 1 + expected_dotnet_jobs = 3 if CURRENT=="1.5.7" else (2 if CURRENT in ("1.5.5-rc1","1.5.6") else 1) assert ci.count("dotnet-version: '10.0.x'")==expected_dotnet_jobs,"SDK_10_JOB_PIN_COUNT" - if CURRENT in ("1.5.5-rc1","1.5.6"): + if CURRENT in ("1.5.5-rc1","1.5.6","1.5.7"): assert ci.count(" windows-private-r535-native:")==1 assert "runs-on: windows-2025" in ci and "tests/r535_windows_native_rollback.ps1" in ci assert "dotnet-version: '8.0.x'" not in ci diff --git a/tests/encrypted_selective_dns_integration_contract.py b/tests/encrypted_selective_dns_integration_contract.py index 2946f649..88d48aab 100644 --- a/tests/encrypted_selective_dns_integration_contract.py +++ b/tests/encrypted_selective_dns_integration_contract.py @@ -106,7 +106,7 @@ def test_controller_checks_root_owned_process_sha_before_commit(self): 'NATIVE_NFQWS_PID_MISMATCH','NATIVE_NFQWS_PROCESS_FOREIGN'): self.assertIn(text,CONTROLLER) def test_r519_candidate_is_explicit_unreleased_versioned_bundle(self): - self.assertIn('DirectInternetMethod_1.5.6_Linux_x86_64.zip',B) + self.assertIn('DirectInternetMethod_1.5.7_Linux_x86_64.zip',B) self.assertIn('linux/system/encrypted_selective_dns.py',B) self.assertIn('linux/system/encrypted_selective_policy.py',B) self.assertIn('linux/system/protected_upgrade_transaction.py',B) diff --git a/tests/linux_user_update_transaction_contract.py b/tests/linux_user_update_transaction_contract.py index aa860c00..f9a1a281 100644 --- a/tests/linux_user_update_transaction_contract.py +++ b/tests/linux_user_update_transaction_contract.py @@ -91,7 +91,7 @@ def metadata(home, source): app = home / ".local/share/DirectInternetMethod" manifest = json.loads((home / ".local/state/DirectInternetMethod/update-pending/manifest.json").read_text()) new = { - "schema": 2, "version": "1.5.6", + "schema": 2, "version": "1.5.7", "managedProviderSha256": manifest["providerSha"], "managedCodeSha256": {k:v["sha"] for k,v in manifest["managed"].items() if k != tx.PROVIDERS}, @@ -154,7 +154,7 @@ def unchanged_data(app, user): pending_manifest = home3 / ".local/state/DirectInternetMethod/update-pending/manifest.json" pending_original = pending_manifest.read_bytes() corrupted = json.loads(pending_original) - need(corrupted["version"] == "1.5.6", "STAGE_NOT_VERSIONED_154") + need(corrupted["version"] == "1.5.7", "STAGE_NOT_VERSIONED_154") corrupted["version"] = "1.5.2" pending_manifest.write_text(json.dumps(corrupted), encoding="utf8") refused = calls(home3, src, "--apply", False) @@ -369,7 +369,7 @@ def unchanged_data(app, user): "router_gateway/providers.json": {"sha": "c" * 64}}, })) protected = sandbox / "fake-protected-backend.json" - protected.write_text(json.dumps({"version": "1.5.6"})) + protected.write_text(json.dumps({"version": "1.5.7"})) old_metadata = b'{"schema":2,"version":"1.5.1"}\n' metadata_path = fake_app / "INSTALL.json" metadata_path.write_bytes(old_metadata) @@ -418,7 +418,7 @@ def run_inline(): accepted = run_inline() need(accepted.returncode == 0, "F415_ATOMIC_METADATA_WRITE_FAILED:" + accepted.stderr) doc = json.loads(metadata_path.read_text(encoding="utf-8")) - need(doc["version"] == "1.5.6" and doc["schema"] == 2 + need(doc["version"] == "1.5.7" and doc["schema"] == 2 and doc["managedCodeSha256"] == {"direct_internet_method.py": "b" * 64} and doc["preservedCustomProviderData"] is True, "F415_METADATA_POSTCONDITION_INVALID") @@ -446,13 +446,13 @@ def run_inline(): doc = { "schema": 2, "product": "Direct Internet Method", - "version": "1.5.6", + "version": "1.5.7", "platform": "linux-x86_64", "installedUtc": "2026-10-12T00:00:00Z", "networkMutationOnInstall": False, "normalRunRequiresAdmin": False, "systemBackend": "/usr/lib/directinternetmethod", - "protectedBackendVersion": "1.5.6", + "protectedBackendVersion": "1.5.7", "protectedBackendByteCompatible": True, "onlineUpdate": "GitHub latest release + SHA256SUMS verification", "managedProviderSha256": manifest["providerSha"], diff --git a/tests/r519_user_update_transaction_contract.py b/tests/r519_user_update_transaction_contract.py index aa860c00..f9a1a281 100644 --- a/tests/r519_user_update_transaction_contract.py +++ b/tests/r519_user_update_transaction_contract.py @@ -91,7 +91,7 @@ def metadata(home, source): app = home / ".local/share/DirectInternetMethod" manifest = json.loads((home / ".local/state/DirectInternetMethod/update-pending/manifest.json").read_text()) new = { - "schema": 2, "version": "1.5.6", + "schema": 2, "version": "1.5.7", "managedProviderSha256": manifest["providerSha"], "managedCodeSha256": {k:v["sha"] for k,v in manifest["managed"].items() if k != tx.PROVIDERS}, @@ -154,7 +154,7 @@ def unchanged_data(app, user): pending_manifest = home3 / ".local/state/DirectInternetMethod/update-pending/manifest.json" pending_original = pending_manifest.read_bytes() corrupted = json.loads(pending_original) - need(corrupted["version"] == "1.5.6", "STAGE_NOT_VERSIONED_154") + need(corrupted["version"] == "1.5.7", "STAGE_NOT_VERSIONED_154") corrupted["version"] = "1.5.2" pending_manifest.write_text(json.dumps(corrupted), encoding="utf8") refused = calls(home3, src, "--apply", False) @@ -369,7 +369,7 @@ def unchanged_data(app, user): "router_gateway/providers.json": {"sha": "c" * 64}}, })) protected = sandbox / "fake-protected-backend.json" - protected.write_text(json.dumps({"version": "1.5.6"})) + protected.write_text(json.dumps({"version": "1.5.7"})) old_metadata = b'{"schema":2,"version":"1.5.1"}\n' metadata_path = fake_app / "INSTALL.json" metadata_path.write_bytes(old_metadata) @@ -418,7 +418,7 @@ def run_inline(): accepted = run_inline() need(accepted.returncode == 0, "F415_ATOMIC_METADATA_WRITE_FAILED:" + accepted.stderr) doc = json.loads(metadata_path.read_text(encoding="utf-8")) - need(doc["version"] == "1.5.6" and doc["schema"] == 2 + need(doc["version"] == "1.5.7" and doc["schema"] == 2 and doc["managedCodeSha256"] == {"direct_internet_method.py": "b" * 64} and doc["preservedCustomProviderData"] is True, "F415_METADATA_POSTCONDITION_INVALID") @@ -446,13 +446,13 @@ def run_inline(): doc = { "schema": 2, "product": "Direct Internet Method", - "version": "1.5.6", + "version": "1.5.7", "platform": "linux-x86_64", "installedUtc": "2026-10-12T00:00:00Z", "networkMutationOnInstall": False, "normalRunRequiresAdmin": False, "systemBackend": "/usr/lib/directinternetmethod", - "protectedBackendVersion": "1.5.6", + "protectedBackendVersion": "1.5.7", "protectedBackendByteCompatible": True, "onlineUpdate": "GitHub latest release + SHA256SUMS verification", "managedProviderSha256": manifest["providerSha"], diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index f6cc2112..f9214212 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -7,7 +7,7 @@ from __future__ import annotations import ast,hashlib,importlib.util,json,pathlib,re,tempfile,unittest,zipfile R=pathlib.Path(__file__).resolve().parents[1] -VERSION="1.5.6" +VERSION="1.5.7" PACKAGE=R/"delivery"/f"DirectInternetMethod_{VERSION}_Linux_x86_64.zip" GUI=R/"linux/app/direct_internet_method.py" HELPER=R/"linux/app/direct_method_helper.sh" @@ -63,7 +63,7 @@ def test_r519b_uninstaller_accepts_only_new_ownership_version(self): self.assertEqual(digest(got),declared["sha256"]) self.assertEqual(declared["sha256"],"6BFD45BA87CE052D020D26BE5B6E0F589DF1B848D202D716AFD8FB87FD1FDE9A") self.assertIn('"socket_fallback.py"', got.decode()) - self.assertIn('doc.get("version") == "1.5.6"',got.decode()) + self.assertIn('doc.get("version") == "1.5.7"',got.decode()) self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) self.assertEqual(digest((R/"linux/app/user_uninstall_preserve.py").read_bytes()),declared["sha256"]) def test_original_unreleased_candidate_preserved_and_current_source_delta(self): @@ -126,12 +126,12 @@ def test_original_published_metadata_untouched(self): self.assertFalse(any("1.5.5-rc1" in x["file"] for x in self.rows)) def test_fresh_root_contract_version(self): root=(R/"linux/system/install_system.sh").read_text() - self.assertIn('{"schema":1,"version":"1.5.6"',root) + self.assertIn('{"schema":1,"version":"1.5.7"',root) self.assertIn('encrypted_selective_dns.py',root) self.assertIn('encrypted_selective_policy.py',root) def test_protected_upgrade_exact_sha(self): text=ROOT_TXN.read_text() - self.assertIn('TARGET_VERSION="1.5.6"',text) + self.assertIn('TARGET_VERSION="1.5.7"',text) self.assertIn('CURRENT_BACKEND_VERSION="1.5.2"',text) self.assertIn('data.get("revision")=="R519"',text) self.assertIn('receipt.get("revision")=="R519"',text) @@ -140,7 +140,7 @@ def test_protected_upgrade_exact_sha(self): def test_root_wrapper_pinned_identity(self): src=(R/"linux/system/upgrade_system.sh").read_text() self.assertIn(digest(ROOT_TXN.read_bytes()),src) - self.assertIn('DirectInternetMethod_1.5.6_Linux_x86_64',src) + self.assertIn('DirectInternetMethod_1.5.7_Linux_x86_64',src) self.assertIn("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED",src) self.assertIn("GUARD_SHA=",src) self.assertLess(src.index("R553_ROOT_GUARD_ACTIVE_PRESTAGE_VERIFIED"), @@ -199,12 +199,12 @@ def test_user_installer_never_pretends_committed(self): self.assertIn('python3 "$USER_TRANSACTION" --rollback',src) def test_gui_and_user_metadata_pins(self): app=GUI.read_text() - self.assertIn('VERSION="1.5.6"',app) + self.assertIn('VERSION="1.5.7"',app) self.assertIn('COEXIST_HELPER_SHA256="'+digest(HELPER.read_bytes()).lower()+'"',app) - self.assertIn('installed.get("version") == "1.5.6"',app) + self.assertIn('installed.get("version") == "1.5.7"',app) updater=USER_TXN.read_text() self.assertIn('len(package_doc["files"]) == 28',updater) - self.assertIn('data.get("version") == "1.5.6"',updater) + self.assertIn('data.get("version") == "1.5.7"',updater) self.assertIn('"encrypted-dns-optin.txt"',updater) def test_rc_semver_prevents_implicit_downgrade(self): code=ast.parse(GUI.read_text()) @@ -228,7 +228,7 @@ def test_archive_manifest_negative_corrupted_payload(self): def test_readme_is_explicitly_unreleased(self): s=PKG_DOC.read_text() self.assertIn("NOT PUBLISHED",s) - self.assertIn("1.5.6",s) + self.assertIn("1.5.7",s) self.assertIn("v1.5.4",s) if __name__=="__main__": result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Tests)) diff --git a/tests/r526_installer_metadata_contract.py b/tests/r526_installer_metadata_contract.py index eca9d576..c74ebe0d 100644 --- a/tests/r526_installer_metadata_contract.py +++ b/tests/r526_installer_metadata_contract.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""R526: hermetic Linux 1.5.6 INSTALL.json activation regression gate. +"""R526: hermetic Linux 1.5.7 INSTALL.json activation regression gate. Run the exact inline metadata program from linux/install.sh against a disposable HOME and a fake protected Root metadata file. No real installer, sudo, systemd, @@ -23,7 +23,7 @@ BEGIN = """python3 - "$APP/INSTALL.json" "$USER_TRANSACTION" <<'PY'\n""" END = "\nPY\n" PROTECTED = 'pathlib.Path("/var/lib/directinternetmethod/install.json")' -VERSION = "1.5.6" +VERSION = "1.5.7" def exact_inline_program() -> str: @@ -98,7 +98,7 @@ def test_01_entrypoint_and_exact_source_contract(self) -> None: self.assertTrue(INSTALL.is_file() and USER_TX.is_file()) self.assertEqual(len(self.program.splitlines()), 64) self.assertEqual(self.program.count(PROTECTED), 1) - self.assertIn('assert protected.get("version") == "1.5.6"', self.program) + self.assertIn('assert protected.get("version") == "1.5.7"', self.program) self.assertIn('transaction_helper.atomic_bytes(', self.program) def test_02_in_place_metadata_and_mutable_preservation(self) -> None: diff --git a/tests/r664_direct_update_monotonicity_contract.py b/tests/r664_direct_update_monotonicity_contract.py index 77d7014c..46281f04 100644 --- a/tests/r664_direct_update_monotonicity_contract.py +++ b/tests/r664_direct_update_monotonicity_contract.py @@ -34,9 +34,9 @@ raise AssertionError("UNSAFE_VERSION_WAS_NOT_REJECTED:" + version) win_release = json.loads((ROOT / "windows/RELEASE.json").read_text("utf-8-sig")) -if win_release.get("version") in ("1.5.5-rc1","1.5.6"): +if win_release.get("version") in ("1.5.5-rc1","1.5.6","1.5.7"): expected_assembly = ("1.5.5.0" if win_release["version"]=="1.5.5-rc1" - else "1.5.6.0") + else "1.5.7.0" if win_release["version"]=="1.5.7" else "1.5.6.0") for rel in ("windows/gui/DirectInternetMethod.csproj", "windows/service/DirectInternetMethod.Service.csproj"): source = (ROOT / rel).read_text("utf-8-sig") diff --git a/tests/r739_linux_update_cache_lifecycle_contract.py b/tests/r739_linux_update_cache_lifecycle_contract.py index 657ca840..5050affe 100644 --- a/tests/r739_linux_update_cache_lifecycle_contract.py +++ b/tests/r739_linux_update_cache_lifecycle_contract.py @@ -34,7 +34,7 @@ exec(compile(ast.Module(body=METHOD, type_ignores=[]), "installed_linux_updater_real_function", "exec"), fn_scope) install = fn_scope["download_and_install_update"] -VERSION = "1.5.6" +VERSION = "1.5.7" ARCHIVE_NAME = f"DirectInternetMethod_{VERSION}_Linux_x86_64.zip" PREFIX = ARCHIVE_NAME.removesuffix(".zip") + "/" diff --git a/tests/r779_root_guard_source_copy_identity_contract.py b/tests/r779_root_guard_source_copy_identity_contract.py index 64a00291..eb491bb7 100644 --- a/tests/r779_root_guard_source_copy_identity_contract.py +++ b/tests/r779_root_guard_source_copy_identity_contract.py @@ -27,7 +27,7 @@ class GuardSourceIdentity(unittest.TestCase): def setUp(self): self.tmp=tempfile.TemporaryDirectory(prefix="r779-owned-",dir=str(ROOT)) self.base=pathlib.Path(self.tmp.name) - self.src=self.base/"DirectInternetMethod_1.5.6_Linux_x86_64" + self.src=self.base/"DirectInternetMethod_1.5.7_Linux_x86_64" (self.src/"system").mkdir(parents=True) (self.src/"app").mkdir() self.root_tx=self.src/"system/protected_upgrade_transaction.py" diff --git a/tests/r781_privileged_entry_contract.py b/tests/r781_privileged_entry_contract.py index 658d42e4..fe69d22c 100644 --- a/tests/r781_privileged_entry_contract.py +++ b/tests/r781_privileged_entry_contract.py @@ -18,7 +18,7 @@ SPEC=importlib.util.spec_from_file_location("dim_r781",SRC) m=importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(m) -PKG="DirectInternetMethod_1.5.6_Linux_x86_64" +PKG="DirectInternetMethod_1.5.7_Linux_x86_64" class RootlessTrustedEntry(unittest.TestCase): def setUp(self): diff --git a/tests/r803_user_from_rc1_forward_contract.py b/tests/r803_user_from_rc1_forward_contract.py index 62007367..c10a35fb 100644 --- a/tests/r803_user_from_rc1_forward_contract.py +++ b/tests/r803_user_from_rc1_forward_contract.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""R803: real user transaction from accepted private 1.5.5-rc1 to 1.5.6. +"""R803: real user transaction from accepted private 1.5.5-rc1 to 1.5.7. Strictly user-owned temporary HOME; no physical install, pkexec, network, systemd, DNS, Tailscale, external browser, or release publication. @@ -48,7 +48,7 @@ def initialize(home,manage_provider): return app,metadata,preserved,H(provider.read_bytes()) def finalize_metadata(app,source_manifest): installed={ - "schema":2,"version":"1.5.6", + "schema":2,"version":"1.5.7", "managedProviderSha256":source_manifest["providerSha"], "managedCodeSha256":{name:row["sha"] for name,row in source_manifest["managed"].items() if name!="router_gateway/providers.json"}, @@ -70,7 +70,7 @@ def case(label,manage_provider,commit): invoke(home,"--stage") pending=home/".local/state/DirectInternetMethod/update-pending" manifest=json.loads((pending/"manifest.json").read_text()) - need(manifest["version"]=="1.5.6" and manifest["previousVersion"]=="1.5.5-rc1","INCORRECT_RC1_PREDECESSOR") + need(manifest["version"]=="1.5.7" and manifest["previousVersion"]=="1.5.5-rc1","INCORRECT_RC1_PREDECESSOR") need(manifest["providerManaged"] is manage_provider,"PROVIDER_MANAGED_POLICY") need(len(manifest["managed"])==(9 if manage_provider else 8),"MANAGED_CARDINALITY") invoke(home,"--apply") @@ -78,7 +78,7 @@ def case(label,manage_provider,commit): if commit: finalize_metadata(app,manifest) invoke(home,"--commit") - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6","RC1_FORWARD_VERSION") + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.7","RC1_FORWARD_VERSION") for name,row in manifest["managed"].items(): need(H((app/name).read_bytes())==row["sha"],"NEW_MANAGED_SHA_"+name) if not manage_provider: diff --git a/tests/r845_private_156_to_157_root_forward_contract.py b/tests/r845_private_156_to_157_root_forward_contract.py new file mode 100644 index 00000000..eef67667 --- /dev/null +++ b/tests/r845_private_156_to_157_root_forward_contract.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""R845: fake-root private 1.5.6 -> 1.5.7 positive and negative regression. + +No native Root, DNS, VPN, network route, polkit or actual installation. +""" +import importlib.util, json, pathlib, unittest +R=pathlib.Path(__file__).resolve().parents[1] +spec=importlib.util.spec_from_file_location("r845_fixture",R/"tests/protected_upgrade_transaction_contract.py") +assert spec and spec.loader +base=importlib.util.module_from_spec(spec) +spec.loader.exec_module(base) +m=base.m +class Private156To157(base.FakeFixture): + def private_156(self): + for name,source,target,old,new,mode in m.FILES: + p=self.root/target + p.parent.mkdir(parents=True,exist_ok=True) + p.write_bytes((self.pkg/source).read_bytes()) + p.chmod(mode) + self.assertEqual(m.hash_file(p),new) + meta=dict(self.orig,version="1.5.6") + self.meta.write_text(json.dumps(meta,sort_keys=True)+"\n") + self.meta.chmod(0o644) + def test_forward_and_rollback(self): + self.private_156() + before=self.root_sha() + self.assertEqual(m.TARGET_VERSION,"1.5.7") + self.assertEqual(self.tx.stage(self.pkg)["status"],"STAGED") + doc=self.tx.manifest() + self.assertEqual(doc["fromVersion"],"1.5.6") + self.assertEqual(doc["toVersion"],"1.5.7") + self.assertEqual(self.tx.apply()["status"],"APPLIED") + self.assertEqual(json.loads(self.meta.read_text())["version"],"1.5.7") + self.assertEqual(self.tx.rollback()["status"],"ROLLED_BACK") + self.assertEqual(json.loads(self.meta.read_text())["version"],"1.5.6") + self.assertEqual(self.root_sha(),before) + def test_tampered_root_denied(self): + self.private_156() + p=self.root/m.FILES[0][2] + p.write_bytes(b"foreign modified root code") + with self.assertRaisesRegex(RuntimeError,"BASELINE|OLD_FILE_SHA"): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + def test_unknown_version_denied(self): + self.private_156() + meta=json.loads(self.meta.read_text()) + meta["version"]="1.5.8" + self.meta.write_text(json.dumps(meta)+"\n") + with self.assertRaisesRegex(RuntimeError,"OLD_ROOT_VERSION"): + self.tx.stage(self.pkg) + self.assertFalse(self.tx.pending.exists()) + def test_version_pair_whitelist(self): + s=(R/"linux/system/upgrade_guard.py").read_text() + self.assertIn('("1.5.6","1.5.6")',s) + self.assertNotIn('("1.5.6","1.5.5-rc1")',s) +if __name__=="__main__": + tests=("test_forward_and_rollback","test_tampered_root_denied","test_unknown_version_denied","test_version_pair_whitelist") + run=unittest.TextTestRunner(verbosity=2).run(unittest.TestSuite(Private156To157(t) for t in tests)) + raise SystemExit(0 if run.wasSuccessful() else 1) diff --git a/tests/r846_windows_157_forward_source_contract.py b/tests/r846_windows_157_forward_source_contract.py new file mode 100644 index 00000000..8e7b8918 --- /dev/null +++ b/tests/r846_windows_157_forward_source_contract.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""R846: proposed private 1.5.7 Windows forward-source proof, not a release.""" +import json +from pathlib import Path +from xml.etree import ElementTree +import unittest + +R=Path(__file__).resolve().parents[1] +PUB=json.loads((R/"RELEASE.json").read_text("utf-8")) +META=json.loads((R/"windows/RELEASE.json").read_text("utf-8")) +ISS=(R/"windows/installer/DirectInternetMethod.iss").read_text("utf-8") +WF=(R/".github/workflows/ci.yml").read_text("utf-8") +UI=(R/"windows/gui/MainWindow.xaml.cs").read_text("utf-8") +UP=(R/"windows/gui/UpdateClient.cs").read_text("utf-8") +SVC=(R/"windows/service/Program.cs").read_text("utf-8") + +class ForwardWindows157(unittest.TestCase): + def test_private_version_not_published(self): + self.assertEqual(PUB["version"],"1.5.4") + self.assertEqual(META["version"],"1.5.7") + self.assertEqual(META["status"],"PRIVATE_WINDOWS_V157_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN") + self.assertEqual(META["previousAccepted"]["tag"],"v1.5.4") + self.assertIsNone(META["publication"]["publishedAt"]) + def test_assembly_forward_from_real_private156(self): + for rel in ("windows/gui/DirectInternetMethod.csproj","windows/service/DirectInternetMethod.Service.csproj"): + tree=ElementTree.parse(R/rel) + for tag,expected in (("Version","1.5.7"),("FileVersion","1.5.7.0"),("AssemblyVersion","1.5.7.0"),("TargetFramework","net10.0-windows")): + self.assertEqual(tree.findtext(".//"+tag),expected,rel) + self.assertGreater((1,5,7,0),(1,5,6,0)) + def test_same_id_inplace_and_single_uninstall_log(self): + for token in ('#define MyAppVersion "1.5.7"','VersionInfoVersion=1.5.7.0', + 'OutputBaseFilename=DirectInternetMethod_1.5.7_Windows_Setup', + 'UsePreviousAppDir=yes','UninstallLogMode=overwrite', + 'AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}', + 'PrivilegesRequired=admin'): + self.assertIn(token,ISS) + def test_monotonic_updater_unweakened(self): + self.assertIn('latest <= current',UP) + self.assertIn('latest <= CurrentVersion',SVC) + self.assertIn('Version.TryParse(clean, out var latest)',UP) + self.assertIn('Version.TryParse(clean, out var latest)',SVC) + self.assertIn('DirectInternetMethod/1.5.7',UI) + def test_historic_r535_receipts_retained(self): + self.assertIn('PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154', + (R/"tests/r535_windows_native_rollback.ps1").read_text("utf-8")) + self.assertIn('PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN', + (R/"tests/r805_windows_156_forward_contract.py").read_text("utf-8")) + self.assertIn('prep/r846-forward-157-native-gates',WF) + self.assertIn('windows-private-r846-native:',WF) + self.assertIn('tests/r846_windows_candidate_hosted_build.ps1',WF) + self.assertIn('tests/r846_windows_native_rollback.ps1',WF) + for rel in ("tests/r846_windows_candidate_hosted_build.ps1","tests/r846_windows_native_rollback.ps1"): + source=(R/rel).read_text("utf-8") + self.assertIn("R846",source) + self.assertIn("refs/heads/prep/r846-forward-157-native-gates",source) + self.assertIn("GOD13emad/DirectInternetMethod",source) + self.assertIn("github-hosted",source) + self.assertNotIn("refs/heads/prep/r803-forward-156-native-gates",source) + +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(ForwardWindows157)) + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/r846_windows_candidate_hosted_build.ps1 b/tests/r846_windows_candidate_hosted_build.ps1 new file mode 100644 index 00000000..5653381e --- /dev/null +++ b/tests/r846_windows_candidate_hosted_build.ps1 @@ -0,0 +1,62 @@ +[CmdletBinding()] +param() +$ErrorActionPreference='Stop' +Set-StrictMode -Version Latest +$Root=$env:GITHUB_WORKSPACE +if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R846_GITHUB_HOST_ONLY'} +if($env:GITHUB_REF -cnotin @('refs/heads/prep/r846-forward-157-native-gates') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R846_BRANCH_HEAD_AUTHORITY'} +$r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json +if($r.version -cne '1.5.7' -or $r.status -cne 'PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R846_RC1_DECLARATION'} +$qa=Join-Path $env:RUNNER_TEMP 'DIM_R846_NATIVE' +$gstage=Join-Path $qa 'gui' +$sstage=Join-Path $qa 'service' +$gt=Join-Path $Root 'windows\app\DirectInternetMethod.exe' +$st=Join-Path $Root 'windows\app\DirectInternetMethod.Service.exe' +$installer=Join-Path $Root 'delivery\DirectInternetMethod_1.5.7_Windows_Setup.exe' +foreach($p in @($qa,$gt,$st,$installer)){if(Test-Path $p){throw ('R846_UNSAFE_RERUN_'+$p)}} +New-Item -ItemType Directory -Path $qa|Out-Null +$iscc=Join-Path ([Environment]::GetFolderPath('ProgramFilesX86')) 'Inno Setup 6\ISCC.exe' +if(!(Test-Path $iscc)){$iscc=Join-Path $env:ProgramFiles 'Inno Setup 6\ISCC.exe'} +if(!(Test-Path $iscc)){throw 'R846_GITHUB_IMAGE_INNO_COMPILER_ABSENT'} +Set-Location $Root +& pwsh -NoProfile -NonInteractive -File tests/prepare_powershell_runtime.ps1 +if($LASTEXITCODE -ne 0){throw 'PWSH_BUNDLE_PREP_FAILED'} +if((Get-FileHash (Join-Path $Root 'windows\vendor\pwsh\pwsh.exe') -Algorithm SHA256).Hash -cne 'BFB46AF89433268872DDB43D1CA7A3F433452EE91ED356A9786940F90118E285'){throw 'PWSH_BUNDLE_SHA'} +& dotnet publish windows/gui/DirectInternetMethod.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:ContinuousIntegrationBuild=true -o $gstage +if($LASTEXITCODE -ne 0){throw 'GUI_PUBLISH_FAILED'} +& dotnet publish windows/service/DirectInternetMethod.Service.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:ContinuousIntegrationBuild=true -o $sstage +if($LASTEXITCODE -ne 0){throw 'SERVICE_PUBLISH_FAILED'} +$gb=Join-Path $gstage 'DirectInternetMethod.exe';$sb=Join-Path $sstage 'DirectInternetMethod.Service.exe' +if(!(Test-Path $gb) -or !(Test-Path $sb)){throw 'RC1_BINARIES_MISSING'} +foreach($name in @('Start-Direct.ps1','Stop-Direct.ps1','Recovery.ps1')){Copy-Item -LiteralPath (Join-Path $Root ('windows\app\'+$name)) -Destination (Join-Path $sstage $name)} +$test=Start-Process $sb -ArgumentList '--console-selftest' -Wait -PassThru -NoNewWindow +if($test.ExitCode -ne 0){throw ('SERVICE_SELFTEST_'+$test.ExitCode)} +Copy-Item $gb $gt +Copy-Item $sb $st +& python -B tests/build_windows_manifest.py +if($LASTEXITCODE -ne 0){throw 'MANIFEST_FAILED'} +& python -B tests/dotnet10_target_contract.py +if($LASTEXITCODE -ne 0){throw 'NET10_CONTRACT_FAILED'} +& python -B tests/windows_contract_audit.py | Out-Null +if($LASTEXITCODE -ne 0){throw 'WINDOWS_AUDIT_FAILED'} +$m=Get-Content (Join-Path $Root 'windows\manifest.json') -Raw|ConvertFrom-Json +if($m.version -cne '1.5.7' -or @($m.files).Count -ne 681){throw 'RC1_MANIFEST_IDENTITY'} +& $iscc /Qp (Join-Path $Root 'windows\installer\DirectInternetMethod.iss') +if($LASTEXITCODE -ne 0){throw 'INNO_COMPILATION_FAILED'} +if(!(Test-Path $installer) -or (Get-Item $installer).Length -lt 50000000){throw 'R846_INSTALLER_MISSING'} +if([string](Get-AuthenticodeSignature $installer).Status -ne 'NotSigned'){throw 'R846_SIGNATURE_POLICY_CHANGED'} +$report=[ordered]@{ + revision='R846';stage='BUILD';status='PASS_ISOLATED_HOSTED_V156_BUILD' + head=$env:GITHUB_SHA;version='1.5.7';stable='v1.5.4' + manifestCount=@($m.files).Count;manifestSha=(Get-FileHash (Join-Path $Root 'windows\manifest.json') -Algorithm SHA256).Hash + guiSha=(Get-FileHash $gt -Algorithm SHA256).Hash + serviceSha=(Get-FileHash $st -Algorithm SHA256).Hash + installerSha=(Get-FileHash $installer -Algorithm SHA256).Hash + installerBytes=(Get-Item $installer).Length;unsigned=$true + installation='NOT_TESTED_YET';publicReleaseUnchanged=$true +} +[IO.File]::WriteAllText((Join-Path $qa 'BUILD_R846.json'),(($report|ConvertTo-Json -Depth 5)+[Environment]::NewLine),[Text.UTF8Encoding]::new($false)) +Write-Output ('R846_GUI_SHA256='+$report.guiSha) +Write-Output ('R846_SERVICE_SHA256='+$report.serviceSha) +Write-Output ('R846_MANIFEST_SHA256='+$report.manifestSha) +Write-Output ('PASS_R846_HOSTED_BUILD_SHA256='+$report.installerSha) diff --git a/tests/r846_windows_native_rollback.ps1 b/tests/r846_windows_native_rollback.ps1 new file mode 100644 index 00000000..075f9511 --- /dev/null +++ b/tests/r846_windows_native_rollback.ps1 @@ -0,0 +1,125 @@ +[CmdletBinding()] +param() +$ErrorActionPreference='Stop' +Set-StrictMode -Version Latest +$Root=$env:GITHUB_WORKSPACE +$Dir=Join-Path $env:RUNNER_TEMP 'DIM_R846_NATIVE' +$Build=Join-Path $Dir 'BUILD_R846.json' +$Result=Join-Path $Dir 'NATIVE_R846.json' +$Old=Join-Path $Dir 'DirectInternetMethod_1.5.4_Windows_Setup.exe' +$New=Join-Path $Root 'delivery\DirectInternetMethod_1.5.7_Windows_Setup.exe' +$App=Join-Path $env:LOCALAPPDATA 'Programs\DirectInternetMethod' +$Priv=Join-Path $env:ProgramFiles 'DirectInternetMethod\Privileged' +$Config=Join-Path $env:ProgramData 'DirectInternetMethod\UserConfig\custom-hosts.txt' +$Provider=Join-Path $App 'app\router_gateway\providers.json' +$Evidence=Join-Path $App 'evidence\R846_KEEP.txt' +$Gui=Join-Path $App 'app\DirectInternetMethod.exe' +$Svc=Join-Path $Priv 'app\DirectInternetMethod.Service.exe' +$Key='HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1' +$PublicSha='FFB9A2AE19C2E89338EE12EE8DA133F5A16DBF4DCF961678C33D77D0C3843133' +$PublicGui='BE9C78C0B5C007B4A46480D423079C219006B730D9589279AA466B0F14BCF00F' +$PublicSvc='2AE7EA11A94BFA3187BC7CDF63A37CB5CD5B55C313C3966330FD280ECC9ECE41' +if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R846_HOSTED_VM_ONLY'} +if($env:GITHUB_REF -cnotin @('refs/heads/prep/r846-forward-157-native-gates') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R846_BRANCH_HEAD_DRIFT'} +$admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() +if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R846_ADMIN_TOKEN_REQUIRED'} +if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R846_UNSAFE_REPLAY_OR_MISSING_BUILD'} +$build=Get-Content $Build -Raw |ConvertFrom-Json +if($build.status -cne 'PASS_ISOLATED_HOSTED_V156_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R846_BUILD_AUTHORITY'} +if((Get-FileHash $New -Algorithm SHA256).Hash -cne $build.installerSha){throw 'R846_RC1_INSTALLER_SHA'} +$report=[ordered]@{schema=1;revision='R846';status='FAIL_UNPROVEN';source=$env:GITHUB_SHA; + runner='github-hosted-windows-2025';privateInstallerSha=$build.installerSha; + publishedStableSha=$PublicSha;noReboot=$true;noPhysicalMachines=$true; + checks=@();errors=@()} +function Require([bool]$ok,[string]$name) { + if(!$ok){throw ('R846_FAIL_CLOSED_'+$name)} + $report.checks+=($name+':PASS') +} +function FileSha([string]$p){ (Get-FileHash -LiteralPath $p -Algorithm SHA256).Hash } +function Network { + [ordered]@{ + defaultRoutes=(@(Get-NetRoute -DestinationPrefix '0.0.0.0/0' | + Select-Object InterfaceIndex,NextHop,RouteMetric|Sort-Object InterfaceIndex,NextHop)|ConvertTo-Json -Depth 5 -Compress) + dns=(@(Get-DnsClientServerAddress -AddressFamily IPv4| + Select-Object InterfaceIndex,ServerAddresses|Sort-Object InterfaceIndex)|ConvertTo-Json -Depth 5 -Compress)} +} +function Install([string]$exe,[string]$step) { + $args=@('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART','/NORESTARTAPPLICATIONS','/CLOSEAPPLICATIONS','/SP-') + $p=Start-Process -FilePath $exe -ArgumentList $args -Wait -PassThru -NoNewWindow + Require ($p.ExitCode -eq 0) ($step+'_EXIT') +} +function ExactRegisteredUninstaller([object]$d) { + # Inno Setup allocates uninsNNN per directory, not per version. Inspect + # the real AppId registration and require just one corresponding file pair. + $entries=@(Get-ChildItem -LiteralPath $App -Force -ErrorAction Stop | Where-Object { $_.Name -match '^unins[0-9]{3}[.](exe|dat|msg)$' }) + $exes=@($entries|Where-Object { $_.Extension -ieq '.exe' }) + $datas=@($entries|Where-Object { $_.Extension -ieq '.dat' }) + $msgs=@($entries|Where-Object { $_.Extension -ieq '.msg' }) + Require ($exes.Count -eq 1 -and $datas.Count -eq 1 -and $msgs.Count -le 1) 'ONE_UNINSTALLER' + $stem=[IO.Path]::GetFileNameWithoutExtension($exes[0].Name) + Require ($stem -ieq [IO.Path]::GetFileNameWithoutExtension($datas[0].Name)) 'MATCHED_UNINSTALL_LOG' + Require ($msgs.Count -eq 0 -or $stem -ieq [IO.Path]::GetFileNameWithoutExtension($msgs[0].Name)) 'MATCHED_UNINSTALL_MSG' + foreach($item in $entries) { + Require (!$item.PSIsContainer -and ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -eq 0) 'UNINSTALLER_NODE_UNSAFE' + } + $path=[IO.Path]::GetFullPath($exes[0].FullName) + Require ([string]$d.UninstallString -ieq ('"'+$path+'"')) 'EXACT_UNINSTALL_PATH_OWNED' + return $path +} +function Registered([string]$version) { + Require (Test-Path $Key) 'SINGLE_HKLM_APPID' + $d=Get-ItemProperty -LiteralPath $Key + Require ($d.DisplayVersion -ceq $version) 'EXACT_REGISTERED_VERSION' + Require ($d.InstallLocation.TrimEnd('\') -ieq $App.TrimEnd('\')) 'SINGLE_INSTALL_PATH' + Require (!(Test-Path ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1'))) 'NO_HKCU_DUPLICATE' + [void](ExactRegisteredUninstaller $d) + return $d +} +try { + Require (!(Test-Path $Key) -and !(Test-Path $App) -and !(Test-Path $Priv)) 'CLEAN_DISPOSABLE_VM' + $before=Network + $report.networkBefore=$before + Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Windows_Setup.exe' -OutFile $Old -MaximumRedirection 5 -TimeoutSec 180 + Require ((Get-Item $Old).Length -eq 154270907 -and (FileSha $Old) -ceq $PublicSha) 'OFFICIAL_STABLE_SHA_PIN' + Install $Old 'STABLE_FRESH' + [void](Registered '1.5.4') + Require ((FileSha $Gui) -ceq $PublicGui -and (FileSha $Svc) -ceq $PublicSvc) 'OFFICIAL_STABLE_GUI_AND_SERVICE' + New-Item -ItemType Directory -Path (Split-Path $Config -Parent) -Force|Out-Null + Require (!(Test-Path $Config)) 'NO_PREEXISTING_PRIVATE_CONFIG' + [IO.File]::WriteAllText($Config,'R846_PRIVATE_CUSTOM_SITE_SENTINEL') + $configSha=FileSha $Config + Require (Test-Path $Provider) 'PROVIDER_EXISTS' + [IO.File]::AppendAllText($Provider,[Environment]::NewLine+' ') + $providerSha=FileSha $Provider + New-Item -ItemType Directory -Path (Split-Path $Evidence -Parent) -Force|Out-Null + [IO.File]::WriteAllText($Evidence,'R846_USER_UNKNOWN_EVIDENCE_KEEP') + $evidenceSha=FileSha $Evidence + Install $New 'RC1_IN_PLACE' + [void](Registered '1.5.7') + Require ((FileSha $Gui) -ceq $build.guiSha -and (FileSha $Svc) -ceq $build.serviceSha) 'RC1_GUI_SERVICE_PIN' + Require ((FileSha (Join-Path $App 'manifest.json')) -ceq $build.manifestSha) 'RC1_MANIFEST_PIN' + Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.7') 'RC1_INSTALLED_VERSION' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' + $d=Registered '1.5.7' + $uninstaller=ExactRegisteredUninstaller $d + $u=Start-Process $uninstaller -ArgumentList @('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART') -Wait -PassThru -NoNewWindow + Require ($u.ExitCode -eq 0) 'RC1_UNINSTALL_ZERO' + Require (!(Test-Path $Key)) 'ONE_UNINSTALLER_REMOVED_REGISTRY' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'UNKNOWN_USER_DATA_SURVIVED_UNINSTALL' + Install $Old 'STABLE_ROLLBACK_REINSTALL' + [void](Registered '1.5.4') + Require ((FileSha $Gui) -ceq $PublicGui -and (FileSha $Svc) -ceq $PublicSvc) 'ORIGINAL_STABLE_BINARY_HASHES_RESTORED' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'PRIVATE_CONFIG_PRESERVED_ROLLBACK' + $after=Network;$report.networkAfter=$after + Require ($before.defaultRoutes -ceq $after.defaultRoutes -and $before.dns -ceq $after.dns) 'NETWORK_BASELINE_EXACT' + $report.status='PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154' + $report.singleUninstaller=$true;$report.customProviderUpgradePreserved=$true;$report.dataPreserved=$true +} catch { + $report.status='DEGRADED_UNRESOLVED' + $report.errors+=([string]$_.Exception.Message) +} finally { + [IO.File]::WriteAllText($Result,(ConvertTo-Json $report -Depth 9)+[Environment]::NewLine,[Text.UTF8Encoding]::new($false)) + Write-Output ('R846_NATIVE_STATUS='+$report.status) + Write-Output ('R846_RECEIPT_SHA256='+((Get-FileHash $Result -Algorithm SHA256).Hash)) +} +if($report.status -ne 'PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154'){throw ('R846_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} diff --git a/tests/release_metadata_audit.py b/tests/release_metadata_audit.py index bc1ac5d4..24f75cfa 100644 --- a/tests/release_metadata_audit.py +++ b/tests/release_metadata_audit.py @@ -170,6 +170,16 @@ def require_public_stable_forward_update_from_rc155(version: str) -> None: # R805 is a strictly unpublished cross-platform v1.5.6 source candidate. # It may retain the immutable public v1.5.4 RELEASE.json only on its exact # private prep branch. It is NOT accepted as a public v1.5.6 publication. +private_win_v157 = ( + version == '1.5.4' and win_version == '1.5.7' + and win_release.get('status') == 'PRIVATE_WINDOWS_V157_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN' + and win_release.get('previousAccepted',{}).get('version') == '1.5.4' + and win_release.get('previousAccepted',{}).get('tag') == 'v1.5.4' + and win_release.get('publication',{}).get('publishedAt') is None + and (branch == 'prep/r846-forward-157-native-gates' + or ref == 'refs/heads/prep/r846-forward-157-native-gates') +) +# R846: a source candidate is not published simply because the version increased. private_win_v156 = ( version == '1.5.4' and win_version == '1.5.6' and win_release.get('status') == 'PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN' @@ -179,7 +189,7 @@ def require_public_stable_forward_update_from_rc155(version: str) -> None: and (branch == 'prep/r805-windows-156' or ref == 'refs/heads/prep/r805-windows-156') ) -if win_version != version and not (private_win_rc1 or private_win_v156): +if win_version != version and not (private_win_rc1 or private_win_v156 or private_win_v157): raise SystemExit('WINDOWS_PRIVATE_CANDIDATE_IDENTITY_NOT_AUTHORIZED') if str(win_manifest.get('version')) != win_version: raise SystemExit('WINDOWS_MANIFEST_NOT_MATCHING_PLATFORM_VERSION') @@ -189,7 +199,8 @@ def require_public_stable_forward_update_from_rc155(version: str) -> None: # Its published metadata remains anchored to v1.5.4 until native acceptance. # Require exact RC1 UI bytes for private candidate and exact stable otherwise. linux_source_expected = ('1.5.5-rc1' if private_win_rc1 else - '1.5.6' if private_win_v156 else version) + '1.5.7' if private_win_v157 else + '1.5.6' if private_win_v156 else version) if ('VERSION="' + linux_source_expected + '"') not in linux_ui and ('VERSION = "' + linux_source_expected + '"') not in linux_ui: raise SystemExit('Linux UI version is inconsistent with authorized source state') diff --git a/tests/tailscale_gui_scope_guard_contract.py b/tests/tailscale_gui_scope_guard_contract.py index d28a8026..c3154f5a 100644 --- a/tests/tailscale_gui_scope_guard_contract.py +++ b/tests/tailscale_gui_scope_guard_contract.py @@ -105,16 +105,16 @@ def mode():return env["load_state"]() check=h_env["_coexistence_backend_ready"] assert check() is False FakePath.entries[good]="safe-backend" -FakePath.entries[meta]='{"version":"1.5.6","uid":1000}' +FakePath.entries[meta]='{"version":"1.5.7","uid":1000}' assert check() is True, "pinned helper and metadata must be recognized" FakePath.entries[good]="different-backend" assert check() is False, "foreign helper must be refused" FakePath.entries[good]="safe-backend" FakePath.entries[meta]='{"version":"1.5.1","uid":1000}' assert check() is False, "old metadata must be refused" -FakePath.entries[meta]='{"version":"1.5.6","uid":1001}' +FakePath.entries[meta]='{"version":"1.5.7","uid":1001}' assert check() is False, "different user metadata must be refused" -FakePath.entries[meta]='{"version":"1.5.6","uid":1000}' +FakePath.entries[meta]='{"version":"1.5.7","uid":1000}' FakePath.symlinks.add(good) assert check() is False, "helper symlink must be refused" FakePath.symlinks.clear() diff --git a/tests/windows_contract_audit.py b/tests/windows_contract_audit.py index 4a1659b6..2d08c3fe 100644 --- a/tests/windows_contract_audit.py +++ b/tests/windows_contract_audit.py @@ -308,6 +308,7 @@ def idx(s,x): return s.find(x) "1.5.4":("CANDIDATE_V154_UNPUBLISHED","1.5.3"), "1.5.5-rc1":("PRIVATE_WINDOWS_RC1_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), "1.5.6":("PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), + "1.5.7":("PRIVATE_WINDOWS_V157_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN","1.5.4"), } current=release_meta.get("version") check("manifest_version", current in allowed_status diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index 3d317fea..b66464c7 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -23,8 +23,8 @@ import sys import tempfile -VERSION = "1.5.6" -PACKAGE = "DirectInternetMethod_1.5.6_Linux_x86_64" +VERSION = "1.5.7" +PACKAGE = "DirectInternetMethod_1.5.7_Linux_x86_64" MANIFEST_SHA256 = "1BDF1EE5C6C3A7C18623C31D16496C077022EDFB16A68163FD885F5D909DAF9A" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 diff --git a/windows/RELEASE.json b/windows/RELEASE.json index bb0283dd..396ab4b9 100644 --- a/windows/RELEASE.json +++ b/windows/RELEASE.json @@ -1,9 +1,9 @@ { "schema": 1, "product": "Direct Internet Method", - "version": "1.5.6", - "status": "PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", - "date": "2026-10-20", + "version": "1.5.7", + "status": "PRIVATE_WINDOWS_V157_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN", + "date": "2026-10-31", "previousAccepted": { "version": "1.5.4", "tag": "v1.5.4", @@ -137,7 +137,7 @@ "Targeted remains the default scope; All Sites is explicit opt-in.", "Windows installer remains not Authenticode-signed." ], - "exactNextAction": "Windows 1.5.6 isolated build and staged-hosted install/upgrade rollback with byte-pinned public v1.5.4; no public promotion before native acceptance.", + "exactNextAction": "Build and validate candidate v1.5.7 Windows in isolated hosted CI; verify monotonic upgrade from private 1.5.6.0, in-place data preservation and rollback against public v1.5.4 without promoting publication before native acceptance and independent review.", "buildIdentityNote": "Installer and manifest hashes are intentionally not embedded here because this file is itself part of the packaged payload. Authoritative artifact identity lives in root RELEASE.json, SHA256SUMS.txt and evidence records.", "publication": { "tag": null, diff --git a/windows/gui/DirectInternetMethod.csproj b/windows/gui/DirectInternetMethod.csproj index 0916bfbd..e5d98076 100644 --- a/windows/gui/DirectInternetMethod.csproj +++ b/windows/gui/DirectInternetMethod.csproj @@ -1 +1 @@ -WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.6false1.5.6.01.5.6.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file +WinExenet10.0-windowstrueenableenableDirectInternetMethodDirectInternetMethod..\app\DirectInternetMethod.ico1.5.7false1.5.7.01.5.7.0Direct Internet MethodDirectInternetMethod.ico \ No newline at end of file diff --git a/windows/gui/MainWindow.xaml.cs b/windows/gui/MainWindow.xaml.cs index bdadecd5..2a84b14d 100644 --- a/windows/gui/MainWindow.xaml.cs +++ b/windows/gui/MainWindow.xaml.cs @@ -132,7 +132,7 @@ async Task EnsureAdultCatalogAsync(bool force) } using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(20) }; - http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.6"); + http.DefaultRequestHeaders.UserAgent.ParseAdd("DirectInternetMethod/1.5.7"); Exception? lastError = null; foreach (var sourceUrl in AdultCatalogUrls) { diff --git a/windows/installer/DirectInternetMethod.iss b/windows/installer/DirectInternetMethod.iss index d5c179a4..ba8ce1e6 100644 --- a/windows/installer/DirectInternetMethod.iss +++ b/windows/installer/DirectInternetMethod.iss @@ -1,5 +1,5 @@ #define MyAppName "Direct Internet Method" -#define MyAppVersion "1.5.6" +#define MyAppVersion "1.5.7" #define MyAppPublisher "Direct Internet Method" #define MyAppExeName "DirectInternetMethod.exe" @@ -7,7 +7,7 @@ AppId={{B5388E8B-9AF3-41F4-87E2-01C9601A36CB} AppName={#MyAppName} AppVersion={#MyAppVersion} -VersionInfoVersion=1.5.6.0 +VersionInfoVersion=1.5.7.0 AppPublisher={#MyAppPublisher} DefaultDirName={localappdata}\Programs\DirectInternetMethod UsePreviousAppDir=yes @@ -19,7 +19,7 @@ UninstallLogMode=overwrite DefaultGroupName=Direct Internet Method DisableProgramGroupPage=yes OutputDir=..\..\delivery -OutputBaseFilename=DirectInternetMethod_1.5.6_Windows_Setup +OutputBaseFilename=DirectInternetMethod_1.5.7_Windows_Setup Compression=lzma2/max SolidCompression=yes WizardStyle=modern diff --git a/windows/manifest.json b/windows/manifest.json index 163beaf9..a4288bea 100644 --- a/windows/manifest.json +++ b/windows/manifest.json @@ -1,7 +1,7 @@ { "schema": 2, "product": "Direct Internet Method", - "version": "1.5.6", + "version": "1.5.7", "architecture": "Native WPF UI + fixed-command Windows service + bundled PowerShell 7.6.6 + leak-closed direct-IP ctrld DoH + accepted zapret/winws/WinDivert multiprotocol engine with Targeted/All Sites scope control", "offlineRuntime": true, "normalRunRequiresAdmin": false, @@ -19,8 +19,8 @@ "scope": "user", "file": "app/DirectInternetMethod.exe", "source": "app/DirectInternetMethod.exe", - "bytes": 139900030, - "sha256": "FBC67BE3B361308953CD07899A160F1A79A8B20124E54B90585BE00C8B391513" + "bytes": 139900022, + "sha256": "C2E30BEE147D28F32756878DA6BC66C879A9CB15C2B238F4ED82CD5F63122BDB" }, { "scope": "user", @@ -61,22 +61,22 @@ "scope": "user", "file": "RELEASE.json", "source": "RELEASE.json", - "bytes": 5851, - "sha256": "AFB3CC3ED64FD3D1E4D9460C1171899FAE0B9233AB96337498BAABF0CF353C5F" + "bytes": 5949, + "sha256": "88B1DB087B356C3F16790325CD68ECA590ACBA32D6150C5E4141FD52A6A8B85A" }, { "scope": "privileged", "file": "app/DirectInternetMethod.Service.exe", "source": "app/DirectInternetMethod.Service.exe", - "bytes": 73627467, - "sha256": "EE02C5188BAF3997DDB56787A671AA7947D2BE895C208271D4AE5ECFE309FF46" + "bytes": 73631555, + "sha256": "21330D21EE8AD631F96BC0F27FB3AE216DEA4F30811535FA2D98A6284301253B" }, { "scope": "privileged", "file": "app/Start-Direct.ps1", "source": "app/Start-Direct.ps1", - "bytes": 26860, - "sha256": "B186872EDE733AE876FF1463E030988E9149A2CC88188F4B6C1FA5B564AEFC05" + "bytes": 27728, + "sha256": "E03AA828A89F8BEA34051C58EEE250685F440CE65B923C4B2CD6EAE8ABE3FE95" }, { "scope": "privileged", diff --git a/windows/service/DirectInternetMethod.Service.csproj b/windows/service/DirectInternetMethod.Service.csproj index 7a43b945..bcbedcfa 100644 --- a/windows/service/DirectInternetMethod.Service.csproj +++ b/windows/service/DirectInternetMethod.Service.csproj @@ -7,10 +7,10 @@ DirectInternetMethod.Service DirectInternetMethod.Service ..\app\DirectInternetMethod.ico - 1.5.6 + 1.5.7 false - 1.5.6.0 - 1.5.6.0 + 1.5.7.0 + 1.5.7.0 Direct Internet Method Service \ No newline at end of file From bb577eaf1eaa7b7bad36c9b47203adaec9f80712 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 14:33:54 +0330 Subject: [PATCH 183/188] fix(r846): re-pin private v1.5.7 protected source SHA chain and hosted installer identity gates --- .github/workflows/ci.yml | 20 +++++++++---------- linux/system/upgrade_guard.py | 4 ++-- linux/system/upgrade_system.sh | 10 +++++----- tests/r519_versioned_package_contract.py | 8 ++++---- .../r532_ephemeral_root_watchdog_contract.py | 4 ++-- tests/r532_root_watchdog_worker.py | 4 ++-- tests/r552_native_upgrade_guard_contract.py | 2 +- tests/r554_native_guard_ephemeral_contract.py | 6 +++--- .../r566_native_systemd_persistent_catchup.py | 2 +- ...rivate_156_to_157_root_forward_contract.py | 3 +++ tests/r846_windows_candidate_hosted_build.ps1 | 4 ++-- tests/r846_windows_native_rollback.ps1 | 6 +++--- tools/security/privileged_entry.py | 2 +- 13 files changed, 39 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3889f20d..2660f276 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: python3 - <<'PY' import hashlib,json,pathlib,zipfile p=pathlib.Path("delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip") - expected="16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF" + expected="F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6" assert p.is_file() and p.stat().st_size==8427156 assert hashlib.sha256(p.read_bytes()).hexdigest().upper()==expected meta=json.loads(pathlib.Path("RELEASE.json").read_text()) @@ -181,7 +181,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: Download and independently pin official Stable 1.5.4 package shell: bash run: | @@ -210,7 +210,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: Download byte-pinned published Stable v1.5.4 archive shell: bash run: | @@ -243,7 +243,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R554 download exact public v1.5.4 baseline shell: bash run: | @@ -272,7 +272,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R555 pin the official stable baseline from its immutable release shell: bash run: | @@ -301,7 +301,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R562 download exact official v1.5.4 installed baseline shell: bash run: | @@ -331,7 +331,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status - name: R565 download exact official v1.5.4 installed baseline shell: bash run: | @@ -376,7 +376,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -402,7 +402,7 @@ jobs: run: | set -euo pipefail python3 build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status test ! -e "$RUNNER_TEMP/DIM_R528_STABLE.zip" curl --fail --show-error --silent --location --retry 3 --max-time 120 \ 'https://github.com/GOD13emad/DirectInternetMethod/releases/download/v1.5.4/DirectInternetMethod_1.5.4_Linux_x86_64.zip' \ @@ -653,7 +653,7 @@ jobs: run: | set -euo pipefail python3 -B build_linux_package.py - printf '%s %s\n' '16009B39037D77399B41BB89DE86B48F8E0841A2AA4A1BB16FDE481180FDD2CF' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status + printf '%s %s\n' 'F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6' 'delivery/DirectInternetMethod_1.5.7_Linux_x86_64.zip' | sha256sum --check --status printf '%s %s\n' 'D0181C15C54B90A3B6E03FBFBE32148341037AB60CF4D6788B6A72140304340F' 'tools/security/privileged_entry.py' | sha256sum --check --status - name: Real root-owned bootstrap staged freeze and bad-manifest fail closed shell: bash diff --git a/linux/system/upgrade_guard.py b/linux/system/upgrade_guard.py index 3707645a..c6e8e419 100644 --- a/linux/system/upgrade_guard.py +++ b/linux/system/upgrade_guard.py @@ -27,8 +27,8 @@ BASELINE="1.5.2" # legacy accepted baseline OLD_USER="1.5.4" # legacy accepted baseline ALLOWED_BASELINE_PAIRS=(("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"),("1.5.6","1.5.6")) -PIN_ROOT="7147AF90E6DC0B29A6CE15A84656CE9587060A7F5F343E7B16281C5602FC693C" -PIN_USER="5077ACE07B27DDBCBEAA4DE56ED718F7FD26934FF65A13251CCEAFA9DD2FEB50" +PIN_ROOT="239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" +PIN_USER="69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" GUARD=Path("/var/lib/directinternetmethod/upgrade-guard") BASELINE_FILE=GUARD/"baseline.json" RESULT=GUARD/"rollback-result.json" diff --git a/linux/system/upgrade_system.sh b/linux/system/upgrade_system.sh index e52de5ea..b146e984 100644 --- a/linux/system/upgrade_system.sh +++ b/linux/system/upgrade_system.sh @@ -6,7 +6,7 @@ set -Eeuo pipefail umask 077 SRC="${1:-}" REQ_UID="${2:-}" -TX_SHA="7147AF90E6DC0B29A6CE15A84656CE9587060A7F5F343E7B16281C5602FC693C" +TX_SHA="239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" [ "$(id -u)" = 0 ] || { echo 'ROOT_REQUIRED' >&2; exit 77; } case "$REQ_UID" in ''|*[!0-9]*) echo 'UID_INVALID' >&2; exit 77;; esac [ "$REQ_UID" -gt 0 ] && [ "$REQ_UID" -lt 2147483648 ] || exit 77 @@ -34,13 +34,13 @@ assert p.is_file() and not p.is_symlink() s=p.stat() assert s.st_uid==0 and stat.S_IMODE(s.st_mode)==0o644 m=json.loads(p.read_text('utf8')) -assert m['schema']==1 and m['version'] in ('1.5.2','1.5.5-rc1','1.5.7') and m['uid']==expected +assert m['schema']==1 and m['version'] in ('1.5.2','1.5.5-rc1','1.5.6') and m['uid']==expected assert m['home'].startswith('/home/') PY # Arm the separately root-owned boot-persistent R553 guard BEFORE any # protected Root Stage/Apply. Source identity is a frozen independent SHA; # a signed-in OS administrator still authenticates through the installer. -GUARD_SHA="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" +GUARD_SHA="66B683E14FCDEC8D62CDFD2879AAB29D9DE0FD778DD67E0522BFB1218DC122D0" [ -f "$SRC/system/upgrade_guard.py" ] && [ ! -L "$SRC/system/upgrade_guard.py" ] || { echo "R553_GUARD_SOURCE_MISSING_OR_SYMLINK" >&2; exit 87; } @@ -101,14 +101,14 @@ try: assert data["schema"]==1 and data["revision"]=="R552" assert data["uid"]==uid and metadata["uid"]==uid and data["home"]==str(home) assert (data["versionFromRoot"],data["versionFromUser"]) in ( - ("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"),("1.5.7","1.5.7")) + ("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"),("1.5.6","1.5.6")) assert metadata["version"]==data["versionFromRoot"] assert data["versionTo"]=="1.5.7" assert data["oldRootMetaSha"]==sha(root) assert data["oldUserMetaSha"]==sha(home/".local/share/DirectInternetMethod/INSTALL.json") assert data["rootTransactionSha"]==root_sha assert sha(guard/"root_tx.py")==root_sha - assert sha(guard/"user_tx.py")=="5077ACE07B27DDBCBEAA4DE56ED718F7FD26934FF65A13251CCEAFA9DD2FEB50" + assert sha(guard/"user_tx.py")=="69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" assert data["userTransactionSha"]==sha(guard/"user_tx.py") assert data["workerSha"]==sha(guard/"worker.py") pending=home/".local/state/DirectInternetMethod/update-pending" diff --git a/tests/r519_versioned_package_contract.py b/tests/r519_versioned_package_contract.py index f9214212..c53e2d5c 100644 --- a/tests/r519_versioned_package_contract.py +++ b/tests/r519_versioned_package_contract.py @@ -14,8 +14,8 @@ ROOT_TXN=R/"linux/system/protected_upgrade_transaction.py" INSTALLER=R/"linux/install.sh" USER_TXN=R/"linux/app/user_update_transaction.py" -PKG_DOC=R/"docs/README_PACKAGE_156.md" -EXPECTED={"version":VERSION,"files":28,"sha256":"A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D"} +PKG_DOC=R/"docs/README_PACKAGE_157.md" +EXPECTED={"version":VERSION,"files":28,"sha256":"F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6"} def digest(data):return hashlib.sha256(data).hexdigest().upper() class Tests(unittest.TestCase): @classmethod @@ -61,7 +61,7 @@ def test_r519b_uninstaller_accepts_only_new_ownership_version(self): got=self.info.read(self.prefix+rel) declared=next(row for row in self.rows if row["file"]==rel) self.assertEqual(digest(got),declared["sha256"]) - self.assertEqual(declared["sha256"],"6BFD45BA87CE052D020D26BE5B6E0F589DF1B848D202D716AFD8FB87FD1FDE9A") + self.assertEqual(declared["sha256"],"869EA1CABA79D36A4987BE97868A4A7378B64987F5CC192255BECDB751020E62") self.assertIn('"socket_fallback.py"', got.decode()) self.assertIn('doc.get("version") == "1.5.7"',got.decode()) self.assertNotIn('doc.get("version") == "1.5.4"',got.decode()) @@ -96,7 +96,7 @@ def test_original_unreleased_candidate_preserved_and_current_source_delta(self): self.assertEqual(after["system/encrypted_selective_dns.py"]["sha256"], "A8A8DD6155DDC4E2C5295A6CAA7B8809144C43985B47FD4664501ED683DB0AAE") self.assertEqual(after["app/direct_internet_method.py"]["sha256"], - "AC4EF0EBD88B62B730B1309AD25B04876DED2E00AF429C369E02EE087F7F377B") + "6D60986F06D6239FB03CE58B4CA496463D77D40B89938F2932F1C52834475D71") self.assertIn("encrypted=encrypted_dns_optin_state()", self.info.read(self.prefix+"app/direct_internet_method.py").decode()) self.assertIn('s.get("selectiveDnsEncrypted")', diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index 9c806b04..d44a1966 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,8 +36,8 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "7147af90e6dc0b29a6ce15a84656ce9587060a7f5f343e7b16281c5602fc693c" -USER_SHA = "5077ace07b27ddbcbeaa4de56ed718f7fd26934ff65a13251cceafa9dd2feb50" +ROOT_SHA = "239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" +USER_SHA = "69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" def sha(p:Path)->str: h=hashlib.sha256() diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index dea5d096..85120ad7 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,8 +25,8 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "7147af90e6dc0b29a6ce15a84656ce9587060a7f5f343e7b16281c5602fc693c" -PIN_USER = "5077ace07b27ddbcbeaa4de56ed718f7fd26934ff65a13251cceafa9dd2feb50" +PIN_ROOT = "239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" +PIN_USER = "69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" def digest(path:Path): h=hashlib.sha256() diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 3ea98efa..66fdf2ec 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -2,7 +2,7 @@ """R552 source-extracted regression: worker/timer logic, ZERO installed actions.""" import hashlib,importlib.util,json,os,pathlib,tempfile,types,unittest SRC=pathlib.Path(__file__).resolve().parents[1]/"linux/system/upgrade_guard.py" -SOURCE_SHA="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" +SOURCE_SHA="66B683E14FCDEC8D62CDFD2879AAB29D9DE0FD778DD67E0522BFB1218DC122D0" assert hashlib.sha256(SRC.read_bytes()).hexdigest().upper()==SOURCE_SHA spec=importlib.util.spec_from_file_location("r552_candidate",SRC) g=importlib.util.module_from_spec(spec) diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index e1d2f55e..e95838bd 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -30,9 +30,9 @@ ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" PIN_PACKAGE="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" -PIN_ROOT="7147AF90E6DC0B29A6CE15A84656CE9587060A7F5F343E7B16281C5602FC693C" -PIN_GUARD="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" -PIN_USER="5077ACE07B27DDBCBEAA4DE56ED718F7FD26934FF65A13251CCEAFA9DD2FEB50" +PIN_ROOT="239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" +PIN_GUARD="66B683E14FCDEC8D62CDFD2879AAB29D9DE0FD778DD67E0522BFB1218DC122D0" +PIN_USER="69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" NAME="directinternetmethod-upgrade-rollback" SEEN=Path("/var/lib/directinternetmethod/upgrade-guard/rollback-result.json") diff --git a/tests/r566_native_systemd_persistent_catchup.py b/tests/r566_native_systemd_persistent_catchup.py index 0721a662..22c36e15 100644 --- a/tests/r566_native_systemd_persistent_catchup.py +++ b/tests/r566_native_systemd_persistent_catchup.py @@ -23,7 +23,7 @@ ROOT=Path(__file__).resolve().parents[1] GUARD=ROOT/"linux/system/upgrade_guard.py" -PIN_GUARD="3A8E5C655A841D7614477E7D44FC74A9DAD1CAAB89462092C8FBF235AB126A83" +PIN_GUARD="66B683E14FCDEC8D62CDFD2879AAB29D9DE0FD778DD67E0522BFB1218DC122D0" def need(value,reason): if not value:raise RuntimeError("R566_SYSTEMD_FAIL_CLOSED_"+reason) diff --git a/tests/r845_private_156_to_157_root_forward_contract.py b/tests/r845_private_156_to_157_root_forward_contract.py index eef67667..6790d190 100644 --- a/tests/r845_private_156_to_157_root_forward_contract.py +++ b/tests/r845_private_156_to_157_root_forward_contract.py @@ -52,6 +52,9 @@ def test_unknown_version_denied(self): def test_version_pair_whitelist(self): s=(R/"linux/system/upgrade_guard.py").read_text() self.assertIn('("1.5.6","1.5.6")',s) + sh=(R/"linux/system/upgrade_system.sh").read_text() + self.assertIn('("1.5.6","1.5.6")',sh) + self.assertNotIn('("1.5.7","1.5.7")',sh) self.assertNotIn('("1.5.6","1.5.5-rc1")',s) if __name__=="__main__": tests=("test_forward_and_rollback","test_tampered_root_denied","test_unknown_version_denied","test_version_pair_whitelist") diff --git a/tests/r846_windows_candidate_hosted_build.ps1 b/tests/r846_windows_candidate_hosted_build.ps1 index 5653381e..cb59ac80 100644 --- a/tests/r846_windows_candidate_hosted_build.ps1 +++ b/tests/r846_windows_candidate_hosted_build.ps1 @@ -6,7 +6,7 @@ $Root=$env:GITHUB_WORKSPACE if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R846_GITHUB_HOST_ONLY'} if($env:GITHUB_REF -cnotin @('refs/heads/prep/r846-forward-157-native-gates') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R846_BRANCH_HEAD_AUTHORITY'} $r=Get-Content (Join-Path $Root 'windows\RELEASE.json') -Raw|ConvertFrom-Json -if($r.version -cne '1.5.7' -or $r.status -cne 'PRIVATE_WINDOWS_V156_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R846_RC1_DECLARATION'} +if($r.version -cne '1.5.7' -or $r.status -cne 'PRIVATE_WINDOWS_V157_SOURCE_CANDIDATE_UNPUBLISHED_UNPROVEN'){throw 'R846_RC1_DECLARATION'} $qa=Join-Path $env:RUNNER_TEMP 'DIM_R846_NATIVE' $gstage=Join-Path $qa 'gui' $sstage=Join-Path $qa 'service' @@ -46,7 +46,7 @@ if($LASTEXITCODE -ne 0){throw 'INNO_COMPILATION_FAILED'} if(!(Test-Path $installer) -or (Get-Item $installer).Length -lt 50000000){throw 'R846_INSTALLER_MISSING'} if([string](Get-AuthenticodeSignature $installer).Status -ne 'NotSigned'){throw 'R846_SIGNATURE_POLICY_CHANGED'} $report=[ordered]@{ - revision='R846';stage='BUILD';status='PASS_ISOLATED_HOSTED_V156_BUILD' + revision='R846';stage='BUILD';status='PASS_ISOLATED_HOSTED_V157_BUILD' head=$env:GITHUB_SHA;version='1.5.7';stable='v1.5.4' manifestCount=@($m.files).Count;manifestSha=(Get-FileHash (Join-Path $Root 'windows\manifest.json') -Algorithm SHA256).Hash guiSha=(Get-FileHash $gt -Algorithm SHA256).Hash diff --git a/tests/r846_windows_native_rollback.ps1 b/tests/r846_windows_native_rollback.ps1 index 075f9511..e8c571f1 100644 --- a/tests/r846_windows_native_rollback.ps1 +++ b/tests/r846_windows_native_rollback.ps1 @@ -25,7 +25,7 @@ $admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity] if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R846_ADMIN_TOKEN_REQUIRED'} if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R846_UNSAFE_REPLAY_OR_MISSING_BUILD'} $build=Get-Content $Build -Raw |ConvertFrom-Json -if($build.status -cne 'PASS_ISOLATED_HOSTED_V156_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R846_BUILD_AUTHORITY'} +if($build.status -cne 'PASS_ISOLATED_HOSTED_V157_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R846_BUILD_AUTHORITY'} if((Get-FileHash $New -Algorithm SHA256).Hash -cne $build.installerSha){throw 'R846_RC1_INSTALLER_SHA'} $report=[ordered]@{schema=1;revision='R846';status='FAIL_UNPROVEN';source=$env:GITHUB_SHA; runner='github-hosted-windows-2025';privateInstallerSha=$build.installerSha; @@ -112,7 +112,7 @@ try { Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'PRIVATE_CONFIG_PRESERVED_ROLLBACK' $after=Network;$report.networkAfter=$after Require ($before.defaultRoutes -ceq $after.defaultRoutes -and $before.dns -ceq $after.dns) 'NETWORK_BASELINE_EXACT' - $report.status='PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154' + $report.status='PASS_HOSTED_NATIVE_154_TO_157_AND_BACK_154' $report.singleUninstaller=$true;$report.customProviderUpgradePreserved=$true;$report.dataPreserved=$true } catch { $report.status='DEGRADED_UNRESOLVED' @@ -122,4 +122,4 @@ try { Write-Output ('R846_NATIVE_STATUS='+$report.status) Write-Output ('R846_RECEIPT_SHA256='+((Get-FileHash $Result -Algorithm SHA256).Hash)) } -if($report.status -ne 'PASS_HOSTED_NATIVE_154_TO_156_AND_BACK_154'){throw ('R846_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} +if($report.status -ne 'PASS_HOSTED_NATIVE_154_TO_157_AND_BACK_154'){throw ('R846_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} diff --git a/tools/security/privileged_entry.py b/tools/security/privileged_entry.py index b66464c7..5f3e11bc 100644 --- a/tools/security/privileged_entry.py +++ b/tools/security/privileged_entry.py @@ -25,7 +25,7 @@ VERSION = "1.5.7" PACKAGE = "DirectInternetMethod_1.5.7_Linux_x86_64" -MANIFEST_SHA256 = "1BDF1EE5C6C3A7C18623C31D16496C077022EDFB16A68163FD885F5D909DAF9A" +MANIFEST_SHA256 = "FE225338BE8FB9B71B347B1BFE2BB9AF4A4D5CDF1738C239FF00A90E21431C1B" TRUSTED_ENTRY = Path("/usr/local/libexec/directinternetmethod/privileged_entry.py") MAX_ITEM = 16 * 1024 * 1024 MAX_ALL = 48 * 1024 * 1024 From e64a0b3ca0bb3dd9baed6779193b4e27f3648326 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 14:37:17 +0330 Subject: [PATCH 184/188] fix(r846): preserve lowercase exact watchdog digest literals for independent SHA contract --- tests/r532_ephemeral_root_watchdog_contract.py | 4 ++-- tests/r532_root_watchdog_worker.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index d44a1966..a0ac41c3 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -36,8 +36,8 @@ UNIT = "dim-r532-watchdog" TIMER = UNIT + ".timer" SERVICE = UNIT + ".service" -ROOT_SHA = "239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" -USER_SHA = "69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" +ROOT_SHA = "239a0c21eb4a9564799a97afe440c120668cedcd49412e33d0ac78c8866820e7" +USER_SHA = "69ae3a76cfcd3bb251546ccd5237f3af081ed0477323a8f95737c8a4bc87b6f8" def sha(p:Path)->str: h=hashlib.sha256() diff --git a/tests/r532_root_watchdog_worker.py b/tests/r532_root_watchdog_worker.py index 85120ad7..6b1cebb3 100644 --- a/tests/r532_root_watchdog_worker.py +++ b/tests/r532_root_watchdog_worker.py @@ -25,8 +25,8 @@ RESULT = GUARD / "timer-return.json" ROOT_HELPER = GUARD / "root_tx.py" USER_HELPER = GUARD / "user_tx.py" -PIN_ROOT = "239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" -PIN_USER = "69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" +PIN_ROOT = "239a0c21eb4a9564799a97afe440c120668cedcd49412e33d0ac78c8866820e7" +PIN_USER = "69ae3a76cfcd3bb251546ccd5237f3af081ed0477323a8f95737c8a4bc87b6f8" def digest(path:Path): h=hashlib.sha256() From 4940e16246c3143bc1392cd72c2e71b647c5fa29 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 14:59:04 +0330 Subject: [PATCH 185/188] test(r847): assert private 1.5.6 rooted pair and 1.5.7 forward guard, retain mixed fail-closed --- tests/r552_native_upgrade_guard_contract.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/r552_native_upgrade_guard_contract.py b/tests/r552_native_upgrade_guard_contract.py index 66fdf2ec..f45bb74b 100644 --- a/tests/r552_native_upgrade_guard_contract.py +++ b/tests/r552_native_upgrade_guard_contract.py @@ -397,17 +397,20 @@ def test_28_rc1_previous_stage_must_use_exact_declared_pair(self): g.staged_user_manifest(self.home,os.getuid(),installed) accepted=g.staged_user_manifest( self.home,os.getuid(),installed,"1.5.5-rc1") - self.assertEqual(accepted["version"],"1.5.6") + self.assertEqual(accepted["version"],"1.5.7") self.assertEqual(accepted["previousVersion"],"1.5.5-rc1") def test_29_only_correlated_root_and_user_versions_allowed(self): self.assertEqual( g.ALLOWED_BASELINE_PAIRS, - (("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"))) + (("1.5.2","1.5.4"),("1.5.5-rc1","1.5.5-rc1"),("1.5.6","1.5.6"))) for mixed in (("1.5.2","1.5.5-rc1"), ("1.5.5-rc1","1.5.4"), ("1.5.3","1.5.4"), - ("1.5.5-rc1","1.5.6")): + ("1.5.5-rc1","1.5.6"), + ("1.5.6","1.5.5-rc1"), + ("1.5.6","1.5.4"), + ("1.5.7","1.5.7")): self.assertNotIn(mixed,g.ALLOWED_BASELINE_PAIRS) def test_10_readiness_worker_does_not_query_elapsed_active_timer(self): From 34ab45eca15b964f1dbccf31562d5f2070258d9f Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 15:03:00 +0330 Subject: [PATCH 186/188] test(r847): pin native ephemeral gates to verified v1.5.7 candidate with unchanged root reject guards --- tests/r528_ephemeral_native_root_cli_contract.py | 14 +++++++------- tests/r529_ephemeral_root_user_transaction.py | 16 ++++++++-------- tests/r532_ephemeral_root_watchdog_contract.py | 8 ++++---- tests/r554_native_guard_ephemeral_contract.py | 14 +++++++------- tests/r567_hosted_denied_polkit_rollback.py | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/r528_ephemeral_native_root_cli_contract.py b/tests/r528_ephemeral_native_root_cli_contract.py index 8a0de900..6b6b471e 100644 --- a/tests/r528_ephemeral_native_root_cli_contract.py +++ b/tests/r528_ephemeral_native_root_cli_contract.py @@ -23,11 +23,11 @@ BASE=Path(__file__).resolve().parents[1] STABLE_SHA="4784fcd44ed3d1b40103bca140b2c0066784f794d07c3ae1f525334766a553c7" -CANDIDATE_SHA="a8356107c5de6529e8270d1ee404a7d8a629040bd0d17f3f83c61c390790655d" +CANDIDATE_SHA="f5506e1ae0092a20071b8d63e770adbf6cde3aa0701cc6a1b4f1e925c74cafb6" STABLE_ARCHIVE="DirectInternetMethod_1.5.4_Linux_x86_64.zip" -CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.6_Linux_x86_64.zip" +CANDIDATE_ARCHIVE="DirectInternetMethod_1.5.7_Linux_x86_64.zip" STABLE_PREFIX="DirectInternetMethod_1.5.4_Linux_x86_64/" -CANDIDATE_PREFIX="DirectInternetMethod_1.5.6_Linux_x86_64/" +CANDIDATE_PREFIX="DirectInternetMethod_1.5.7_Linux_x86_64/" SOURCE=BASE/"linux/system/protected_upgrade_transaction.py" HOSTED_WORKSPACE=Path("/home/runner/work/DirectInternetMethod/DirectInternetMethod") HOSTED_TEMP=Path("/home/runner/work/_temp") @@ -127,7 +127,7 @@ def stage_exact_candidate(candidate_zip:Path,temp:Path)->Path: with zipfile.ZipFile(candidate_zip) as z: archive_verify(z,CANDIDATE_PREFIX,30) manifest=json.loads(z.read(CANDIDATE_PREFIX+"PACKAGE_MANIFEST.json")) - need(manifest.get("version")=="1.5.6" and len(manifest.get("files",[]))==28, + need(manifest.get("version")=="1.5.7" and len(manifest.get("files",[]))==28, "RC1_PACKAGE_VERSION_MANIFEST") for f in manifest["files"]: relative=f["file"] @@ -189,7 +189,7 @@ def run_native(): "hosts":fsha(Path("/etc/hosts")), } m=module() - need(m.TARGET_VERSION=="1.5.6" and + need(m.TARGET_VERSION=="1.5.7" and m.CURRENT_BACKEND_VERSION=="1.5.2","NATIVE_PRODUCT_VERSION") source=stage_exact_candidate(candidate,temp) baseline=seed_real_protected_root(stable,user,m) @@ -212,7 +212,7 @@ def run_native(): stat.S_IMODE(file.stat().st_mode)==mode, "REAL_ROOT_UPGRADED_FILE_"+name) current=json.loads(Path("/var/lib/directinternetmethod/install.json").read_text()) - need(current["version"]=="1.5.6","REAL_ROOT_METADATA_NOT_ACTIVATED") + need(current["version"]=="1.5.7","REAL_ROOT_METADATA_NOT_ACTIVATED") try: t.commit(temp/"NO_NATIVE_ACCEPTANCE_RECEIPT.json") raise RuntimeError("R528_BAD_ACCEPTANCE_WAS_ALLOWED") @@ -246,7 +246,7 @@ def run_native(): "productionMachineTouched":False, "nativeRoot":True,"effectiveUid":os.geteuid(),"protectedPreVersion":"1.5.2", "protectedPostRollbackVersion":"1.5.2", - "protectedCandidateAppliedBeforeRollback":"1.5.6", + "protectedCandidateAppliedBeforeRollback":"1.5.7", "publicStablePreserved":"v1.5.4","pinnedSource":CANDIDATE_SHA, "pinnedPublishedStable":STABLE_SHA, "rootOwnedFilesRestored":len(baseline), diff --git a/tests/r529_ephemeral_root_user_transaction.py b/tests/r529_ephemeral_root_user_transaction.py index c5d9ff7a..21e37f90 100644 --- a/tests/r529_ephemeral_root_user_transaction.py +++ b/tests/r529_ephemeral_root_user_transaction.py @@ -2,7 +2,7 @@ """R529: native Root + unprivileged User transaction across one disposable VM. Not a physical network/GUI acceptance. Use exact v1.5.4 published ZIP user and -protected Root code, exact 1.5.6 candidate ZIP, unmodified RootTransaction("/") +protected Root code, exact 1.5.7 candidate ZIP, unmodified RootTransaction("/") and real subprocess-driven user transaction / inline INSTALL.json production fragment. Both transactions must rollback byte-for-byte. NO live network or systemd, No Tailscale changes, no installation onto a user's physical computer. @@ -228,7 +228,7 @@ def execute(): as_runner(user, tx_script, "--stage", "--source", extracted) require((pending / "manifest.json").is_file(), "USER_PENDING_STAGE") manifest=json.loads((pending / "manifest.json").read_text()) - require(manifest["version"]=="1.5.6" and + require(manifest["version"]=="1.5.7" and manifest["providerManaged"] is False and manifest["providerSha"] is None, "CUSTOM_USER_PROVIDER_OWNERSHIP") @@ -237,7 +237,7 @@ def execute(): # Protected absolute-root stage/apply with actual root-owned files. require(root_tx.stage(extracted)["status"]=="STAGED", "ROOT_STAGE") require(root_tx.apply()["status"]=="APPLIED", "ROOT_APPLY") - require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", "ROOT_NEW_METADATA") + require(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.7", "ROOT_NEW_METADATA") # Real unprivileged User transaction CLI replaces managed code only. as_runner(user, tx_script, "--apply", "--source", extracted) for rel,row in manifest["managed"].items(): @@ -262,12 +262,12 @@ def execute(): inline=tail.split(end,1)[0] require(len(inline.splitlines())==64 and 'transaction_helper.atomic_bytes(' in inline and - 'assert protected.get("version") == "1.5.6"' in inline, + 'assert protected.get("version") == "1.5.7"' in inline, "INSTALLER_METADATA_PROVENANCE_CHANGED") as_runner_inline(user,inline,app/"INSTALL.json",tx_script) record=json.loads((app/"INSTALL.json").read_text()) - require(record["version"]=="1.5.6" and - record["protectedBackendVersion"]=="1.5.6", + require(record["version"]=="1.5.7" and + record["protectedBackendVersion"]=="1.5.7", "TWO_SIDED_VERSION_METADATA") require(record["managedProviderSha256"] is None and record["preservedCustomProviderData"] is True and @@ -299,11 +299,11 @@ def execute(): print(json.dumps({ "revision":"R529", "status":"PASS_HOSTED_ROOT_USER_ACTUAL_TRANSACTION", "productionMachineTouched":False,"hostedRunner":True, - "publicStable":"v1.5.4","candidate":"1.5.6", + "publicStable":"v1.5.4","candidate":"1.5.7", "userTransaction":"STAGE_APPLY_INLINE_ATOMIC_METADATA_ROLLBACK", "protectedRoot":"ABSOLUTE_ROOT_STAGE_APPLY_ROLLBACK", "preVersions":"user1.5.4/root1.5.2", - "appliedVersion":"both1.5.6", + "appliedVersion":"both1.5.7", "postRollbackVersions":"user1.5.4/root1.5.2", "oldUserCodeFilesVerified":len(old_user), "protectedRootFilesVerified":len(root_before), diff --git a/tests/r532_ephemeral_root_watchdog_contract.py b/tests/r532_ephemeral_root_watchdog_contract.py index a0ac41c3..75e9c701 100644 --- a/tests/r532_ephemeral_root_watchdog_contract.py +++ b/tests/r532_ephemeral_root_watchdog_contract.py @@ -3,7 +3,7 @@ Both invocations are separate CI steps on an ephemeral GitHub-hosted Ubuntu VM. The first starts a detached systemd monotonic timer BEFORE any candidate file -mutation, leaves actual protected Root+unprivileged User 1.5.6 staged and +mutation, leaves actual protected Root+unprivileged User 1.5.7 staged and applied, and EXITs. The second waits for the independent timer and verifies byte-exact restoration to 1.5.2 Root / 1.5.4 User, without running rollback. All installed products on physical hosts and network services are untouched. @@ -180,9 +180,9 @@ def arm(): r529.as_runner(user,user_script,"--apply","--source",extracted) r529.as_runner_inline(user,exact_inline(r526,extracted), app/"INSTALL.json",user_script) - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.7", "NEW_USER_METADATA") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.7", "NEW_ROOT_METADATA") need(root_tx.status()["status"]=="APPLIED","ROOT_APPLIED_PENDING") need((Path("/home/runner/.local/state/DirectInternetMethod/update-pending/manifest.json")).is_file(), @@ -192,7 +192,7 @@ def arm(): print("PASS_R532_WATCHDOG_ARMED_BEFORE_MUTATION_THEN_MAIN_EXIT") print("WORKER_ROOT_OWNED_SHA",doc["workerSHA256"]) print("TIMER_UNIT",TIMER,"SERVICE_UNIT",SERVICE) - print("PRE_TIMER_VERSIONS", "root=1.5.6","user=1.5.6") + print("PRE_TIMER_VERSIONS", "root=1.5.7","user=1.5.7") def verify(): need(GUARD.is_dir() and GUARD.stat().st_uid==0,"EXPECTED_GUARD_NOT_FOUND") diff --git a/tests/r554_native_guard_ephemeral_contract.py b/tests/r554_native_guard_ephemeral_contract.py index e95838bd..7c8c66b3 100644 --- a/tests/r554_native_guard_ephemeral_contract.py +++ b/tests/r554_native_guard_ephemeral_contract.py @@ -29,7 +29,7 @@ R526=R/"tests/r526_installer_metadata_contract.py" ROOT=R/"linux/system/protected_upgrade_transaction.py" GUARD=R/"linux/system/upgrade_guard.py" -PIN_PACKAGE="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" +PIN_PACKAGE="F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6" PIN_ROOT="239A0C21EB4A9564799A97AFE440C120668CEDCD49412E33D0AC78C8866820E7" PIN_GUARD="66B683E14FCDEC8D62CDFD2879AAB29D9DE0FD778DD67E0522BFB1218DC122D0" PIN_USER="69AE3A76CFCD3BB251546CCD5237F3AF081ED0477323A8F95737C8A4BC87B6F8" @@ -174,9 +174,9 @@ def run(): need("transaction_helper.atomic_bytes(" in inline and len(inline.splitlines())==64,"INSTALL_INLINE_IDENTITY") r529.as_runner_inline(user,inline,app/"INSTALL.json",tx_user) - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.7", "UPGRADED_USER_VERSION") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.7", "UPGRADED_ROOT_VERSION") need(root_tx.status()["status"]=="APPLIED","ROOT_STILL_PENDING") # R562 tests interruption AFTER a deliberately simulated protected Root @@ -254,8 +254,8 @@ def run(): need(root_tx.status()["status"]=="COMMITTED" and root_tx.pending.is_dir() and pending.is_dir(), "ACCEPTED_ROOT_OR_USER_BACKUP_WAS_REVERTED") - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6" - and json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.7" + and json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.7", "COMMITTED_VERSION_CHANGED_ON_FAILED_FINALIZATION") for rel,row in manifest["managed"].items(): dest=app/rel @@ -283,9 +283,9 @@ def run(): "ACCEPTED_ROOT_USER_COMMIT_NOT_RECONCILED") need(root_tx.status()["status"]=="COMMITTED","ACCEPTED_ROOT_PHASE_LOST") need(not pending.exists(),"USER_BACKUP_NOT_RELEASED") - need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.6", + need(json.loads((app/"INSTALL.json").read_text())["version"]=="1.5.7", "COMMITTED_USER_VERSION") - need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.6", + need(json.loads(Path("/var/lib/directinternetmethod/install.json").read_text())["version"]=="1.5.7", "COMMITTED_ROOT_VERSION") installed_manifest=json.loads((app/"INSTALL.json").read_text()) for rel,row in manifest["managed"].items(): diff --git a/tests/r567_hosted_denied_polkit_rollback.py b/tests/r567_hosted_denied_polkit_rollback.py index 5427f557..9f829762 100644 --- a/tests/r567_hosted_denied_polkit_rollback.py +++ b/tests/r567_hosted_denied_polkit_rollback.py @@ -23,7 +23,7 @@ BASE=Path(__file__).resolve().parents[1] R528=BASE/"tests/r528_ephemeral_native_root_cli_contract.py" R529=BASE/"tests/r529_ephemeral_root_user_transaction.py" -ARCHIVE="A8356107C5DE6529E8270D1EE404A7D8A629040BD0D17F3F83C61C390790655D" +ARCHIVE="F5506E1AE0092A20071B8D63E770ADBF6CDE3AA0701CC6A1B4F1E925C74CAFB6" def require(v,code): if not v: raise RuntimeError("R567_DENIED_AUTH_FAIL_CLOSED_"+code) From 1ab328f899a4b9e350cd1e8063489a542b377c79 Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 15:45:54 +0330 Subject: [PATCH 187/188] test(r848-hosted-156-to-157-once): hosted private 1.5.6 -> 1.5.7 in-place and rollback against pinned original installer --- .github/workflows/ci.yml | 41 ++++++ tests/dotnet10_target_contract.py | 2 +- ...r848_windows_private156_forward_native.ps1 | 132 ++++++++++++++++++ ...ndows_private156_native_source_contract.py | 25 ++++ 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 tests/r848_windows_private156_forward_native.ps1 create mode 100644 tests/r848_windows_private156_native_source_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2660f276..ea40c428 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -707,6 +707,47 @@ jobs: if-no-files-found: error retention-days: 7 + windows-private-r848-v156-forward: + name: windows-private-r848-v156-forward-one-time + # Historical original v1.5.6 artifact expires 2026-11-01. + # Only the one-time R848 commit runs this; later pushes skip safely. + if: github.event_name == 'push' && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/prep/r846-forward-157-native-gates' && contains(github.event.head_commit.message, 'r848-hosted-156-to-157-once') + needs: windows-source + runs-on: windows-2025 + timeout-minutes: 75 + permissions: + contents: read + actions: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6 + with: + dotnet-version: '10.0.x' + - name: R848 build verified private v1.5.7 from current exact SHA + shell: pwsh + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/r846_windows_candidate_hosted_build.ps1 + if($LASTEXITCODE -ne 0){throw 'R848_BUILD_FAILED'} + - name: R848 original private 1.5.6 to 1.5.7 native and back + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + $ErrorActionPreference='Stop' + & pwsh -NoProfile -NonInteractive -File tests/r848_windows_private156_forward_native.ps1 + if($LASTEXITCODE -ne 0){throw 'R848_PRIVATE_NATIVE_FAILED'} + - name: Retain R848 one-time Windows native private upgrade evidence + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: r848-private156-to-157-native-${{ github.run_id }} + path: | + ${{ runner.temp }}/DIM_R846_NATIVE/BUILD_R846.json + ${{ runner.temp }}/DIM_R848_NATIVE/NATIVE_R848.json + if-no-files-found: warn + retention-days: 7 + windows-release-contract: name: windows-release-contract-unsigned if: startsWith(github.ref, 'refs/tags/v') diff --git a/tests/dotnet10_target_contract.py b/tests/dotnet10_target_contract.py index 34ff74b6..e79cbe1a 100644 --- a/tests/dotnet10_target_contract.py +++ b/tests/dotnet10_target_contract.py @@ -63,7 +63,7 @@ def negative_first(): def main(): negative_first() ci=CI.read_text("utf-8") - expected_dotnet_jobs = 3 if CURRENT=="1.5.7" else (2 if CURRENT in ("1.5.5-rc1","1.5.6") else 1) + expected_dotnet_jobs = 4 if CURRENT=="1.5.7" else (2 if CURRENT in ("1.5.5-rc1","1.5.6") else 1) assert ci.count("dotnet-version: '10.0.x'")==expected_dotnet_jobs,"SDK_10_JOB_PIN_COUNT" if CURRENT in ("1.5.5-rc1","1.5.6","1.5.7"): assert ci.count(" windows-private-r535-native:")==1 diff --git a/tests/r848_windows_private156_forward_native.ps1 b/tests/r848_windows_private156_forward_native.ps1 new file mode 100644 index 00000000..980fa70b --- /dev/null +++ b/tests/r848_windows_private156_forward_native.ps1 @@ -0,0 +1,132 @@ +[CmdletBinding()] +param() +$ErrorActionPreference='Stop' +Set-StrictMode -Version Latest +$Root=$env:GITHUB_WORKSPACE +$Dir=Join-Path $env:RUNNER_TEMP 'DIM_R848_NATIVE' +$Build=Join-Path $env:RUNNER_TEMP 'DIM_R846_NATIVE\BUILD_R846.json' +$Result=Join-Path $Dir 'NATIVE_R848.json' +$Old=Join-Path $Dir 'DirectInternetMethod_1.5.6_Windows_Setup.exe' +$New=Join-Path $Root 'delivery\DirectInternetMethod_1.5.7_Windows_Setup.exe' +$App=Join-Path $env:LOCALAPPDATA 'Programs\DirectInternetMethod' +$Priv=Join-Path $env:ProgramFiles 'DirectInternetMethod\Privileged' +$Config=Join-Path $env:ProgramData 'DirectInternetMethod\UserConfig\custom-hosts.txt' +$Provider=Join-Path $App 'app\router_gateway\providers.json' +$Evidence=Join-Path $App 'evidence\R848_KEEP.txt' +$Gui=Join-Path $App 'app\DirectInternetMethod.exe' +$Svc=Join-Path $Priv 'app\DirectInternetMethod.Service.exe' +$Key='HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1' +$PublicSha='5BDE9DFFC3B866A7754DABEF9EDC853CFE519A08944163C39D812B2C8E9F3620' +$PublicGui='0CC4270B507BC9A141B4012378EE28D42C0EE8C5509E03A9ED3FB8C73B9865A3' +$PublicSvc='32AA6E88F57097D669D395999ED23A8AA85C2A330C41C542D1CB44A744C031FB' +if($env:CI -ne 'true' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted' -or $env:GITHUB_REPOSITORY -cne 'GOD13emad/DirectInternetMethod'){throw 'R848_HOSTED_VM_ONLY'} +if($env:GITHUB_REF -cnotin @('refs/heads/prep/r846-forward-157-native-gates') -or (git -C $Root rev-parse HEAD).Trim() -cne $env:GITHUB_SHA){throw 'R848_BRANCH_HEAD_DRIFT'} +$admin=[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() +if(!$admin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){throw 'R848_ADMIN_TOKEN_REQUIRED'} +if(!(Test-Path $Build) -or (Test-Path $Result) -or (Test-Path $Old)){throw 'R848_UNSAFE_REPLAY_OR_MISSING_BUILD'} +if([string]::IsNullOrWhiteSpace($env:GH_TOKEN)){throw 'R848_GH_TOKEN_ACTIONS_READ_REQUIRED'} +$build=Get-Content $Build -Raw |ConvertFrom-Json +if($build.status -cne 'PASS_ISOLATED_HOSTED_V157_BUILD' -or $build.head -cne $env:GITHUB_SHA){throw 'R848_BUILD_AUTHORITY'} +if((Get-FileHash $New -Algorithm SHA256).Hash -cne $build.installerSha){throw 'R848_RC1_INSTALLER_SHA'} +$report=[ordered]@{schema=1;revision='R848';status='FAIL_UNPROVEN';source=$env:GITHUB_SHA; + runner='github-hosted-windows-2025';privateInstallerSha=$build.installerSha; + previousPrivateSha=$PublicSha;noReboot=$true;noPhysicalMachines=$true; + checks=@();errors=@()} +function Require([bool]$ok,[string]$name) { + if(!$ok){throw ('R848_FAIL_CLOSED_'+$name)} + $report.checks+=($name+':PASS') +} +function FileSha([string]$p){ (Get-FileHash -LiteralPath $p -Algorithm SHA256).Hash } +function Network { + [ordered]@{ + defaultRoutes=(@(Get-NetRoute -DestinationPrefix '0.0.0.0/0' | + Select-Object InterfaceIndex,NextHop,RouteMetric|Sort-Object InterfaceIndex,NextHop)|ConvertTo-Json -Depth 5 -Compress) + dns=(@(Get-DnsClientServerAddress -AddressFamily IPv4| + Select-Object InterfaceIndex,ServerAddresses|Sort-Object InterfaceIndex)|ConvertTo-Json -Depth 5 -Compress)} +} +function Install([string]$exe,[string]$step) { + $args=@('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART','/NORESTARTAPPLICATIONS','/CLOSEAPPLICATIONS','/SP-') + $p=Start-Process -FilePath $exe -ArgumentList $args -Wait -PassThru -NoNewWindow + Require ($p.ExitCode -eq 0) ($step+'_EXIT') +} +function ExactRegisteredUninstaller([object]$d) { + # Inno Setup allocates uninsNNN per directory, not per version. Inspect + # the real AppId registration and require just one corresponding file pair. + $entries=@(Get-ChildItem -LiteralPath $App -Force -ErrorAction Stop | Where-Object { $_.Name -match '^unins[0-9]{3}[.](exe|dat|msg)$' }) + $exes=@($entries|Where-Object { $_.Extension -ieq '.exe' }) + $datas=@($entries|Where-Object { $_.Extension -ieq '.dat' }) + $msgs=@($entries|Where-Object { $_.Extension -ieq '.msg' }) + Require ($exes.Count -eq 1 -and $datas.Count -eq 1 -and $msgs.Count -le 1) 'ONE_UNINSTALLER' + $stem=[IO.Path]::GetFileNameWithoutExtension($exes[0].Name) + Require ($stem -ieq [IO.Path]::GetFileNameWithoutExtension($datas[0].Name)) 'MATCHED_UNINSTALL_LOG' + Require ($msgs.Count -eq 0 -or $stem -ieq [IO.Path]::GetFileNameWithoutExtension($msgs[0].Name)) 'MATCHED_UNINSTALL_MSG' + foreach($item in $entries) { + Require (!$item.PSIsContainer -and ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -eq 0) 'UNINSTALLER_NODE_UNSAFE' + } + $path=[IO.Path]::GetFullPath($exes[0].FullName) + Require ([string]$d.UninstallString -ieq ('"'+$path+'"')) 'EXACT_UNINSTALL_PATH_OWNED' + return $path +} +function Registered([string]$version) { + Require (Test-Path $Key) 'SINGLE_HKLM_APPID' + $d=Get-ItemProperty -LiteralPath $Key + Require ($d.DisplayVersion -ceq $version) 'EXACT_REGISTERED_VERSION' + Require ($d.InstallLocation.TrimEnd('\') -ieq $App.TrimEnd('\')) 'SINGLE_INSTALL_PATH' + Require (!(Test-Path ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B5388E8B-9AF3-41F4-87E2-01C9601A36CB}_is1'))) 'NO_HKCU_DUPLICATE' + [void](ExactRegisteredUninstaller $d) + return $d +} +try { + Require (!(Test-Path $Key) -and !(Test-Path $App) -and !(Test-Path $Priv)) 'CLEAN_DISPOSABLE_VM' + $before=Network + $report.networkBefore=$before + $artifactJson=& gh api repos/GOD13emad/DirectInternetMethod/actions/artifacts/12969796101 --jq '{id: .id, name: .name, expired: .expired, run: .workflow_run.id}' + if($LASTEXITCODE -ne 0){throw 'R848_GITHUB_ARTIFACT_QUERY_FAILED'} + $artifact=$artifactJson|ConvertFrom-Json + Require ($artifact.id -eq 12969796101 -and $artifact.name -ceq 'r600-private-tested-v156-41038434425' -and $artifact.expired -eq $false -and $artifact.run -eq 41038434425) 'PRIVATE_156_ARTIFACT_PROVENANCE' + & gh run download 41038434425 -R GOD13emad/DirectInternetMethod -n r600-private-tested-v156-41038434425 -D $Dir + if($LASTEXITCODE -ne 0){throw 'R848_DOWNLOAD_PRIVATE_156_FAILED'} + Require (Test-Path $Old) 'PRIVATE_156_SETUP_DOWNLOADED' + Require ((Get-Item $Old).Length -eq 154257997 -and (FileSha $Old) -ceq $PublicSha) 'EXACT_PRIVATE_156_SETUP_PIN' + Install $Old 'PRIVATE_156_FRESH' + [void](Registered '1.5.6') + Require ((FileSha $Gui) -ceq $PublicGui -and (FileSha $Svc) -ceq $PublicSvc) 'EXACT_PRIVATE_156_GUI_AND_SERVICE' + New-Item -ItemType Directory -Path (Split-Path $Config -Parent) -Force|Out-Null + Require (!(Test-Path $Config)) 'NO_PREEXISTING_PRIVATE_CONFIG' + [IO.File]::WriteAllText($Config,'R848_PRIVATE_CUSTOM_SITE_SENTINEL') + $configSha=FileSha $Config + Require (Test-Path $Provider) 'PROVIDER_EXISTS' + [IO.File]::AppendAllText($Provider,[Environment]::NewLine+' ') + $providerSha=FileSha $Provider + New-Item -ItemType Directory -Path (Split-Path $Evidence -Parent) -Force|Out-Null + [IO.File]::WriteAllText($Evidence,'R848_USER_UNKNOWN_EVIDENCE_KEEP') + $evidenceSha=FileSha $Evidence + Install $New 'RC1_IN_PLACE' + [void](Registered '1.5.7') + Require ((FileSha $Gui) -ceq $build.guiSha -and (FileSha $Svc) -ceq $build.serviceSha) 'RC1_GUI_SERVICE_PIN' + Require ((FileSha (Join-Path $App 'manifest.json')) -ceq $build.manifestSha) 'RC1_MANIFEST_PIN' + Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.7') 'RC1_INSTALLED_VERSION' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' + $d=Registered '1.5.7' + $uninstaller=ExactRegisteredUninstaller $d + $u=Start-Process $uninstaller -ArgumentList @('/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART') -Wait -PassThru -NoNewWindow + Require ($u.ExitCode -eq 0) 'RC1_UNINSTALL_ZERO' + Require (!(Test-Path $Key)) 'ONE_UNINSTALLER_REMOVED_REGISTRY' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'UNKNOWN_USER_DATA_SURVIVED_UNINSTALL' + Install $Old 'PRIVATE_156_ROLLBACK_REINSTALL' + [void](Registered '1.5.6') + Require ((FileSha $Gui) -ceq $PublicGui -and (FileSha $Svc) -ceq $PublicSvc) 'EXACT_PRIVATE_156_BINARY_HASHES_RESTORED' + Require ((FileSha $Config) -ceq $configSha -and (FileSha $Evidence) -ceq $evidenceSha) 'PRIVATE_CONFIG_PRESERVED_ROLLBACK' + $after=Network;$report.networkAfter=$after + Require ($before.defaultRoutes -ceq $after.defaultRoutes -and $before.dns -ceq $after.dns) 'NETWORK_BASELINE_EXACT' + $report.status='PASS_HOSTED_NATIVE_156_TO_157_AND_BACK_156' + $report.singleUninstaller=$true;$report.customProviderUpgradePreserved=$true;$report.dataPreserved=$true +} catch { + $report.status='DEGRADED_UNRESOLVED' + $report.errors+=([string]$_.Exception.Message) +} finally { + [IO.File]::WriteAllText($Result,(ConvertTo-Json $report -Depth 9)+[Environment]::NewLine,[Text.UTF8Encoding]::new($false)) + Write-Output ('R848_NATIVE_STATUS='+$report.status) + Write-Output ('R848_RECEIPT_SHA256='+((Get-FileHash $Result -Algorithm SHA256).Hash)) +} +if($report.status -ne 'PASS_HOSTED_NATIVE_156_TO_157_AND_BACK_156'){throw ('R848_NATIVE_NOT_ACCEPTED_'+($report.errors -join ';'))} diff --git a/tests/r848_windows_private156_native_source_contract.py b/tests/r848_windows_private156_native_source_contract.py new file mode 100644 index 00000000..d0566545 --- /dev/null +++ b/tests/r848_windows_private156_native_source_contract.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +"""One-time exact original-repo hosted native private-to-private release bridge.""" +from pathlib import Path +import unittest +R=Path(__file__).resolve().parents[1] +WF=(R/".github/workflows/ci.yml").read_text() +SCRIPT=(R/"tests/r848_windows_private156_forward_native.ps1").read_text() +class R848(unittest.TestCase): + def test_provenance_and_hosted_authority(self): + for s in ("GOD13emad/DirectInternetMethod","github-hosted","refs/heads/prep/r846-forward-157-native-gates","GITHUB_SHA","GH_TOKEN","12969796101","41038434425","r600-private-tested-v156-41038434425"): + self.assertIn(s,SCRIPT) + def test_fixed_private_v156_asset_digests(self): + for s in ("5BDE9DFFC3B866A7754DABEF9EDC853CFE519A08944163C39D812B2C8E9F3620","0CC4270B507BC9A141B4012378EE28D42C0EE8C5509E03A9ED3FB8C73B9865A3","32AA6E88F57097D669D395999ED23A8AA85C2A330C41C542D1CB44A744C031FB"): + self.assertIn(s,SCRIPT) + def test_native_transaction_contract(self): + for s in ("PASS_HOSTED_NATIVE_156_TO_157_AND_BACK_156","IN_PLACE_USER_DATA_PRESERVED","ONE_UNINSTALLER","NETWORK_BASELINE_EXACT","EXACT_PRIVATE_156_BINARY_HASHES_RESTORED","DIM_R846_NATIVE"): + self.assertIn(s,SCRIPT) + self.assertNotIn("1.5.4",SCRIPT) + def test_strict_one_time_ci_and_token_permissions(self): + for s in ("windows-private-r848-v156-forward:","r848-hosted-156-to-157-once","actions: read","tests/r848_windows_private156_forward_native.ps1","windows-private-r846-native:"): + self.assertIn(s,WF) + self.assertIn("GH_TOKEN: "+chr(36)+"{{ github.token }}",WF) +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(R848)) + raise SystemExit(0 if result.wasSuccessful() else 1) From 7d587587349d011e6aa4df0c1b339b2d16813dfd Mon Sep 17 00:00:00 2001 From: Aliakbar Emadoddin <94546844+GOD13emad@users.noreply.github.com> Date: Sat, 31 Oct 2026 16:37:59 +0330 Subject: [PATCH 188/188] test(r850-hosted-integrity-once): assert all 674 installed privileged file hashes after private in-place upgrade --- .github/workflows/ci.yml | 2 +- tests/r846_windows_native_rollback.ps1 | 2 + ...r848_windows_private156_forward_native.ps1 | 2 + ...tive_installed_manifest_source_contract.py | 31 +++++++++++ ...indows_privileged_manifest_attestation.ps1 | 53 +++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/r850_windows_native_installed_manifest_source_contract.py create mode 100644 tests/r850_windows_privileged_manifest_attestation.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea40c428..3cb04307 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -711,7 +711,7 @@ jobs: name: windows-private-r848-v156-forward-one-time # Historical original v1.5.6 artifact expires 2026-11-01. # Only the one-time R848 commit runs this; later pushes skip safely. - if: github.event_name == 'push' && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/prep/r846-forward-157-native-gates' && contains(github.event.head_commit.message, 'r848-hosted-156-to-157-once') + if: github.event_name == 'push' && github.repository == 'GOD13emad/DirectInternetMethod' && github.ref == 'refs/heads/prep/r846-forward-157-native-gates' && (contains(github.event.head_commit.message, 'r848-hosted-156-to-157-once') || contains(github.event.head_commit.message, 'r850-hosted-integrity-once')) needs: windows-source runs-on: windows-2025 timeout-minutes: 75 diff --git a/tests/r846_windows_native_rollback.ps1 b/tests/r846_windows_native_rollback.ps1 index e8c571f1..2e3294ea 100644 --- a/tests/r846_windows_native_rollback.ps1 +++ b/tests/r846_windows_native_rollback.ps1 @@ -98,6 +98,8 @@ try { [void](Registered '1.5.7') Require ((FileSha $Gui) -ceq $build.guiSha -and (FileSha $Svc) -ceq $build.serviceSha) 'RC1_GUI_SERVICE_PIN' Require ((FileSha (Join-Path $App 'manifest.json')) -ceq $build.manifestSha) 'RC1_MANIFEST_PIN' + & pwsh -NoProfile -NonInteractive -File (Join-Path $Root 'tests/r850_windows_privileged_manifest_attestation.ps1') -UserRoot $App -PrivilegedRoot $Priv -ExpectedVersion '1.5.7' -ExpectedManifestSha256 $build.manifestSha -ExpectedManagedFiles 681 -ExpectedPrivilegedFiles 674 + Require ($LASTEXITCODE -eq 0) 'R850_INSTALLED_PRIVILEGED_674_SHA_MATCH' Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.7') 'RC1_INSTALLED_VERSION' Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' $d=Registered '1.5.7' diff --git a/tests/r848_windows_private156_forward_native.ps1 b/tests/r848_windows_private156_forward_native.ps1 index 980fa70b..2d3a3159 100644 --- a/tests/r848_windows_private156_forward_native.ps1 +++ b/tests/r848_windows_private156_forward_native.ps1 @@ -105,6 +105,8 @@ try { [void](Registered '1.5.7') Require ((FileSha $Gui) -ceq $build.guiSha -and (FileSha $Svc) -ceq $build.serviceSha) 'RC1_GUI_SERVICE_PIN' Require ((FileSha (Join-Path $App 'manifest.json')) -ceq $build.manifestSha) 'RC1_MANIFEST_PIN' + & pwsh -NoProfile -NonInteractive -File (Join-Path $Root 'tests/r850_windows_privileged_manifest_attestation.ps1') -UserRoot $App -PrivilegedRoot $Priv -ExpectedVersion '1.5.7' -ExpectedManifestSha256 $build.manifestSha -ExpectedManagedFiles 681 -ExpectedPrivilegedFiles 674 + Require ($LASTEXITCODE -eq 0) 'R850_INSTALLED_PRIVILEGED_674_SHA_MATCH' Require ((Get-Content (Join-Path $App 'RELEASE.json') -Raw|ConvertFrom-Json).version -ceq '1.5.7') 'RC1_INSTALLED_VERSION' Require ((FileSha $Config) -ceq $configSha -and (FileSha $Provider) -ceq $providerSha -and (FileSha $Evidence) -ceq $evidenceSha) 'IN_PLACE_USER_DATA_PRESERVED' $d=Registered '1.5.7' diff --git a/tests/r850_windows_native_installed_manifest_source_contract.py b/tests/r850_windows_native_installed_manifest_source_contract.py new file mode 100644 index 00000000..5ef38642 --- /dev/null +++ b/tests/r850_windows_native_installed_manifest_source_contract.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""R850 native installed manifest hardening in both historic baseline paths.""" +from pathlib import Path +import unittest +R=Path(__file__).resolve().parents[1] +PROBE=(R/"tests/r850_windows_privileged_manifest_attestation.ps1").read_text() +WF=(R/".github/workflows/ci.yml").read_text() +class Contract(unittest.TestCase): + def test_strict_exact_manifest_and_protected_file_sha(self): + for s in ("Get-FileHash","SHA256","ExpectedManifestSha256","FILE_REPARSE","PATH_ESCAPE", + "PRIVILEGED_SHA_MISMATCH","PRIVILEGED_COUNT","DUPLICATE_SCOPE_FILE", + "ExpectedManagedFiles=681","ExpectedPrivilegedFiles=674"): + self.assertIn(s,PROBE) + def test_exact_native_hosted_v154_and_v156_forward_suites(self): + for file in ("tests/r846_windows_native_rollback.ps1","tests/r848_windows_private156_forward_native.ps1"): + x=(R/file).read_text() + self.assertIn("R850_INSTALLED_PRIVILEGED_674_SHA_MATCH",x) + self.assertIn("r850_windows_privileged_manifest_attestation.ps1",x) + self.assertIn("-ExpectedVersion '1.5.7'",x) + self.assertIn("-ExpectedManifestSha256 $build.manifestSha",x) + def test_one_time_private_source_prevent_repeated_artifact_use(self): + self.assertIn("r850-hosted-integrity-once",WF) + self.assertIn("github.repository == 'GOD13emad/DirectInternetMethod'",WF) + self.assertIn("windows-private-r846-native:",WF) + self.assertIn("windows-private-r848-v156-forward:",WF) + def test_attestation_read_only(self): + for s in ("Set-Content","WriteAllText","Copy-Item","Remove-Item","Start-Process","Invoke-WebRequest","pkexec"): + self.assertNotIn(s,PROBE) +if __name__=="__main__": + result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Contract)) + raise SystemExit(0 if result.wasSuccessful() else 1) diff --git a/tests/r850_windows_privileged_manifest_attestation.ps1 b/tests/r850_windows_privileged_manifest_attestation.ps1 new file mode 100644 index 00000000..bcee0bcd --- /dev/null +++ b/tests/r850_windows_privileged_manifest_attestation.ps1 @@ -0,0 +1,53 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$UserRoot, + [Parameter(Mandatory=$true)][string]$PrivilegedRoot, + [Parameter(Mandatory=$true)][string]$ExpectedVersion, + [Parameter(Mandatory=$true)][string]$ExpectedManifestSha256, + [int]$ExpectedManagedFiles=681, + [int]$ExpectedPrivilegedFiles=674 +) +$ErrorActionPreference='Stop' +Set-StrictMode -Version Latest +function Guard([bool]$ok,[string]$errorCode) { + if(!$ok){throw ('R850_INSTALLED_MANIFEST_FAIL_CLOSED_'+$errorCode)} +} +Guard ($ExpectedVersion -cin @('1.5.6','1.5.7')) 'VERSION_ARGUMENT' +Guard ($ExpectedManifestSha256 -match '^[A-Fa-f0-9]{64}$') 'EXPECTED_SHA_FORMAT' +Guard ($ExpectedManagedFiles -gt 0 -and $ExpectedPrivilegedFiles -gt 0 -and $ExpectedPrivilegedFiles -le $ExpectedManagedFiles) 'COUNTS' +$rootUser=[IO.Path]::GetFullPath($UserRoot).TrimEnd('\') +$rootPriv=[IO.Path]::GetFullPath($PrivilegedRoot).TrimEnd('\') +Guard ($rootUser -cne $rootPriv) 'ROOTS_IDENTICAL' +Guard (Test-Path -LiteralPath $rootUser -PathType Container) 'USER_ROOT_ABSENT' +Guard (Test-Path -LiteralPath $rootPriv -PathType Container) 'PRIV_ROOT_ABSENT' +$manifest=Join-Path $rootUser 'manifest.json' +Guard (Test-Path -LiteralPath $manifest -PathType Leaf) 'MANIFEST_ABSENT' +$item=Get-Item -LiteralPath $manifest -Force +Guard (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -eq 0) 'MANIFEST_REPARSE' +Guard ((Get-FileHash -LiteralPath $manifest -Algorithm SHA256).Hash -ceq $ExpectedManifestSha256.ToUpperInvariant()) 'MANIFEST_SHA_MISMATCH' +$doc=Get-Content -LiteralPath $manifest -Raw -Encoding UTF8|ConvertFrom-Json +Guard ([string]$doc.version -ceq $ExpectedVersion) 'VERSION_MISMATCH' +Guard (@($doc.files).Count -eq $ExpectedManagedFiles) 'TOTAL_COUNT' +$seen=[Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) +$good=0 +foreach($f in @($doc.files)){ + $scope=[string]$f.scope + Guard ($scope -cin @('privileged','user')) 'INVALID_SCOPE' + $relative=[string]$f.file + Guard (![string]::IsNullOrWhiteSpace($relative)) 'BLANK_RELATIVE_PATH' + Guard (!([IO.Path]::IsPathRooted($relative)) -and !$relative.Contains('..') -and !$relative.Contains(':') -and !$relative.Contains([char]0)) 'UNSAFE_RELATIVE_PATH' + Guard ($seen.Add($scope+':'+$relative)) 'DUPLICATE_SCOPE_FILE' + $root=if($scope -ceq 'privileged'){$rootPriv}else{$rootUser} + $file=[IO.Path]::GetFullPath((Join-Path $root ($relative.Replace('/',[IO.Path]::DirectorySeparatorChar)))) + Guard ($file.StartsWith(($root+[IO.Path]::DirectorySeparatorChar),[StringComparison]::OrdinalIgnoreCase)) 'PATH_ESCAPE' + Guard (Test-Path -LiteralPath $file -PathType Leaf) 'FILE_MISSING' + $fi=Get-Item -LiteralPath $file -Force + Guard (($fi.Attributes -band [IO.FileAttributes]::ReparsePoint) -eq 0) 'FILE_REPARSE' + if($scope -ceq 'privileged'){ + Guard ($fi.Length -eq [long]$f.bytes) 'PRIVILEGED_SIZE_MISMATCH' + Guard ((Get-FileHash -LiteralPath $file -Algorithm SHA256).Hash -ceq ([string]$f.sha256).ToUpperInvariant()) 'PRIVILEGED_SHA_MISMATCH' + $good++ + } +} +Guard ($good -eq $ExpectedPrivilegedFiles) 'PRIVILEGED_COUNT' +Write-Output ('R850_NATIVE_INSTALLED_PRIVILEGED_MANIFEST_PASS_'+$good+'_OF_'+$ExpectedPrivilegedFiles)