From 37de2594b42b5e55b5d485d4ecf3b3534d143427 Mon Sep 17 00:00:00 2001 From: dxnnystyles Date: Wed, 28 Oct 2026 20:01:09 -0700 Subject: [PATCH 1/2] test: enforce scheduled workflow safeguards --- tests/test_scheduled_workflow_safeguards.py | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 tests/test_scheduled_workflow_safeguards.py diff --git a/tests/test_scheduled_workflow_safeguards.py b/tests/test_scheduled_workflow_safeguards.py new file mode 100644 index 0000000..2c8f062 --- /dev/null +++ b/tests/test_scheduled_workflow_safeguards.py @@ -0,0 +1,66 @@ +from pathlib import Path + +import pytest +import yaml + + +WORKFLOWS_DIR = Path(__file__).parents[1] / ".github/workflows" +EXPECTED_SCHEDULED_PERMISSIONS = { + "codeowners-access.yml": {"contents": "read"}, + "codeql.yml": { + "actions": "read", + "contents": "read", + "security-events": "write", + }, + "external-links.yml": {"contents": "read", "issues": "write"}, + "traction-live.yml": {"contents": "read", "issues": "write"}, +} + + +def _scheduled_workflows(): + workflows = [] + for path in sorted(WORKFLOWS_DIR.glob("*.yml")): + workflow = yaml.load(path.read_text(encoding="utf-8"), Loader=yaml.BaseLoader) + if "schedule" in workflow.get("on", {}): + workflows.append((path, workflow)) + return workflows + + +SCHEDULED_WORKFLOWS = _scheduled_workflows() + + +def test_scheduled_workflows_have_explicit_minimal_permissions(): + actual = {path.name for path, _ in SCHEDULED_WORKFLOWS} + assert actual == set(EXPECTED_SCHEDULED_PERMISSIONS) + + for path, workflow in SCHEDULED_WORKFLOWS: + permissions = workflow.get("permissions") + assert permissions == EXPECTED_SCHEDULED_PERMISSIONS[path.name] + assert permissions["contents"] == "read" + + +@pytest.mark.parametrize( + "path,workflow", + SCHEDULED_WORKFLOWS, + ids=[path.name for path, _ in SCHEDULED_WORKFLOWS], +) +def test_scheduled_workflows_have_concurrency_controls(path, workflow): + concurrency = workflow.get("concurrency") + assert isinstance(concurrency, dict), path.name + assert concurrency.get("group", "").strip(), path.name + assert "cancel-in-progress" in concurrency, path.name + + +@pytest.mark.parametrize( + "path,workflow", + SCHEDULED_WORKFLOWS, + ids=[path.name for path, _ in SCHEDULED_WORKFLOWS], +) +def test_scheduled_workflow_jobs_have_timeouts(path, workflow): + jobs = workflow.get("jobs") + assert isinstance(jobs, dict) and jobs, path.name + + for job_name, job in jobs.items(): + timeout = job.get("timeout-minutes") + assert timeout is not None, f"{path.name}:{job_name} has no timeout" + assert 1 <= int(timeout) <= 360, f"{path.name}:{job_name} has invalid timeout" From be2d95be09feddb738a8b0c0ee5e2101cb19eaff Mon Sep 17 00:00:00 2001 From: dxnnystyles Date: Wed, 28 Oct 2026 20:19:27 -0700 Subject: [PATCH 2/2] ci: install test dependencies for profiler workflow --- .github/workflows/audits.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/audits.yml b/.github/workflows/audits.yml index 8fcd66c..352176c 100644 --- a/.github/workflows/audits.yml +++ b/.github/workflows/audits.yml @@ -126,7 +126,7 @@ jobs: - name: Install faircode + pytest run: | python -m pip install --upgrade pip - pip install -e ".[excel,parquet,proxy,mcp]" pytest pytest-cov + pip install -e ".[test,excel,parquet,proxy,mcp]" # One run: the whole suite with line coverage printed at the end. A test failure fails the # job; the coverage figure itself is informational, not a gate (#932). diff --git a/pyproject.toml b/pyproject.toml index 3640634..9847613 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ parquet = ["pyarrow>=10.0"] proxy = ["scipy>=1.10"] benchmark = ["scikit-learn>=1.8.0", "pyyaml>=6.0", "fairlearn>=0.14.0", "matplotlib>=3.10"] mcp = ["mcp>=2.0"] -test = ["pytest>=8.0", "pytest-cov>=5.0", "certifi>=2023.1.1"] # certifi: scripts/check_external_links.py trust store fallback +test = ["pytest>=8.0", "pytest-cov>=5.0", "certifi>=2023.1.1", "pyyaml>=6.0"] # certifi: scripts/check_external_links.py trust store fallback; pyyaml: workflow safeguard tests browser = ["playwright>=1.40"] # tests/browser; also run `python -m playwright install chromium` [project.urls]