From 92906ad691833c8b9132f829451a335385aebfef Mon Sep 17 00:00:00 2001 From: Orual Date: Sat, 1 Aug 2026 22:50:22 -0400 Subject: [PATCH] GC: constrain lifecycle validator gates --- .polytoken/hooks/workflow-context.sh | 25 +++++++++++---- justfile | 12 +++++++ src/appview/tests.rs | 2 +- src/env.rs | 3 ++ src/thing_detail.rs | 4 +-- tools/check-production-sql.py | 47 ++++++++++++++++++++++++++++ tools/validate-gc-state.py | 33 +++++++++++++++++-- 7 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 src/env.rs diff --git a/.polytoken/hooks/workflow-context.sh b/.polytoken/hooks/workflow-context.sh index e3b3ac3..b6da563 100755 --- a/.polytoken/hooks/workflow-context.sh +++ b/.polytoken/hooks/workflow-context.sh @@ -32,23 +32,36 @@ session_api_metadata() { port="$(jq -r '.port // empty' "$startup" 2>/dev/null)" credential_file="$(jq -r '.credential_file_path // empty' "$startup" 2>/dev/null)" - if [ -z "$port" ] || [ -z "$credential_file" ] || [ ! -f "$credential_file" ]; then + if ! [[ "$port" =~ ^[0-9]+$ ]] \ + || [ "$port" -lt 1 ] \ + || [ "$port" -gt 65535 ] \ + || [ -z "$credential_file" ] \ + || [ ! -f "$credential_file" ]; then return 1 fi token="$(jq -r '.token // empty' "$credential_file" 2>/dev/null)" - if [ -z "$token" ]; then + if [ -z "$token" ] || printf '%s' "$token" | LC_ALL=C grep -q '[[:cntrl:]]'; then return 1 fi printf '%s\t%s\n' "$port" "$token" } +curl_config_escape() { + local value="$1" + value="${value//\\/\\\\}" + value="${value//\"/\\\"}" + printf '%s' "$value" +} + state_summary() { - local port="$1" token="$2" - curl -fsS --max-time 1 \ - -H "Authorization: Bearer ${token}" \ - "http://127.0.0.1:${port}/state" 2>/dev/null \ + local port="$1" token="$2" authorization + authorization="$(curl_config_escape "Authorization: Bearer ${token}")" + + printf 'header = "%s"\n' "$authorization" \ + | curl -fsS --max-time 1 --config - \ + "http://127.0.0.1:${port}/state" 2>/dev/null \ | jq -r ' "- Active facet: \(.active_facet // "unknown")\n" + "- Active model: \(.active_model // "unknown")\n" + diff --git a/justfile b/justfile index cd27fca..6478c96 100644 --- a/justfile +++ b/justfile @@ -117,6 +117,18 @@ validate-gc-state *ARGS: validate-gc-jj-state *ARGS: python3 tools/validate-gc-jj-state.py {{ ARGS }} +# Check that build-time environment handling does not expose server secrets to the frontend. +check-public-env *ARGS: + python3 tools/check-public-env.py {{ ARGS }} + +# Guard compile-time SQL usage in handwritten production Rust. +check-production-sql *ARGS: + python3 tools/check-production-sql.py {{ ARGS }} + +# Validate current documentation references and architecture maps. +check-doc-references *ARGS: + python3 tools/check-doc-references.py {{ ARGS }} + # Guard the single production appview content-type normalizer and its callers. check-content-type *ARGS: python3 tools/check-content-type.py {{ ARGS }} diff --git a/src/appview/tests.rs b/src/appview/tests.rs index d7c8903..83e8798 100644 --- a/src/appview/tests.rs +++ b/src/appview/tests.rs @@ -1,7 +1,7 @@ //! Focused appview test modules. #![cfg(test)] -pub(super) use super::{actor, error, proxy, router, ssr, state, views}; +pub(super) use super::{error, proxy, state, views}; #[cfg(test)] #[path = "download_tests.rs"] diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000..a843a7f --- /dev/null +++ b/src/env.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by build.rs. +// Frontend (WASM) compile-time configuration. +// Do not edit; regenerate by rebuilding. diff --git a/src/thing_detail.rs b/src/thing_detail.rs index 28c1bd0..bbd148f 100644 --- a/src/thing_detail.rs +++ b/src/thing_detail.rs @@ -75,9 +75,7 @@ fn part_download_href(uri: &AtUri) -> Option { if path.collection.as_str() != "space.polymodel.library.part" { return None; } - if path.rkey.is_none() { - return None; - } + path.rkey.as_ref()?; Some(format!( "/xrpc/space.polymodel.library.getPartFile?uri={}", urlencoding::encode(uri.as_str()) diff --git a/tools/check-production-sql.py b/tools/check-production-sql.py index 761b4fb..4ce3823 100644 --- a/tools/check-production-sql.py +++ b/tools/check-production-sql.py @@ -431,14 +431,40 @@ def _is_explicit_test_owned(path: Path, root: Path) -> bool: return path.stem in {"tests", "test"} or path.name.endswith("_tests.rs") +PATH_MODULE = re.compile( + r"#\s*\[\s*path\s*=\s*([\"'])([^\"']+)\1\s*\]" +) +FILE_CFG_TEST = re.compile(r"(?m)^\s*#!\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]") +CFG_TEST_ATTRIBUTE = re.compile(r"#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]\s*$") + + +def _test_owned_path_modules(root: Path) -> set[Path]: + """Find sibling files imported only through test-gated path modules.""" + owned: set[Path] = set() + src = root / "src" + for parent in src.rglob("*.rs"): + source = parent.read_text(encoding="utf-8") + file_is_test = FILE_CFG_TEST.search(source) is not None + for match in PATH_MODULE.finditer(source): + preceding = source[: match.start()] + if not file_is_test and CFG_TEST_ATTRIBUTE.search(preceding) is None: + continue + target = (parent.parent / match.group(2)).resolve() + if target.is_file() and target.is_relative_to(src.resolve()): + owned.add(target) + return owned + + def source_paths(root: Path) -> list[Path]: src = root / "src" if not src.is_dir(): return [] + test_owned_modules = _test_owned_path_modules(root) return sorted( path for path in src.rglob("*.rs") if not _is_explicit_test_owned(path, root) + and path.resolve() not in test_owned_modules ) @@ -529,6 +555,23 @@ def run_self_test() -> None: (root / "src" / "fixture_tests.rs").write_text( "sqlx::query_as(\"fixture file\");\n", encoding="utf-8" ) + (root / "src" / "test_dispatch.rs").write_text( + "#![cfg(test)]\n" + "#[path = \"test_support.rs\"]\n" + "mod test_support;\n", + encoding="utf-8", + ) + (root / "src" / "test_support.rs").write_text( + "sqlx::query(\"test fixture module\");\n", encoding="utf-8" + ) + (root / "src" / "production_path.rs").write_text( + "#[path = \"production_support.rs\"]\n" + "mod production_support;\n", + encoding="utf-8", + ) + (root / "src" / "production_support.rs").write_text( + "sqlx::query(\"production path module\");\n", encoding="utf-8" + ) (root / "src" / "contest.rs").write_text( "pub fn production_contest() { sqlx::query(\"contest\"); }\n", encoding="utf-8", @@ -547,6 +590,7 @@ def run_self_test() -> None: (root / "src" / "nested.rs", 20), (root / "src" / "production.rs", 2), (root / "src" / "production.rs", 3), + (root / "src" / "production_support.rs", 1), ] if found != expected: raise AssertionError(f"checker self-test mismatch: {found!r}") @@ -567,6 +611,9 @@ def run_self_test() -> None: "mod all_tests { sqlx::query(\"masked\"); }\n", encoding="utf-8", ) + (root / "src" / "production_support.rs").write_text( + "sqlx::query!(\"production path\");\n", encoding="utf-8" + ) found = violations(root) if found: raise AssertionError(f"allowed macro self-test mismatch: {found!r}") diff --git a/tools/validate-gc-state.py b/tools/validate-gc-state.py index dffc6b1..0dfd527 100755 --- a/tools/validate-gc-state.py +++ b/tools/validate-gc-state.py @@ -14,8 +14,10 @@ RUN_ID = "gc-2026-08-01-catchup" UNITS = set("ABCDEFG") CLUSTERS = {f"C{i}" for i in range(1, 14)} REQUIRED = ("manifest.txt", "scope.md", "workflow.dot", "events.log", "gradebook.jsonl", "clusters.jsonl", "deferrals.jsonl", "todos.jsonl") -EVENTS = {"setup", "raw_discovered", "clustered", "verified", "rejected", "needs_more_evidence", "cleanup_ready", "planned", "in_progress", "cleaned", "deferred", "blocked", "ejected_from_gc", "accepted_risk", "stale_expired"} +EVENTS = {"setup", "raw_discovered", "clustered", "verified", "rejected", "needs_more_evidence", "cleanup_ready", "planned", "in_progress", "cleaned", "partially_cleaned", "deferred", "blocked", "ejected_from_gc", "accepted_risk", "stale_expired"} TERMINAL = {"done", "blocked", "deferred", "rejected", "ejected_from_gc", "partially_cleaned"} +OPERATOR_GATE_TODOS = {44, 45, 46, 47} +POST_ACCEPTANCE_TODOS = {48, 49, 50} def fail(message: str) -> None: @@ -116,6 +118,11 @@ def validate_state(root: Path, require_dispositions: bool, require_reviews: bool fail(f"todos.jsonl:{index}: missing {field}") if record["status"] not in TERMINAL | {"pending", "in_progress"}: fail(f"todos.jsonl:{index}: invalid status") + todo_id = int(record["id"]) + if record.get("operator_gate") and todo_id not in OPERATOR_GATE_TODOS: + fail(f"todos.jsonl:{index}: operator_gate is not allowed for todo {todo_id}") + if record.get("post_acceptance") and todo_id not in POST_ACCEPTANCE_TODOS: + fail(f"todos.jsonl:{index}: post_acceptance is not allowed for todo {todo_id}") if require_dispositions: event_clusters = {record.get("cluster_id") for record in events} for cluster in ("C3", "C4", "C5", "C7", "C12", "C13"): @@ -139,8 +146,8 @@ def validate_state(root: Path, require_dispositions: bool, require_reviews: bool fail(f"review record {index} missing {field}") if finding["severity"] in {"Critical", "High"} and finding["disposition"] not in {"fixed", "rebutted", "accepted_risk"}: fail(f"unresolved {finding['severity']} finding {finding['finding_id']}") - if any(record["status"] not in TERMINAL for record in todos): - fail("all todos must be terminal for --require-review-records") + if any(record["status"] not in TERMINAL and not record.get("operator_gate") and not record.get("post_acceptance") for record in todos): + fail("all pre-acceptance todos must be terminal for --require-review-records") print(f"GC state valid: {RUN_ID}") @@ -174,6 +181,26 @@ def self_test() -> None: pass else: fail("negative fixture unexpectedly passed") + + (state / "events.log").write_text(json.dumps(event) + "\n") + valid_todo = { + "id": "44", + "title": "operator acceptance", + "status": "pending", + "dependencies": [], + "checkpoint_evidence": [], + "operator_gate": True, + } + (state / "todos.jsonl").write_text(json.dumps(valid_todo) + "\n") + validate_state(root, False, False) + invalid_todo = {**valid_todo, "id": "1"} + (state / "todos.jsonl").write_text(json.dumps(invalid_todo) + "\n") + try: + validate_state(root, False, False) + except ValueError: + pass + else: + fail("ordinary todo gate fixture unexpectedly passed") print("validate-gc-state self-test passed") -- 2.51.2