From 8e934e3263687c69952ddafb3de926dc56cac206 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 17:40:38 +0900 Subject: [PATCH 1/4] fix(automation): run securely pinned review repair hourly --- .github/workflows/pr-review-fix-scheduler.yml | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr-review-fix-scheduler.yml b/.github/workflows/pr-review-fix-scheduler.yml index cc7875bc8..951206d55 100644 --- a/.github/workflows/pr-review-fix-scheduler.yml +++ b/.github/workflows/pr-review-fix-scheduler.yml @@ -26,7 +26,7 @@ on: retry_hours: description: Minimum hours before redispatching autofix for the same head required: false - default: "24" + default: "1" type: string autofix_workflow: description: Autofix workflow file to dispatch @@ -44,14 +44,16 @@ on: default: "" type: string canonical_ref: - description: Ref of ContextualWisdomLab/.github to use for scheduler code + description: Deprecated compatibility input; accepted and ignored because privileged source is bound to the called workflow SHA required: false - default: "main" + default: "" type: string repository_dispatch: types: [pr-review-fix-scheduler] schedule: - - cron: "23 */2 * * *" + # Run away from minute zero, where scheduled GitHub Actions are more likely + # to be delayed, while preserving a bounded one-dispatch-per-run repair loop. + - cron: "23 * * * *" concurrency: group: central-pr-review-fix-scheduler-${{ github.event.client_payload.target_repository || inputs.target_repository || vars.PR_REVIEW_FIX_TARGET_REPOSITORY || github.repository }} @@ -80,16 +82,19 @@ jobs: DRY_RUN: ${{ github.event.client_payload.dry_run == true || github.event.client_payload.dry_run == 'true' || inputs.dry_run == true }} MAX_PRS: ${{ github.event.client_payload.max_prs || inputs.max_prs || '50' }} MAX_DISPATCHES: ${{ github.event.client_payload.max_dispatches || inputs.max_dispatches || '1' }} - RETRY_HOURS: ${{ github.event.client_payload.retry_hours || inputs.retry_hours || '24' }} + RETRY_HOURS: ${{ github.event.client_payload.retry_hours || inputs.retry_hours || '1' }} AUTOFIX_WORKFLOW: pr-review-autofix.yml AUTOFIX_REPOSITORY: ContextualWisdomLab/.github - CANONICAL_REF: main steps: - - name: Checkout canonical scheduler + - name: Checkout immutable called-workflow source uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: - repository: ContextualWisdomLab/.github - ref: ${{ env.CANONICAL_REF }} + # For a reusable workflow the ordinary github context belongs to the + # caller. The job workflow context identifies the called workflow's + # repository and immutable resolved SHA, preventing a caller input or + # mutable branch from selecting the privileged scheduler code. + repository: ${{ job.workflow_repository }} + ref: ${{ job.workflow_sha }} fetch-depth: 1 persist-credentials: false From 5332731adb1e92007c6042cc61fb6615ffaf698a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 17:40:56 +0900 Subject: [PATCH 2/4] test(automation): pin hourly review repair cadence --- tests/test_hourly_review_fix_workflow.py | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/test_hourly_review_fix_workflow.py diff --git a/tests/test_hourly_review_fix_workflow.py b/tests/test_hourly_review_fix_workflow.py new file mode 100644 index 000000000..28287f401 --- /dev/null +++ b/tests/test_hourly_review_fix_workflow.py @@ -0,0 +1,30 @@ +"""Contract tests for the autonomous hourly review-feedback repair loop.""" + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def test_review_fix_scheduler_runs_hourly_with_bounded_retry() -> None: + """Keep feedback repair hourly, off minute zero, and bounded per PR head.""" + workflow = ( + REPO_ROOT / ".github" / "workflows" / "pr-review-fix-scheduler.yml" + ).read_text(encoding="utf-8") + + schedule_contract = workflow.split("schedule:", 1)[1].split("concurrency:", 1)[0] + retry_contract = workflow.split("retry_hours:", 1)[1].split( + "autofix_workflow:", 1 + )[0] + + assert 'cron: "23 * * * *"' in schedule_contract + assert 'cron: "23 */2 * * *"' not in schedule_contract + assert 'default: "1"' in retry_contract + assert ( + "RETRY_HOURS: ${{ github.event.client_payload.retry_hours || " + "inputs.retry_hours || '1' }}" + ) in workflow + assert ( + "MAX_DISPATCHES: ${{ github.event.client_payload.max_dispatches || " + "inputs.max_dispatches || '1' }}" + ) in workflow From ab5b5f5c16c686297d017c1ee3ff570a54cd2667 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 17:41:17 +0900 Subject: [PATCH 3/4] test(automation): pin immutable reusable workflow source --- ...test_pr_review_fix_scheduler_source_pin.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/test_pr_review_fix_scheduler_source_pin.py diff --git a/tests/test_pr_review_fix_scheduler_source_pin.py b/tests/test_pr_review_fix_scheduler_source_pin.py new file mode 100644 index 000000000..3e1db72ed --- /dev/null +++ b/tests/test_pr_review_fix_scheduler_source_pin.py @@ -0,0 +1,54 @@ +"""Supply-chain contract for the reusable PR-review autofix scheduler.""" + +from __future__ import annotations + +from pathlib import Path + + +_REPO_ROOT = Path(__file__).resolve().parents[1] +_WORKFLOW = _REPO_ROOT / ".github" / "workflows" / "pr-review-fix-scheduler.yml" + + +def _workflow_text() -> str: + """Read the reusable scheduler workflow as UTF-8 text.""" + return _WORKFLOW.read_text(encoding="utf-8") + + +def test_reusable_scheduler_checks_out_the_called_workflow_sha() -> None: + """Privileged scheduler code comes from the immutable called-workflow revision.""" + workflow = _workflow_text() + assert "repository: ${{ job.workflow_repository }}" in workflow + assert "ref: ${{ job.workflow_sha }}" in workflow + assert "persist-credentials: false" in workflow + + +def test_reusable_scheduler_source_is_not_caller_input_controlled() -> None: + """No caller-supplied ref or ordinary caller GitHub SHA selects trusted code.""" + workflow = _workflow_text() + assert "inputs.canonical_ref" not in workflow + assert "github.event.client_payload.canonical_ref" not in workflow + assert "ref: ${{ env.CANONICAL_REF }}" not in workflow + assert "ref: ${{ github.sha }}" not in workflow + + +def test_deprecated_canonical_ref_input_is_accepted_but_never_consumed() -> None: + """Existing callers can upgrade pins without controlling privileged source.""" + workflow = _workflow_text() + declaration = workflow.split("canonical_ref:", 1)[1].split( + "repository_dispatch:", 1 + )[0] + + assert "Deprecated compatibility input" in declaration + assert "ignored" in declaration + assert 'default: ""' in declaration + assert workflow.count("canonical_ref") == 1 + + +def test_reusable_scheduler_retains_least_privilege_and_bounded_dispatch() -> None: + """Source pinning does not broaden token scope or queue fan-out.""" + workflow = _workflow_text() + assert "contents: write" not in workflow + assert "pull-requests: write" not in workflow + assert "MAX_DISPATCHES:" in workflow + assert "RETRY_HOURS:" in workflow + assert "cancel-in-progress: true" in workflow From e01e52b32d237f16ce614b1760469f9c6f64a3c4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 19:07:01 +0900 Subject: [PATCH 4/4] security: require patched Strix transitive dependencies --- requirements-strix-ci.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-strix-ci.txt b/requirements-strix-ci.txt index e32bd39a9..adc733731 100644 --- a/requirements-strix-ci.txt +++ b/requirements-strix-ci.txt @@ -1,6 +1,7 @@ strix-agent==1.0.4 google-cloud-aiplatform==1.133.0 protobuf<7.0.0 -cryptography==49.0.0 +aiohttp==3.14.3 +cryptography==50.0.0 python-multipart==0.0.32 pyasn1==0.6.4