From 034b42f4521eb8b2250c4009e13a6c3581f5edbd Mon Sep 17 00:00:00 2001 From: Farnam Taheri Date: Wed, 13 May 2026 01:08:13 +0330 Subject: [PATCH 1/2] Guard StormDNS upstream boundary --- .gitattributes | 1 + .github/workflows/stormdns-boundary.yml | 45 +++++++++++++++++++++ docs/STORMDNS_UPSTREAM_POLICY.md | 52 +++++++++++++++++++++++++ scripts/check-stormdns-boundary.sh | 26 +++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/stormdns-boundary.yml create mode 100644 docs/STORMDNS_UPSTREAM_POLICY.md create mode 100755 scripts/check-stormdns-boundary.sh diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfdb8b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/.github/workflows/stormdns-boundary.yml b/.github/workflows/stormdns-boundary.yml new file mode 100644 index 0000000..8e9b31a --- /dev/null +++ b/.github/workflows/stormdns-boundary.yml @@ -0,0 +1,45 @@ +name: StormDNS Boundary + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + - unlabeled + +permissions: + contents: read + pull-requests: read + +jobs: + upstream-guard: + name: Guard upstream engine changes + runs-on: ubuntu-latest + + steps: + - name: Checkout WhiteDNS + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: false + + - name: Check StormDNS upstream boundary + env: + HAS_UPSTREAM_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'allow-stormdns-upstream') }} + run: | + set -euo pipefail + base_ref="${{ github.event.pull_request.base.sha }}" + head_ref="${{ github.event.pull_request.head.sha }}" + + if scripts/check-stormdns-boundary.sh "${base_ref}" "${head_ref}"; then + exit 0 + fi + + if [[ "${HAS_UPSTREAM_LABEL}" == "true" ]]; then + echo "StormDNS upstream changes are intentionally labeled." + exit 0 + fi + + exit 1 diff --git a/docs/STORMDNS_UPSTREAM_POLICY.md b/docs/STORMDNS_UPSTREAM_POLICY.md new file mode 100644 index 0000000..b05de2f --- /dev/null +++ b/docs/STORMDNS_UPSTREAM_POLICY.md @@ -0,0 +1,52 @@ +# StormDNS Upstream Policy + +WhiteDNS treats `third_party/StormDNS` as a black-box upstream engine. + +The Android app must integrate with StormDNS only through stable runtime boundaries: + +- The packaged StormDNS executable. +- CLI flags passed to that executable. +- Generated TOML configuration files. +- Generated resolver files. +- stdout/stderr telemetry emitted by the process. +- Process exit codes. +- Version and capability detection from the executable. +- Files written by StormDNS under the WhiteDNS-owned working directory. + +## Disallowed Coupling + +WhiteDNS app features must not depend on: + +- StormDNS internal Go packages. +- Private StormDNS source layout. +- Unexported Go symbols or package structure. +- Parsing upstream Go source files for app behavior. +- Editing upstream code to satisfy Android app feature needs. +- Runtime assumptions that require knowing StormDNS internals beyond documented executable behavior. + +Build tooling may know where the upstream client command lives so it can compile the executable. App runtime code must not. + +## Allowed Upstream Changes + +Changes under `third_party/StormDNS` are allowed only when they are intentional upstream maintenance, such as: + +- Updating the pinned submodule commit. +- Syncing a reviewed upstream fix. +- Refreshing native binaries from a documented upstream commit. + +Any pull request that changes `third_party/StormDNS` must include the label: + +`allow-stormdns-upstream` + +Without that label, CI fails before review to prevent accidental local edits to the upstream engine. + +## Review Checklist + +For any pull request touching StormDNS integration: + +- Confirm app code interacts through the executable boundary only. +- Confirm generated TOML and resolver files are owned by WhiteDNS and live under app-controlled runtime directories. +- Confirm telemetry parsing is tolerant of unknown or changed output. +- Confirm optional behavior is gated by executable version/capability detection when needed. +- Confirm no Android app code imports, parses, or depends on StormDNS Go internals. +- Confirm `third_party/StormDNS` changes are either absent or intentionally labeled and explained. diff --git a/scripts/check-stormdns-boundary.sh b/scripts/check-stormdns-boundary.sh new file mode 100755 index 0000000..5194d31 --- /dev/null +++ b/scripts/check-stormdns-boundary.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +base_ref="${1:-origin/main}" +head_ref="${2:-HEAD}" + +if ! git rev-parse --verify "${base_ref}" >/dev/null 2>&1; then + echo "Base ref '${base_ref}' is not available." >&2 + exit 2 +fi + +changed_files="$( + git diff --name-only "${base_ref}...${head_ref}" -- 'third_party/StormDNS' || true +)" + +if [[ -z "${changed_files}" ]]; then + echo "No StormDNS upstream changes detected." + exit 0 +fi + +echo "StormDNS upstream changes detected:" >&2 +echo "${changed_files}" >&2 +echo >&2 +echo "WhiteDNS treats third_party/StormDNS as a black-box upstream engine." >&2 +echo "Add the 'allow-stormdns-upstream' pull request label only when these changes are intentional upstream maintenance." >&2 +exit 1 From ff7a2a1c080fbfb75717e21b472776453cea180c Mon Sep 17 00:00:00 2001 From: Farnam Taheri Date: Wed, 13 May 2026 03:34:30 +0330 Subject: [PATCH 2/2] Harden StormDNS boundary guard --- .github/workflows/stormdns-boundary.yml | 11 ++++++++--- docs/STORMDNS_UPSTREAM_POLICY.md | 6 +++--- scripts/check-stormdns-boundary.sh | 7 ++++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/stormdns-boundary.yml b/.github/workflows/stormdns-boundary.yml index 8e9b31a..89ed6e0 100644 --- a/.github/workflows/stormdns-boundary.yml +++ b/.github/workflows/stormdns-boundary.yml @@ -33,13 +33,18 @@ jobs: base_ref="${{ github.event.pull_request.base.sha }}" head_ref="${{ github.event.pull_request.head.sha }}" - if scripts/check-stormdns-boundary.sh "${base_ref}" "${head_ref}"; then + set +e + scripts/check-stormdns-boundary.sh "${base_ref}" "${head_ref}" + guard_status="$?" + set -e + + if [[ "${guard_status}" == "0" ]]; then exit 0 fi - if [[ "${HAS_UPSTREAM_LABEL}" == "true" ]]; then + if [[ "${guard_status}" == "1" && "${HAS_UPSTREAM_LABEL}" == "true" ]]; then echo "StormDNS upstream changes are intentionally labeled." exit 0 fi - exit 1 + exit "${guard_status}" diff --git a/docs/STORMDNS_UPSTREAM_POLICY.md b/docs/STORMDNS_UPSTREAM_POLICY.md index b05de2f..c4517cb 100644 --- a/docs/STORMDNS_UPSTREAM_POLICY.md +++ b/docs/STORMDNS_UPSTREAM_POLICY.md @@ -28,13 +28,13 @@ Build tooling may know where the upstream client command lives so it can compile ## Allowed Upstream Changes -Changes under `third_party/StormDNS` are allowed only when they are intentional upstream maintenance, such as: +Changes under `third_party/StormDNS` or `.gitmodules` are allowed only when they are intentional upstream maintenance, such as: - Updating the pinned submodule commit. - Syncing a reviewed upstream fix. - Refreshing native binaries from a documented upstream commit. -Any pull request that changes `third_party/StormDNS` must include the label: +Any pull request that changes `third_party/StormDNS` or `.gitmodules` must include the label: `allow-stormdns-upstream` @@ -49,4 +49,4 @@ For any pull request touching StormDNS integration: - Confirm telemetry parsing is tolerant of unknown or changed output. - Confirm optional behavior is gated by executable version/capability detection when needed. - Confirm no Android app code imports, parses, or depends on StormDNS Go internals. -- Confirm `third_party/StormDNS` changes are either absent or intentionally labeled and explained. +- Confirm `third_party/StormDNS` and `.gitmodules` changes are either absent or intentionally labeled and explained. diff --git a/scripts/check-stormdns-boundary.sh b/scripts/check-stormdns-boundary.sh index 5194d31..05e8f0f 100755 --- a/scripts/check-stormdns-boundary.sh +++ b/scripts/check-stormdns-boundary.sh @@ -9,8 +9,13 @@ if ! git rev-parse --verify "${base_ref}" >/dev/null 2>&1; then exit 2 fi +if ! git rev-parse --verify "${head_ref}" >/dev/null 2>&1; then + echo "Head ref '${head_ref}' is not available." >&2 + exit 2 +fi + changed_files="$( - git diff --name-only "${base_ref}...${head_ref}" -- 'third_party/StormDNS' || true + git diff --name-only "${base_ref}...${head_ref}" -- 'third_party/StormDNS' '.gitmodules' )" if [[ -z "${changed_files}" ]]; then