Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

### 보안 패치 (CodeQL extended current-head)

- PR governance now rejects malformed repository identities before any GitHub
API call, with a regression fixture covering shell-metacharacter input.
- `cryptography`를 `50.0.0`으로 갱신해 공격자 제공 PKCS#7 EnvelopedData 복호화 결과의 오류·타이밍 차이로 발생하는 Bleichenbacher oracle(`CVE-2026-69247`, `GHSA-g6cj-pr64-35w5`)을 제거하고, backend·uv lock·hash lock·Strix CI 의존성 증거를 같은 버전으로 동기화했습니다. Strix 잠금은 `google-cloud-aiplatform==1.160.0`의 `<7` 제약을 위반하던 `protobuf==7.35.1`을 이미 검증된 `6.33.6`으로 복구해 다시 해석·설치 가능하게 했습니다.
- CodeQL `extended` 기본 설정이 current `develop`에서 확인한 Critical 8건·High 21건·Medium 1건을 코드 경계에서 제거합니다. 서버 요청은 검증된 loopback/HTTPS origin, 동일 OIDC issuer origin, 허용 API 경로·쿼리만 재구성하고 redirect를 자동 추종하지 않으며, 공개 IPv6 authority를 보존합니다. UI smoke는 고정 Node/Next 실행 파일과 인자, localhost:3001 allowlist, private `mkdtemp` artifact 디렉터리 및 containment 검사만 사용합니다.
- OIDC token endpoint는 운영 환경에서 서버 전용 `OIDC_ALLOWED_HOSTS` 정확 호스트 allowlist를 필수로 적용합니다. hostname의 모든 DNS 결과가 공인 주소인지 검증한 뒤 해당 주소 집합을 native HTTP(S) 연결의 `lookup`에 고정하고, 원래 issuer hostname은 Host/TLS SNI로 유지해 사설 주소 해석과 DNS rebinding 사이의 TOCTOU를 차단합니다. 실패 로그는 입력 URL·token 대신 고정된 configuration/DNS·transport/response/backend-verification reason code만 남깁니다.
Expand Down
3 changes: 3 additions & 0 deletions docs/development/merge-gate-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ awaited by default.
only; raw CLI diagnostics stay in the workflow run log. Inside Actions the
gate runs with a pinned system PATH so earlier steps cannot influence tool
resolution via GITHUB_PATH.
- The trusted-base metadata gate validates the repository identity before
constructing any GitHub API path and rejects malformed values without
invoking `gh`; PR numbers remain positive decimal identifiers.
- Authoritative `Review skipped` evidence counts only when the same check
output carries no blocking warning/failure language alongside it.
- `reviewDecision=CHANGES_REQUESTED` is a blocker until requested changes are
Expand Down
5 changes: 5 additions & 0 deletions scripts/ci/pr_governance_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
exit 1
fi

if ! [[ "${GITHUB_REPOSITORY:-}" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then
printf 'Repository identity is invalid; refusing to evaluate.\n'
exit 1
fi
Comment on lines +26 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Repository identity guard is sound

The added guard ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ requires exactly one slash and rejects shell metacharacters, running before OWNER/REPO extraction so malformed values fail closed before any gh call. Behaves as intended.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


OWNER="${GITHUB_REPOSITORY%/*}"
REPO="${GITHUB_REPOSITORY#*/}"
BLOCKERS=()
Expand Down
52 changes: 45 additions & 7 deletions scripts/ci/test_pr_governance_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ if [ "$1" = "api" ] && [[ "$2" == repos/*/commits/*/check-runs* ]]; then
coderabbit_pending)
printf '{"check_runs":[{"name":"CodeRabbit","app":{"slug":"coderabbitai"},"status":"in_progress","conclusion":null,"html_url":"https://checks/coderabbit"}]}'
;;
missing_coderabbit|missing_coderabbit_with_adversarial_approval|missing_coderabbit_stale_approval|missing_coderabbit_actions_approval|missing_coderabbit_one_probe|opencode_reviews_error|coderabbit_status_success|coderabbit_status_pending|coderabbit_status_failed|coderabbit_status_unknown|coderabbit_approval_pending_comment|coderabbit_approval_pending_walkthrough|coderabbit_multiline_approval_pending_comment|github_code_quality_approval_pending_comment|coderabbit_stale_head_with_unrelated_current_sha)
missing_coderabbit|missing_coderabbit_with_adversarial_approval|missing_coderabbit_stale_approval|missing_coderabbit_actions_approval|missing_coderabbit_one_probe|opencode_reviews_error|coderabbit_status_success|coderabbit_status_pending|coderabbit_status_failed|coderabbit_status_unknown|coderabbit_approval_pending_comment|coderabbit_approval_pending_walkthrough|coderabbit_multiline_approval_pending_comment|coderabbit_multiline_json_approval_pending_comment|github_code_quality_approval_pending_comment|coderabbit_stale_head_with_unrelated_current_sha|coderabbit_current_head_mixed_approval_pending_blocking_comment)
printf '{"check_runs":[]}'
;;
coderabbit_failed)
Expand Down Expand Up @@ -243,13 +243,13 @@ if [ "$1" = "api" ] && [[ "$args" == *repos/*/issues/42/comments* ]]; then
coderabbit_approval_pending_walkthrough)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nCodeRabbit has no unresolved comments, but it has not reviewed the latest commit.\\nCodeRabbit will approve the changes if it finds no pre-merge blocking issues.\\n- [ ] {\\\"headCommitId\\\":\\\"0123456789abcdef0123456789abcdef01234567\\\"}\\n<!-- approval_notice_end -->\\n<!-- walkthrough_start -->\\nGeneric walkthrough terms: Failure, Warning, Potential issue, and blocking comments are described here.\\n<!-- walkthrough_end -->"}]'
;;
coderabbit_multiline_approval_pending_comment)
coderabbit_multiline_json_approval_pending_comment)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nheadCommitId:\\n0123456789abcdef0123456789abcdef01234567\\n<!-- approval_notice_end -->"}]'
;;
coderabbit_stale_head_with_unrelated_current_sha)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nPotential issue remains under review.\\n- [ ] {\\"headCommitId\\":\\"old-head\\"}\\nThe current head is 0123456789abcdef0123456789abcdef01234567, but this is unrelated prose.\\n<!-- approval_notice_end -->"}]'
;;
coderabbit_mixed_approval_pending_blocking_comment)
coderabbit_current_head_mixed_approval_pending_blocking_comment)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nheadCommitId: old-head\\nBlocking issue: the current parser can cross tenant boundaries.\\nCurrent head is 0123456789abcdef0123456789abcdef01234567 in unrelated prose.\\n<!-- approval_notice_end -->"}]'
;;
coderabbit_current_failure_approval_notice)
Expand All @@ -261,6 +261,12 @@ if [ "$1" = "api" ] && [[ "$args" == *repos/*/issues/42/comments* ]]; then
coderabbit_current_potential_issue_approval_notice)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nheadCommitId: 0123456789abcdef0123456789abcdef01234567\\nPotential issue: the current parser can cross tenant boundaries.\\n<!-- approval_notice_end -->"}]'
;;
coderabbit_multiline_approval_pending_comment)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nCodeRabbit has no unresolved comments, but it has not reviewed the latest commit.\\nCodeRabbit will approve the changes if it finds no blocking issues.\\n- [ ] {\\"headCommitId\\":\\n \\"0123456789abcdef0123456789abcdef01234567\\"}\\n<!-- approval_notice_end -->"}]'
;;
coderabbit_mixed_approval_pending_blocking_comment)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nCodeRabbit has no unresolved comments, but it has not reviewed the latest commit.\\nBlocking issue: the current parser can cross tenant boundaries.\\n- [ ] {\\"headCommitId\\":\\"0123456789abcdef0123456789abcdef01234567\\"}\\n<!-- approval_notice_end -->"}]'
;;
Comment on lines +264 to +269

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Added multiline notice fixtures never run

The added stub branches coderabbit_multiline_approval_pending_comment and coderabbit_mixed_approval_pending_blocking_comment are referenced by no run_gate call; the multiline and mixed tests instead run the _json and _current_head variants (test_pr_governance_gate.sh, test_pr_governance_gate.sh). The genuinely multiline JSON headCommitId fixture the PR aims to cover is never exercised.

Prompt for agents
The two fake-gh scenarios added at scripts/ci/test_pr_governance_gate.sh:264 (coderabbit_multiline_approval_pending_comment) and :267 (coderabbit_mixed_approval_pending_blocking_comment) are dead: no run_gate call uses them. The tests assert_multiline_coderabbit_approval_pending_notice_does_not_block (line 804) and assert_mixed_coderabbit_approval_pending_notice_blocks (line 826) instead run the renamed coderabbit_multiline_json_approval_pending_comment and coderabbit_current_head_mixed_approval_pending_blocking_comment scenarios. Decide the intended coverage: either point these tests at the newly added genuinely-multiline fixtures (and add coderabbit_mixed_approval_pending_blocking_comment to the check-runs empty case at line 143 so the issue-comment path is reached), or add dedicated test functions that run the new scenarios and register them in the runner list at the bottom of the file. Remove whichever fixtures remain unreferenced so the fake gh has no dead branches.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

coderabbit_malformed_approval_pending_comment)
printf '[{"id":777,"user":{"login":"coderabbitai[bot]"},"created_at":"2026-05-19T00:01:00Z","body":"<!-- approval_notice_start -->\\n## Approval pending\\nPotential issue for 0123456789abcdef0123456789abcdef01234567"}]'
;;
Expand Down Expand Up @@ -713,6 +719,37 @@ assert_invalid_pr_number_fails_closed_without_gh_calls() {
fi
}

assert_invalid_repository_fails_closed_without_gh_calls() {
local temp_dir status
temp_dir="$(mktemp -d)"
mkdir -p "$temp_dir/bin"
make_fake_gh "$temp_dir/bin"
: > "$temp_dir/gh.log"
set +e
GH_LOG="$temp_dir/gh.log" \
GH_SCENARIO="pass" \
PATH="$temp_dir/bin:$PATH" \
GITHUB_REPOSITORY='owner/repo;echo injected' \
GH_TOKEN="fake" \
EVENT_NAME="workflow_dispatch" \
DIRECT_PR_NUMBER="42" \
TARGET_PR_NUMBER="" \
WORKFLOW_RUN_PR_NUMBER="" \
bash "$script" > "$temp_dir/output.txt" 2>&1
status=$?
set -e

if [ "$status" != "1" ]; then
printf 'expected exit 1 for invalid repository identity, got %s\n' "$status" >&2
return 1
fi
assert_in_file 'Repository identity is invalid; refusing to evaluate.' "$temp_dir/output.txt"
if [ -s "$temp_dir/gh.log" ]; then
printf 'expected no gh invocations for invalid repository identity\n' >&2
return 1
fi
}

assert_evaluation_error_publishes_gate_failure() {
local temp_dir
temp_dir="$(mktemp -d)"
Expand Down Expand Up @@ -764,7 +801,7 @@ assert_coderabbit_approval_pending_walkthrough_does_not_block() {
assert_multiline_coderabbit_approval_pending_notice_does_not_block() {
local temp_dir
temp_dir="$(mktemp -d)"
run_gate coderabbit_multiline_approval_pending_comment "$temp_dir"
run_gate coderabbit_multiline_json_approval_pending_comment "$temp_dir"

assert_exit_code 0 "$temp_dir"
assert_in_file 'Waiting for current-head CodeRabbit evidence' "$temp_dir/output.txt"
Expand All @@ -786,7 +823,7 @@ assert_stale_coderabbit_head_with_unrelated_current_sha_blocks() {
assert_mixed_coderabbit_approval_pending_notice_blocks() {
local temp_dir
temp_dir="$(mktemp -d)"
run_gate coderabbit_mixed_approval_pending_blocking_comment "$temp_dir"
run_gate coderabbit_current_head_mixed_approval_pending_blocking_comment "$temp_dir"

assert_exit_code 0 "$temp_dir"
assert_in_file 'Current-head CodeRabbit issue comment has blocking warning/failure evidence' "$temp_dir/gh.log"
Expand Down Expand Up @@ -1061,14 +1098,15 @@ assert_coderabbit_skip_with_blocking_language_blocks
assert_unrecognized_required_check_state_blocks
assert_pr_checks_error_is_not_published_verbatim
assert_invalid_pr_number_fails_closed_without_gh_calls
assert_invalid_repository_fails_closed_without_gh_calls
assert_evaluation_error_publishes_gate_failure
assert_coderabbit_blocking_issue_comment_blocks
assert_coderabbit_approval_pending_notice_does_not_block
assert_coderabbit_approval_pending_walkthrough_does_not_block
assert_multiline_coderabbit_approval_pending_notice_does_not_block
assert_coderabbit_approval_pending_walkthrough_does_not_block
assert_stale_coderabbit_head_with_unrelated_current_sha_blocks
assert_mixed_coderabbit_approval_pending_notice_blocks
assert_current_coderabbit_approval_notice_findings_block
assert_mixed_coderabbit_approval_pending_notice_blocks
assert_malformed_coderabbit_approval_pending_notice_blocks
assert_github_code_quality_approval_pending_notice_does_not_block
assert_github_code_quality_blocking_issue_comment_blocks
Expand Down