compute-guard: Hub conditions (90-day cap, named approver, CODEOWNERS, engine-port noise cut)
- non-structural exemptions need approved_by (windy-hub|windy-mind) and expire within 90 days; a longer amnesty simply does not apply and is reported (OVER-CAP). compute-door/guard-self: yearly. - .github/CODEOWNERS on the allow-lists + guard. - engine-port rule skips contracts/schemas/specs/openapi dirs and *.json (53 baseline hits, was 57). - tests: findings carry kind+name never the value; shipped allow file obeys its own rules. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
7
.github/CODEOWNERS
vendored
Normal file
7
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Changes to the guard allow-lists / exemptions need review by the account owner on GitHub, AND an
|
||||
# approved_by (windy-hub | windy-mind) on every non-structural entry, which the guard enforces itself
|
||||
# (scripts/compute_guard.py: load_allow). A lane never approves its own exemption (Hub 10-02).
|
||||
/ci/compute-guard-allow.yml @sneakyfree
|
||||
/ci/secret-guard-allow.yml @sneakyfree
|
||||
/ci/ci-hygiene-allow.yml @sneakyfree
|
||||
/scripts/compute_guard.py @sneakyfree
|
||||
@@ -78,16 +78,18 @@ def test_allow_list_needs_a_reason_per_entry(tmp_path):
|
||||
|
||||
|
||||
def _entry(**kw):
|
||||
base = dict(repo="x", paths=["*"], reason="r", exemption="owner-approved", expires="2099-01-01")
|
||||
base = dict(repo="x", paths=["*"], reason="r", exemption="owner-approved", expires="2099-01-01",
|
||||
approved_by="windy-hub")
|
||||
base.update(kw)
|
||||
lines = ["allow:", " - repo: x", " paths: ['*']", " reason: r"]
|
||||
for k in ("exemption", "expires"):
|
||||
for k in ("exemption", "expires", "approved_by"):
|
||||
if base.get(k) is not None:
|
||||
lines.append(f" {k}: {base[k]}")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def test_allow_entries_need_a_named_exemption_and_an_expiry(tmp_path):
|
||||
from datetime import date
|
||||
f = tmp_path / "a.yml"
|
||||
f.write_text(_entry(exemption=None))
|
||||
with pytest.raises(ValueError):
|
||||
@@ -101,8 +103,8 @@ def test_allow_entries_need_a_named_exemption_and_an_expiry(tmp_path):
|
||||
f.write_text(_entry(expires="someday"))
|
||||
with pytest.raises(ValueError):
|
||||
cg.load_allow(f)
|
||||
f.write_text(_entry())
|
||||
assert len(cg.load_allow(f)) == 1
|
||||
f.write_text(_entry(expires="2026-12-01"))
|
||||
assert len(cg.load_allow(f, today=date(2026, 10, 2))) == 1
|
||||
|
||||
|
||||
def test_expired_exemption_stops_excusing_and_is_reported(tmp_path):
|
||||
@@ -330,3 +332,39 @@ def test_ollama_in_added_pr_lines_only():
|
||||
"+URL = 'http://veron:11434/api/chat'\n")
|
||||
got = cg.parse_added("some-repo", diff, [])
|
||||
assert [(f.kind, f.line) for f in got] == [("veron ollama", 2)]
|
||||
|
||||
|
||||
def test_non_structural_exemptions_need_an_independent_approver_and_a_90_day_cap(tmp_path):
|
||||
from datetime import date
|
||||
f = tmp_path / "a.yml"
|
||||
f.write_text(_entry(approved_by=None))
|
||||
with pytest.raises(ValueError):
|
||||
cg.load_allow(f, today=date(2026, 10, 2))
|
||||
f.write_text(_entry(approved_by="windy-chat")) # a lane may not approve itself/another lane
|
||||
with pytest.raises(ValueError):
|
||||
cg.load_allow(f, today=date(2026, 10, 2))
|
||||
f.write_text(_entry(expires="2026-12-31")) # exactly 90 days: fine
|
||||
assert len(cg.load_allow(f, today=date(2026, 10, 2))) == 1
|
||||
f.write_text(_entry(expires="2027-01-01")) # 91 days: does NOT apply, and is reported
|
||||
assert cg.load_allow(f, today=date(2026, 10, 2)) == [] and cg.OVERCAP
|
||||
f.write_text(_entry(exemption="compute-door", approved_by=None, expires="2027-10-02"))
|
||||
assert len(cg.load_allow(f, today=date(2026, 10, 2))) == 1 # structural: yearly, no approver field
|
||||
|
||||
|
||||
def test_shipped_allow_file_obeys_its_own_rules():
|
||||
allow = cg.load_allow()
|
||||
assert allow and not cg.EXPIRED and not cg.OVERCAP
|
||||
for e in allow:
|
||||
if e["exemption"] not in cg.STRUCTURAL:
|
||||
assert e["approved_by"] in cg.APPROVERS
|
||||
|
||||
|
||||
def test_findings_carry_kind_and_name_never_the_value_and_ports_skip_contracts():
|
||||
for line, kind in [("ELEVENLABS_API_KEY=sk_live_SUPERSECRET123456789", "voice-ai key"),
|
||||
('DEEPGRAM_API_KEY = "dg-VALUE-0123456789abcdef"', "voice-ai key")]:
|
||||
hits = cg.scan_line("app/x.py", line)
|
||||
assert [k for k, _ in hits] == [kind]
|
||||
assert all("SUPERSECRET" not in m and "VALUE" not in m for _, m in hits)
|
||||
assert cg.scan_line("engine/contracts/ops.mcp.v1.json", '"url": "http://h:8099/x"') == []
|
||||
assert cg.scan_line("services/api/openapi/spec.json", '"url": "http://h:8099/x"') == []
|
||||
assert [k for k, _ in cg.scan_line("deploy/docker-compose.yml", " - 8099:8099 # :8099")] == ["talk engine port"]
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
# relative to the repo root. Owner of this file: Windy Git lane (13); changes
|
||||
# go through the orchestrator. EVERY entry needs `exemption` (local-user-hardware | owner-approved |
|
||||
# compute-door | guard-self) and `expires` (YYYY-MM-DD): nothing gets permanent amnesty (Mind 10-02);
|
||||
# an expired entry stops excusing code on that date and shows in the guard report. Source of the first entries: COMPUTE_BYPASS_AUDIT.md.
|
||||
# non-structural exemptions also need approved_by (windy-hub | windy-mind; a lane never approves its own)
|
||||
# and expire within 90 days; an expired entry stops excusing code on that date and shows in the guard report. Source of the first entries: COMPUTE_BYPASS_AUDIT.md.
|
||||
allow:
|
||||
- repo: windy-mind
|
||||
paths: ["*"]
|
||||
@@ -19,18 +20,24 @@ allow:
|
||||
Mind stays opt-in there, or every self-hosted user's inference lands on
|
||||
Grant's bill (no-cloud-cost-liability rule; audit #7).
|
||||
exemption: owner-approved
|
||||
approved_by: windy-hub
|
||||
approved_on: 2026-10-02
|
||||
expires: 2026-12-31
|
||||
|
||||
- repo: windy-code
|
||||
paths: ["extensions/windy-ai/*"]
|
||||
reason: "User BYOK AI extension: the user's own provider keys; Mind is one opt-in provider (audit #8)."
|
||||
exemption: owner-approved
|
||||
approved_by: windy-hub
|
||||
approved_on: 2026-10-02
|
||||
expires: 2026-12-31
|
||||
|
||||
- repo: windy-connect
|
||||
paths: ["*writers/*"]
|
||||
reason: "Writes client configs that NAME the user's own provider env vars; makes no provider calls (audit #11)."
|
||||
exemption: owner-approved
|
||||
approved_by: windy-hub
|
||||
approved_on: 2026-10-02
|
||||
expires: 2026-12-31
|
||||
|
||||
- repo: windy-pro
|
||||
@@ -41,12 +48,16 @@ allow:
|
||||
the CSP line allows exactly those user-keyed hosts (audit #10). The
|
||||
account-server is NOT covered: server-side calls go through Mind.
|
||||
exemption: owner-approved
|
||||
approved_by: windy-hub
|
||||
approved_on: 2026-10-02
|
||||
expires: 2026-12-31
|
||||
|
||||
- repo: windy-pro
|
||||
paths: ["src/client/web/src/pages/panels/MindPanel.jsx"]
|
||||
reason: "Validates the USER's own OpenRouter key for BYOK (audit #10); spends no house money."
|
||||
exemption: owner-approved
|
||||
approved_by: windy-hub
|
||||
approved_on: 2026-10-02
|
||||
expires: 2026-12-31
|
||||
|
||||
- repo: windy-pro
|
||||
@@ -56,6 +67,8 @@ allow:
|
||||
matches: ['openrouter\.ai/auth\?', 'openrouter\.ai/api/v1/auth/keys']
|
||||
reason: "BYOK key acquisition via OpenRouter OAuth PKCE; no inference; successor of MindPanel allow (ADR-064)."
|
||||
exemption: owner-approved
|
||||
approved_by: windy-hub
|
||||
approved_on: 2026-10-02
|
||||
expires: 2026-12-31
|
||||
|
||||
- repo: windy-git
|
||||
|
||||
@@ -31,7 +31,7 @@ import re
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from datetime import date, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
@@ -73,6 +73,8 @@ PY_SDKS = r"anthropic|openai|groq|mistralai|cohere|google\.generativeai|google\.
|
||||
JS_SDKS = (r"@anthropic-ai/sdk|openai|groq-sdk|@google/generative-ai|@google/genai|@mistralai/mistralai"
|
||||
r"|cohere-ai|together-ai|@ai-sdk/(?:anthropic|openai|groq|google|mistral)")
|
||||
|
||||
# The engine-port rule is about CODE/CONFIG that calls the engine, not API contracts, schemas or specs.
|
||||
PORT_SKIP = re.compile(r"(^|/)(contracts?|schemas?|specs?|openapi)/|\.json$", re.I)
|
||||
WRANGLER = re.compile(r"(^|/)wrangler\.(toml|jsonc?)$")
|
||||
WRANGLER_AI = re.compile(r'^\s*\[ai\]\s*$|^\s*"ai"\s*:\s*\{')
|
||||
|
||||
@@ -121,7 +123,11 @@ class Finding:
|
||||
|
||||
|
||||
EXEMPTIONS = {"local-user-hardware", "owner-approved", "compute-door", "guard-self"}
|
||||
STRUCTURAL = {"compute-door", "guard-self"} # the door itself and the guard's own files: yearly review
|
||||
APPROVERS = {"windy-hub", "windy-mind"} # a lane never approves its own exemption (Hub 10-02)
|
||||
MAX_DAYS = 90 # every other exemption: 90 days max, then re-approve
|
||||
EXPIRED: list[dict] = [] # entries dropped as expired on the last load_allow (reported, never silent)
|
||||
OVERCAP: list[dict] = [] # entries dropped because their expiry is further out than MAX_DAYS
|
||||
|
||||
|
||||
def load_allow(path: Path = ALLOW_FILE, today: date | None = None, strict: bool = True) -> list[dict]:
|
||||
@@ -133,6 +139,7 @@ def load_allow(path: Path = ALLOW_FILE, today: date | None = None, strict: bool
|
||||
entries = data.get("allow") or []
|
||||
active = []
|
||||
EXPIRED.clear()
|
||||
OVERCAP.clear()
|
||||
for e in entries:
|
||||
if not (e.get("repo") and e.get("paths") and str(e.get("reason", "")).strip()):
|
||||
raise ValueError(f"allow entry needs repo, paths and a reason: {e}")
|
||||
@@ -145,6 +152,12 @@ def load_allow(path: Path = ALLOW_FILE, today: date | None = None, strict: bool
|
||||
exp = e["expires"] if isinstance(e.get("expires"), date) else date.fromisoformat(str(e.get("expires")))
|
||||
except ValueError as err:
|
||||
raise ValueError(f"allow entry needs expires: YYYY-MM-DD: {e.get('repo')} {e.get('paths')}") from err
|
||||
if e["exemption"] not in STRUCTURAL:
|
||||
if e.get("approved_by") not in APPROVERS:
|
||||
raise ValueError(f"allow entry needs approved_by in {sorted(APPROVERS)}: {e.get('repo')} {e.get('paths')}")
|
||||
if exp > today + timedelta(days=MAX_DAYS):
|
||||
OVERCAP.append({**e, "expires": exp.isoformat()}) # a longer amnesty simply does not apply
|
||||
continue
|
||||
if exp < today:
|
||||
EXPIRED.append({**e, "expires": exp.isoformat()})
|
||||
else:
|
||||
@@ -182,6 +195,8 @@ def scan_line(path: str, text: str) -> list[tuple[str, str]]:
|
||||
continue
|
||||
if kind == "workers ai binding" and not WRANGLER.search(path):
|
||||
continue
|
||||
if kind == "talk engine port" and PORT_SKIP.search(path):
|
||||
continue
|
||||
m = rx.search(text)
|
||||
if m:
|
||||
hits.append((kind, m.group(0).strip()[:60]))
|
||||
@@ -358,6 +373,9 @@ def status_for(findings: list[Finding], whole_tree: bool,
|
||||
|
||||
def report(repos: list[str]) -> int:
|
||||
allow = load_allow()
|
||||
for e in OVERCAP:
|
||||
print(f"## OVER-CAP exemption (> {MAX_DAYS} days, NOT applied): {e['repo']} {e['paths']} "
|
||||
f"[{e['exemption']}] expires {e['expires']}")
|
||||
for e in EXPIRED:
|
||||
print(f"## EXPIRED exemption (no longer excuses anything): {e['repo']} {e['paths']} "
|
||||
f"[{e['exemption']}] expired {e['expires']}")
|
||||
|
||||
Reference in New Issue
Block a user