Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/audit-central-ruleset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ jobs:
audit:
name: Audit organization ruleset 18156473
runs-on: ${{ github.repository == 'ContextualWisdomLab/.github' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('["ubuntu-24.04"]') }}
env:
GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
Expand All @@ -41,12 +39,18 @@ jobs:

- name: Read live inherited organization ruleset and repository scope
env:
GH_TOKEN: ${{ secrets.CWL_RULESET_AUDIT_TOKEN || '' }}
ORG_LOGIN: ContextualWisdomLab
RULESET_WRITE_CREDENTIAL_AVAILABLE: ${{ secrets.CWL_RULESET_AUDIT_TOKEN != '' }}
RULESET_ID: "18156473"
STACKED_RULESET_ID: "21732164"
RULESET_SENTINEL_REPOSITORY: naruon
run: |
set -euo pipefail
if [ "$RULESET_WRITE_CREDENTIAL_AVAILABLE" = "false" ]; then
echo "::error::Ruleset bypass audit requires CWL_RULESET_AUDIT_TOKEN with ruleset write access; github.token and review/merge credentials are not accepted as substitutes."
exit 1
fi
ruleset_json="$RUNNER_TEMP/central-required-workflow-ruleset.json"
ruleset_with_scope_json="$RUNNER_TEMP/central-required-workflow-ruleset-with-scope.json"
ruleset_error="$RUNNER_TEMP/central-required-workflow-ruleset.error"
Expand Down Expand Up @@ -118,6 +122,7 @@ jobs:
# unrelated failure.
if: always()
env:
GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }}
ORG_LOGIN: ContextualWisdomLab
ORG_WIDE_CREDENTIAL_AVAILABLE: ${{ secrets.PR_REVIEW_MERGE_TOKEN != '' || secrets.OPENCODE_APPROVE_TOKEN != '' }}
run: |
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
### Central ruleset audit detects standing bypass actors

- The ruleset audit now fails closed when the visible payload for ruleset
`18156473` exposes a persistent bypass actor or omits/malforms
`bypass_actors`. This closes the detector gap exposed by LineageWeave PR
#1153; it does not claim that the live actor has been removed. Protected
settings cleanup and a subsequent scheduled audit remain required evidence.

### Intel macOS native archives are bound to x86_64 bytes

- The release prescreener now requires every native member in an Intel macOS
Expand Down
11 changes: 11 additions & 0 deletions docs/org-required-workflow-rollout.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,17 @@ non-fork inventory snapshot and rollout ledger, not the ruleset target list.

## Risks and follow-up

- Ruleset `18156473` must not carry a persistent bypass actor. A genuine
circular-admission exception requires separate live authorization, exact-head
evidence, and immediate removal after the guarded operation; an `ALWAYS`
actor turns that exception into an unaudited ordinary merge path. The central
audit therefore treats any standing bypass actor, or a missing or malformed
`bypass_actors` collection that cannot prove its absence, as configuration
drift. The ruleset read uses only the dedicated `CWL_RULESET_AUDIT_TOKEN`,
provisioned with ruleset-write access so GitHub returns `bypass_actors`; an
absent credential fails before the read. The repository `github.token` and
review/merge credentials are not ruleset-audit fallbacks. The separate CodeQL
coverage step retains its org-repository read credentials.
- Existing open PRs may need a new push or base update before the latest required workflow SHA appears on their current head.
- The central OpenCode workflow now routes model-backed review through the canonical contextual-orchestrator contract; model/provider selection and fallback belong to that owner boundary, not workflow-local heuristics or paid fallback.
- The central OpenCode config includes a read-only `code-reviewer` subagent for focused review passes. The subagent may read, grep, glob, and run safe local verification commands, but it must not edit files, stage changes, commit, push, install dependencies, mutate branches, or touch production state.
Expand Down
6 changes: 6 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

이 문서는 제품·기술·운영 Gap을 현재 문서와 현재 GitHub 상태에 묶어 두는 기준선이다. 새 작업은 먼저 이 문서의 Gap ID를 PR 설명과 테스트 증거에 연결하고, PR의 정확한 exact HEAD·Checks·리뷰를 다시 수집한 뒤 구현한다. 표의 상태는 작성 시점의 관측값이므로, 병합 판단에는 재사용하지 않는다. 이 인벤토리는 스냅샷이며 merge authorization이 아니다.

### 2026-10-07 persistent-bypass incident delta

| Gap ID | 상태 | exact-head evidence | causal owner / next gate |
|---|---|---|---|
| CONTROL-RULESET-PERSISTENT-BYPASS-01 | **GREEN locally; protected integration, credential provisioning, and live ruleset remediation pending** | LineageWeave #1153 head `f18c736f4c51dea62d2f543134e077a3f9d9eb22` and squash result `03c575013e00fbce3d31bdc73eabd2bd13fa65e1` have the same tree, but the head had failed Tests and no APPROVED review. The active organization ruleset exposed an organization-admin `ALWAYS` bypass actor. RED proved `audit_ruleset()` accepted both that standing actor and a payload that omitted `bypass_actors`; GREEN reports the standing actor plus missing or malformed actor data as drift. The scheduled read now requires the dedicated `CWL_RULESET_AUDIT_TOKEN` capability and never falls back to `github.token`; an absent token or hidden `bypass_actors` fails closed. | `.github` owns `scripts/ci/audit_central_required_workflows.py` and the audit credential boundary. Merge this contract through exact-head Checks and independent review, provision `CWL_RULESET_AUDIT_TOKEN` with ruleset-write access, then remove the persistent actor from live ruleset `18156473` through the protected organization-admin settings path and capture the next scheduled audit success. LineageWeave #1170 preserves the complete Voice delta and retains only the incident record; neither lane retroactively certifies #1153. |

### 2026-09-19 exact-head incident delta

| Gap ID | 상태 | exact-head evidence | causal owner / next gate |
Expand Down
20 changes: 20 additions & 0 deletions scripts/ci/audit_central_required_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,26 @@ def audit_ruleset(payload: dict[str, Any]) -> list[str]:
if payload.get("enforcement") != "active":
errors.append("central ruleset enforcement is not active")

bypass_actors = payload.get("bypass_actors")
if "bypass_actors" not in payload:
errors.append("central ruleset bypass actor data is missing")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
elif not isinstance(bypass_actors, list):
errors.append("central ruleset bypass actor data is malformed")
elif bypass_actors:
actor_labels = sorted(
(
f"{actor.get('actor_type', '<missing>')}:"
f"{actor.get('actor_id', '<missing>')}:"
f"{actor.get('bypass_mode', '<missing>')}"
)
if isinstance(actor, dict)
else "<malformed>"
for actor in bypass_actors
)
errors.append(
f"central ruleset has persistent bypass actors: {actor_labels}"
)

conditions = payload.get("conditions")
conditions = conditions if isinstance(conditions, dict) else {}
repository_names = conditions.get("repository_name")
Expand Down
25 changes: 21 additions & 4 deletions tests/test_audit_org_codeql_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ def default_setup_scanning_nothing(name: str) -> dict:
}


def covered_by_recent_analysis(name: str, *, days_ago: int = 1) -> dict:
def covered_by_recent_analysis(
name: str, *, days_ago: int = 1, reference_time: datetime = NOW
) -> dict:
"""Return a repository payload covered by a recent, successful CodeQL analysis."""
created_at = (NOW - timedelta(days=days_ago)).isoformat().replace("+00:00", "Z")
created_at = (reference_time - timedelta(days=days_ago)).isoformat().replace(
"+00:00", "Z"
)
return {
"name": name,
"archived": False,
Expand Down Expand Up @@ -263,7 +267,13 @@ def test_null_latest_analysis_is_not_coverage() -> None:


def test_audit_codeql_coverage_defaults_now_to_current_time() -> None:
repositories = [covered_by_recent_analysis("DefaultNowRepo", days_ago=0)]
repositories = [
covered_by_recent_analysis(
"DefaultNowRepo",
days_ago=0,
reference_time=datetime.now(timezone.utc),
)
]

assert audit.audit_codeql_coverage(repositories) == []

Expand Down Expand Up @@ -302,7 +312,14 @@ def test_main_fail_path_reports_gaps_from_stdin(monkeypatch, capsys) -> None:
def test_main_pass_path_reports_from_file_arg(tmp_path, capsys) -> None:
payload_path = tmp_path / "repositories.json"
payload_path.write_text(
json.dumps([covered_by_default_setup("ELUNVERA"), covered_by_recent_analysis("Orgmetra")]),
json.dumps(
[
covered_by_default_setup("ELUNVERA"),
covered_by_recent_analysis(
"Orgmetra", reference_time=datetime.now(timezone.utc)
),
]
),
encoding="utf-8",
)

Expand Down
1 change: 1 addition & 0 deletions tests/test_central_required_workflow_exact_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def _ruleset_payload() -> dict:
"name": audit.RULESET_NAME,
"target": "branch",
"enforcement": "active",
"bypass_actors": [],
"conditions": {
"repository_name": {
"include": ["~ALL"],
Expand Down
78 changes: 78 additions & 0 deletions tests/test_central_required_workflow_ruleset_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def ruleset_payload() -> dict:
"name": "CWL Central required workflows",
"target": "branch",
"enforcement": "active",
"bypass_actors": [],
"conditions": {
"repository_name": {
"include": ["~ALL"],
Expand Down Expand Up @@ -118,6 +119,53 @@ def test_expected_central_ruleset_passes(monkeypatch, capsys) -> None:
)


def test_central_ruleset_rejects_persistent_bypass_actor() -> None:
"""A standing actor cannot bypass the exact-head review and check gates."""
payload = ruleset_payload()
payload["bypass_actors"] = [
{
"actor_id": 1,
"actor_type": "OrganizationAdmin",
"bypass_mode": "always",
}
]

assert audit.audit_ruleset(payload) == [
"central ruleset has persistent bypass actors: "
"['OrganizationAdmin:1:always']"
]


def test_central_ruleset_rejects_missing_bypass_actor_data() -> None:
"""An incomplete live payload cannot prove that no bypass actor exists."""
payload = ruleset_payload()
payload.pop("bypass_actors")

assert audit.audit_ruleset(payload) == [
"central ruleset bypass actor data is missing"
]


def test_central_ruleset_rejects_malformed_bypass_actor_data() -> None:
"""Unreadable bypass configuration fails closed."""
payload = ruleset_payload()
payload["bypass_actors"] = "invalid"

assert audit.audit_ruleset(payload) == [
"central ruleset bypass actor data is malformed"
]


def test_central_ruleset_describes_malformed_bypass_actor_entry() -> None:
"""A malformed standing actor remains visible in the drift reason."""
payload = ruleset_payload()
payload["bypass_actors"] = ["invalid"]

assert audit.audit_ruleset(payload) == [
"central ruleset has persistent bypass actors: ['<malformed>']"
]


def test_inherited_ruleset_and_organization_scope_probes_pass() -> None:
assert audit.audit_ruleset(inherited_ruleset_payload()) == []

Expand Down Expand Up @@ -378,6 +426,7 @@ def test_audit_reports_all_structural_and_protection_drift() -> None:
"expected ruleset name CWL Central required workflows",
"central ruleset target is not branch",
"central ruleset enforcement is not active",
"central ruleset bypass actor data is missing",
"central ruleset does not include all repositories",
"central ruleset repository exclusions drifted: expected ['.github', 'IRT-bibliography-set', 'noema'], got []",
"central ruleset does not target every default branch",
Expand Down Expand Up @@ -491,6 +540,35 @@ def test_scheduled_audit_and_rollout_document_semgrep_and_noema_requirements() -
assert "- `.github/workflows/sast-semgrep.yml`" in rollout


def test_ruleset_audit_requires_dedicated_write_credential() -> None:
"""The bypass audit must not confuse hidden fields with live drift."""
workflow = (REPO_ROOT / ".github/workflows/audit-central-ruleset.yml").read_text(
encoding="utf-8"
)
ruleset_step = workflow.split(
"- name: Read live inherited organization ruleset and repository scope\n", 1
)[1].split(" - name: ", 1)[0]
coverage_step = workflow.split(
"- name: Audit organization CodeQL coverage\n", 1
)[1].split(" - name: ", 1)[0]

assert (
"GH_TOKEN: ${{ secrets.CWL_RULESET_AUDIT_TOKEN || '' }}" in ruleset_step
)
assert (
"RULESET_WRITE_CREDENTIAL_AVAILABLE: "
"${{ secrets.CWL_RULESET_AUDIT_TOKEN != '' }}" in ruleset_step
)
assert 'if [ "$RULESET_WRITE_CREDENTIAL_AVAILABLE" = "false" ]; then' in ruleset_step
assert "requires CWL_RULESET_AUDIT_TOKEN with ruleset write access" in ruleset_step
assert "|| github.token" not in ruleset_step
assert "GH_TOKEN: ${{ github.token }}" not in ruleset_step
assert (
"GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN "
"|| secrets.OPENCODE_APPROVE_TOKEN || '' }}" in coverage_step
)


def test_audit_organization_codeql_coverage_step_has_freshness_and_credential_guard() -> None:
workflow = (REPO_ROOT / ".github/workflows/audit-central-ruleset.yml").read_text(
encoding="utf-8"
Expand Down
Loading