Skip to content

fix(review): authorize private target PR lookup with Noema App - #85

Closed
seonghobae wants to merge 5 commits into
mainfrom
fix/private-target-review-auth
Closed

fix(review): authorize private target PR lookup with Noema App#85
seonghobae wants to merge 5 commits into
mainfrom
fix/private-target-review-auth

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Purpose

Repair the central reviewer’s cross-repository trust bootstrap so private target repositories are supported without widening authority. GitHub documents the automatically generated GITHUB_TOKEN as repository-scoped to the repository containing the workflow; the previous central-review.yml used that Noema-repository token for the first target PR lookup before minting the target-scoped Noema App token.

Exact implementation

Freshly revalidated source identity:

  • Base branch: main; live/base tip 9adb784902cae19a150146a8fadd6a0491f67814.
  • Head branch: fix/private-target-review-auth.
  • Current exact head: 8d5d62437a944e8c1e9eda4a2b695edb3b5678e3.
  • Draft by operational-acceptance design.

The evidence-collection job now orders the trust bootstrap as:

syntactic target repository / PR / head validation
→ derive bounded repository name only
→ mint existing read-only Noema App token for exactly that repository
→ read and bind the live target PR with that App token
→ require open state + exact dispatched head + same target head/base repository
→ checkout trusted reviewer and exact target source

The target identifier step performs no target-state GitHub API request and has no GH_TOKEN. The first live repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER} read uses ${{ steps.noema_read_app.outputs.token }}. Downstream evidence collection already uses the same scoped App token, so this removes a credential-scope inconsistency rather than introducing another credential family.

No merge, release, deployment, contents-write, or broader publication authority is added.

Test-first lineage

  • 01d4f69c0c98c8aa95c79afe0dd8ac8835a09769: test-only contract requiring target validation → repository-scoped App token → authenticated live PR binding → checkout. The pre-fix workflow visibly violated this ordering and used ${{ github.token }} in the live target-binding step. GitHub-hosted ci, reviewer-ci, and Security Scan remained queued without runner assignment and were cancelled by concurrency when the branch advanced, so no executed RED run is falsely claimed.
  • a2084c9ce7c9c523227c5dad93162c1c8a4ba8f1: minimal workflow repair moving live target-state access behind the existing read-only target-scoped App token.
  • 575b840e08a33f4ee22815b97a0f5489fa0b66a4: APA 7 doctoring documenting GitHub token scope, failure behavior, and private-target acceptance.
  • 0b611e649878038828ff8c3dc8b39c9385455121: records the security/interoperability contract under CHANGELOG.md## Unreleased.
  • 8d5d62437a944e8c1e9eda4a2b695edb3b5678e3: current exact head; adds regression assertions for the existing review-dependent-check exclusion set so the independent reviewer waits on every review-independent current-head check while excluding only opencode-review and metadata-only gate evaluation. The exact comparison from 0b611... changes only reviewer/tests/test_central_review_workflow.py (+20/-0); production workflow source is unchanged by this final commit.

RCA and feasibility

Root cause: the first cross-repository API read used workflow-repository GITHUB_TOKEN authority even though the product supports targets beyond the Noema repository. Public targets can hide the defect because their PR metadata is public; private targets cannot rely on that authority.

Verified remediation path: the workflow already possesses a dedicated Noema GitHub App credential and already minted a repository-scoped read token for later target checkout/evidence reads. The narrow repair therefore performs only syntax validation before token creation, then uses that existing least-privilege token for the first live target read.

Rejected alternatives: broadening GITHUB_TOKEN, adding PAT-like credentials, making the target repository public, skipping exact-head binding, widening the App installation token to the whole organization, or treating public-repository success as proof of private-target authority.

Current exact-head evidence

For exact head 8d5d62437a944e8c1e9eda4a2b695edb3b5678e3:

  • ci run 31320482243: terminal failure. The application/repository tests and configured 100% coverage gates complete successfully before the unchanged repository-wide npm audit --audit-level=high boundary rejects inherited nanoid@3.3.16 (GHSA-2v37-7h3g-55p8). The root dependency remediation is PR fix(security): update transitive nanoid for CVE-2026-67213 #76; duplicating it here is rejected.
  • reviewer-ci run 31320482244: terminal success.
  • Central Security Scan run 31320482249: terminal success.
  • Formal GitHub reviews: none.
  • Current inline review threads: 0.

The earlier queued-run symptom has recovered for this exact head; it is not counted as a source remedy and remains tracked as organization/control-plane RCA under #30. The current CI failure is a distinct inherited dependency boundary, not a private-target-auth implementation failure.

Documentation

docs/doctoring/private-target-review-auth.md records the decision, failure/recovery behavior, acceptance contract, and current GitHub primary sources in APA 7 format. CHANGELOG.md records the behavior change under ## Unreleased.

Required acceptance before Ready / merge

  1. Integrate fix(security): update transitive nanoid for CVE-2026-67213 #76 under actual live governance so the inherited dependency audit can pass without an audit waiver.
  2. Refresh/revalidate this exact branch if the protected base moves materially, then require terminal-success ci, reviewer-ci, and Security Scan on the unchanged exact head.
  3. Review the current exact head and resolve only valid current findings; satisfy applicable qualifying independent-approval and ruleset requirements.
  4. Preserve 100% reviewer/production coverage and public-docstring gates.
  5. After protected integration, dispatch a real review against a private ContextualWisdomLab repository on which the Noema App is installed and prove exact-head evidence collection succeeds without permission broadening.
  6. Record protected-main operational evidence before claiming private-target support accepted for release/acquisition purposes.

Queued, pending, cancelled, absent, status-only, stale-head, predecessor-head, model-only, or synthetic evidence is never promoted to passing check, approval, or merge authority. No version bump or release is warranted for an unintegrated workflow repair.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b5b810e-1b1d-442f-a6b5-e86b03a35660

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review the unchanged exact head 8d5d62437a944e8c1e9eda4a2b695edb3b5678e3. Focus on the private-target authentication bootstrap, single-repository App-token scope, stale-head/refusal semantics, review-dependent check exclusion set, and whether any target-state read can occur with workflow-repository GITHUB_TOKEN. This review is diagnostic evidence only and does not substitute for qualifying independent merge approval.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Exact HEAD and changed files =='
git rev-parse HEAD
git status --short
git diff --name-status 9adb784902cae19a150146a8fadd6a0491f67814..8d5d62437a944e8c1e9eda4a2b695edb3b5678e3

printf '%s\n' '== Candidate workflow, test, and documentation files =='
fd -HI 'central_review|central-review|test_central_review_workflow|private-target-review-auth|CHANGELOG' . \
  -E .git -E node_modules | sort

printf '%s\n' '== Workflow and test outlines =='
for f in $(fd -HI 'central-review.yml|test_central_review_workflow.py' . -E .git -E node_modules); do
  echo "--- $f"
  ast-grep outline "$f" --items all || true
done

printf '%s\n' '== Target API calls and token-related assignments =='
rg -n -i -C 5 \
  '(repos/\$\{TARGET_REPOSITORY\}/pulls|/pulls/\$\{PR_NUMBER\}|gh\s+api|GH_TOKEN|github\.token|create-github-app-token|app[_ -]?token|TARGET_REPOSITORY|PR_NUMBER)' \
  .github reviewer \
  -g '*.yml' -g '*.yaml' -g '*.py' 2>/dev/null || true

printf '%s\n' '== Pull request metadata and current checks =='
gh pr view 85 --repo ContextualWisdomLab/noema \
  --json number,headRefOid,baseRefOid,isDraft,reviewDecision,reviews,statusCheckRollup \
  --jq '{number,headRefOid,baseRefOid,isDraft,reviewDecision,reviews: [.reviews[] | {author: .author.login, state}], checks: [.statusCheckRollup[] | {name, status, conclusion, headSha}]}' || true

Length of output: 95187


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Exact production and test diffs =='
git diff --unified=80 \
  9adb784902cae19a150146a8fadd6a0491f67814..8d5d62437a944e8c1e9eda4a2b695edb3b5678e3 \
  -- .github/workflows/central-review.yml reviewer/tests/test_central_review_workflow.py

printf '%s\n' '== Authentication bootstrap and evidence collection =='
sed -n '19,275p' .github/workflows/central-review.yml

printf '%s\n' '== Attestation and publication binding =='
sed -n '275,475p' .github/workflows/central-review.yml

printf '%s\n' '== Focused regression tests =='
cat -n reviewer/tests/test_central_review_workflow.py

printf '%s\n' '== All central-review gh API calls with step, environment, and source range =='
python3 - <<'PY'
from pathlib import Path

lines = Path(".github/workflows/central-review.yml").read_text(encoding="utf-8").splitlines()
for index, line in enumerate(lines):
    if "gh api" not in line:
        continue
    start = index
    while start > 0 and not lines[start].startswith("      - name:"):
        start -= 1
    end = index
    while end + 1 < len(lines) and not lines[end + 1].startswith("      - name:"):
        end += 1
    print(f"\n--- lines {start + 1}-{end + 1}: {lines[start].strip()}")
    print("\n".join(f"{number + 1:4}: {lines[number]}" for number in range(start, end + 1)))
PY

printf '%s\n' '== Python GitHub API entry points and environment token use =='
ast-grep outline reviewer/noema_reviewer/github_io.py --items all
rg -n -C 4 '(os\.environ|GH_TOKEN|GITHUB_TOKEN|subprocess\.run|default_runner|gh[" ]*,[" ]*"api")' \
  reviewer/noema_reviewer/github_io.py

Length of output: 60857


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Redaction-safe central-review credential and target-call inventory =='
python3 - <<'PY'
from pathlib import Path

lines = Path(".github/workflows/central-review.yml").read_text(encoding="utf-8").splitlines()
steps = []
current = None
for number, line in enumerate(lines, 1):
    if line.startswith("      - name:"):
        if current:
            steps.append(current)
        current = {"name": line.split(":", 1)[1].strip(), "start": number, "lines": []}
    if current:
        current["lines"].append(line)
if current:
    steps.append(current)

for step in steps:
    text = "\n".join(step["lines"])
    target_refs = (
        "TARGET_REPOSITORY" in text
        or "target-source" in text
        or "needs.collect_evidence.outputs.repository_name" in text
    )
    api_call = "gh api" in text or "gh attestation" in text or "fetch_manifest(" in text
    if target_refs or api_call:
        print(
            f'{step["start"]:>4} {step["name"]} | '
            f'target_ref={target_refs} gh_api={"gh api" in text} '
            f'gh_attestation={"gh attestation" in text} fetch_manifest={"fetch_manifest(" in text} '
            f'read_app_token={"steps.noema_read_app.outputs.token" in text} '
            f'write_app_token={"steps.noema_write_app.outputs.token" in text} '
            f'workflow_token={"github.token" in text}'
        )
PY

printf '%s\n' '== Every GitHub CLI invocation in reviewer I/O =='
rg -n -C 6 '"gh"|gh\s+api|graphql|GITHUB_TOKEN|GH_TOKEN' reviewer/noema_reviewer/github_io.py

printf '%s\n' '== Manifest collection and publication tail =='
sed -n '231,475p' .github/workflows/central-review.yml

printf '%s\n' '== Static integrity checks =='
git diff --check 9adb784902cae19a150146a8fadd6a0491f67814..8d5d62437a944e8c1e9eda4a2b695edb3b5678e3
git diff --numstat 9adb784902cae19a150146a8fadd6a0491f67814..8d5d62437a944e8c1e9eda4a2b695edb3b5678e3
git status --short

Length of output: 22492


@seonghobae No finding identified in the requested scope for exact head 8d5d62437a944e8c1e9eda4a2b695edb3b5678e3.

  • The target identifier step does only syntax validation and repository-name derivation. It makes no target GitHub API request.
  • The workflow mints noema_read_app for exactly ContextualWisdomLab/${repository_name} before the first repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER} read.
  • The initial PR bind, current-head polling, check-run polling, target checkout, and fetch_manifest use the read-only App token.
  • The publication job mints noema_write_app for the same single repository. Its live PR revalidation and review publication use that App token.
  • I found no target-state read in central-review.yml that uses github.token.
  • The collection job rejects a closed PR, a stale head, and a fork or base-repository mismatch. The publication job repeats these checks and also rejects a mismatched manifest repository, PR number, or head SHA.
  • The check waiter paginates all current-head check-run pages. It excludes only opencode-review and metadata-only gate evaluation.
  • git diff --check reports no whitespace errors.

This is static diagnostic evidence. It does not prove the Noema App installation can read a private target repository. The required live private-target dispatch remains necessary. The PR is also still a draft, and current metadata reports a completed failing verify check.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Superseded by clean protected-main successor #92 after #76 integrated. #92 starts from protected main c85d710 and preserves the three security-relevant unique artifacts exactly: central-review workflow behavior, deterministic reviewer regression, and APA 7 doctoring. The predecessor CHANGELOG.md blob is deliberately not replayed because it predates the protected nanoid entry; #92 explicitly requires a bounded changelog addition from the then-current protected file before Ready/merge. Old #85 checks/reviews do not transfer. Closing this stale branch removes a duplicate writer/evidence lane without treating #92 as accepted.

@seonghobae seonghobae closed this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant