Skip to content

fix(security): isolate hourly readiness gh environment - #109

Closed
seonghobae wants to merge 5 commits into
mainfrom
fix/hourly-commercial-readiness-gh-env
Closed

fix(security): isolate hourly readiness gh environment#109
seonghobae wants to merge 5 commits into
mainfrom
fix/hourly-commercial-readiness-gh-env

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Purpose

Test-first repair for #108. The hourly commercial-readiness adapter let spawnSync("gh", ...) inherit the complete parent environment. This branch makes GitHub CLI subprocess authority explicit instead of forwarding ambient workflow/model/provider/runner credentials and configuration.

Exact source identity

  • protected base: main at c85d710804139c0697d7ef8fa47d02b1389e6d84;
  • branch: fix/hourly-commercial-readiness-gh-env;
  • RED head: e76d8be9023d5dfb344890cf7250e7f17ee65c04;
  • GREEN head: 05d6a0ae0f2c9d59d2557679c25db0ddbcdfa822.

RCA -> RED -> GREEN

runGh() used spawnSync("gh", ...) without an env option, so Node passed the complete ambient environment to gh. That unnecessarily exposed unrelated workflow/model/provider credentials and runner configuration to a subprocess whose only intended authority is GitHub CLI access.

The RED regression supplies a hostile synthetic parent containing GITHUB_TOKEN, NVIDIA NIM, Maintainer/Reviewer App private material, Cloudflare credentials, proxy state, HOME, NODE_OPTIONS, reviewer configuration and unrelated NOEMA_* state. It requires the child environment to contain only optional non-empty PATH, explicit GH_TOKEN, pinned GH_HOST=github.com, and deterministic NO_COLOR=1; an empty parent retains only the two pinned non-secret defaults. The source contract separately requires runGh() to pass that environment explicitly and forbids env: process.env.

Application CI run 31436718995, job 93612364591, checked out exact RED head e76d8be9023d5dfb344890cf7250e7f17ee65c04 and failed exactly the new contract: createGhSubprocessEnvironment was absent and runGh() did not pass the explicit environment. The rest of the suite passed.

GREEN 05d6a0ae0f2c9d59d2557679c25db0ddbcdfa822 exports the narrow child-environment builder and passes its result to spawnSync, preserving shell: false, bounded output, pagination, exact-head/live-base validation and all existing readiness/governance decisions.

Current exact-head proof

For unchanged head 05d6a0ae0f2c9d59d2557679c25db0ddbcdfa822:

  • application ci run 31436885124: terminal success; verify job 93612862199 checked out this exact SHA, reported 62/62 test files and 647/647 tests passing, and exposed 100% statements / 100% branches / 100% functions / 100% lines for the owned production coverage set;
  • reviewer-ci run 31436885115: terminal success;
  • central Security Scan run 31436885127: terminal success under live central workflow revision 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba / security-scan.yml blob c3b8fa5dbbb21a684209683a3d4b6d29c8e0d536;
  • current central semantics: PR-triggered only for bases main, master, or develop; OSV and dependency review are diff-scoped hard gates, fixable MEDIUM/HIGH/CRITICAL Trivy findings are repo-wide hard failures, and Scorecard is soft posture evidence;
  • the two code-quality findings emitted on the deliberately RED predecessor are resolved after the production export exists; current unresolved inline threads: 0;
  • visible formal review remains COMMENTED, not qualifying independent approval.

Boundary

This change narrows only the hourly readiness adapter's gh child-process environment. It does not change workflow permissions, reviewer/maintainer authority, merge/release/deployment authority, branch/ruleset policy, central .github workflows, KPI evidence, licensing posture, or acquisition claims.

Technical exact-head success is not merge authority. Any live qualifying independent-review requirement remains fail-closed; no self-approval, synthetic status, gate weakening, release or deployment is performed.

Related: #27, #29, #80, #108

Summary by CodeRabbit

  • 개선 사항

    • GitHub CLI 실행 시 필요한 환경 변수만 전달하도록 개선했습니다.
    • 호스트 설정과 색상 출력을 일관되게 관리해 실행 안정성을 높였습니다.
    • 셸을 거치지 않는 실행에서도 외부 환경 변수의 영향을 줄였습니다.
  • 보안 및 안정성

    • 보고서와 오류 출력에서 민감한 토큰이 노출되지 않도록 관련 검증을 강화했습니다.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75295ba0-95cf-4654-9b5a-8b15cec8c10e

📥 Commits

Reviewing files that changed from the base of the PR and between 05d6a0a and 11618bb.

📒 Files selected for processing (2)
  • scripts/hourly-commercial-readiness.mjs
  • test/hourly-commercial-readiness-script.test.ts
📝 Walkthrough

Walkthrough

GitHub CLI 하위 프로세스에 GH_HOST, NO_COLOR, 유효한 PATHGH_TOKEN만 전달하도록 환경 구성을 추가했습니다. runGh와 관련 테스트가 제한된 환경 사용을 검증합니다.

Changes

GitHub CLI 하위 프로세스 환경

Layer / File(s) Summary
하위 프로세스 환경 구성
scripts/hourly-commercial-readiness.mjs
createGhSubprocessEnvironment가 허용된 환경 변수를 생성합니다. runGh는 제한된 환경으로 gh를 실행합니다.
환경 격리 계약 검증
test/hourly-commercial-readiness-script.test.ts
허용 목록, 셸 없는 실행, 보고서 출력 및 오류 길이 제한을 검증합니다. 테스트는 ambient process.env의 직접 전달을 금지합니다.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant runGh
  participant createGhSubprocessEnvironment
  participant ghCLI
  runGh->>createGhSubprocessEnvironment: 환경 구성 요청
  createGhSubprocessEnvironment-->>runGh: 제한된 환경 반환
  runGh->>ghCLI: 제한된 환경으로 gh 실행
Loading

Possibly related issues

Possibly related PRs

  • ContextualWisdomLab/noema#107: 다른 스크립트와 함수에서 ambient process.env 상속을 명시적 허용 목록으로 변경하는 유사한 코드 수준 변경입니다.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 hourly readiness의 GitHub CLI 환경 격리라는 주요 변경 사항을 정확하고 간결하게 설명합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/hourly-commercial-readiness-gh-env

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.

Comment thread test/hourly-commercial-readiness-script.test.ts Fixed
Comment thread test/hourly-commercial-readiness-script.test.ts Fixed
@seonghobae
seonghobae marked this pull request as ready for review August 10, 2026 22:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/hourly-commercial-readiness-script.test.ts (1)

246-273: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

고정된 GH_HOSTNO_COLOR의 덮어쓰기도 검증하십시오.

현재 입력에는 GH_HOSTNO_COLOR의 공격자 값이 없습니다. 따라서 Line 262-Line 267의 기대값은 기본 상수가 존재하는지만 확인합니다. 입력에 GH_HOST: "evil.example"NO_COLOR: "0"을 추가하고, 결과가 계속 github.com"1"인지 확인하십시오.

As per coding guidelines, test/**/*.ts: Add or update regression tests for security and API behavior changes; use Vitest and preserve assertions covering token non-disclosure and protocol contracts.

회귀 입력 보강 예시
       NOEMA_MAINTENANCE_ENABLED: "true",
+      GH_HOST: "evil.example",
+      NO_COLOR: "0",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/hourly-commercial-readiness-script.test.ts` around lines 246 - 273, 보안
회귀 테스트의 입력에 공격자 제공 값인 GH_HOST: "evil.example"과 NO_COLOR: "0"을 추가하고,
createGhSubprocessEnvironment의 결과가 여전히 GH_HOST를 "github.com", NO_COLOR를 "1"로
고정하는지 검증하십시오. 기존의 GH_TOKEN 전달 및 민감한 환경 변수 제외 검증은 그대로 유지하십시오.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/hourly-commercial-readiness.mjs`:
- Around line 32-44: Update createGhSubprocessEnvironment so GH_TOKEN is
supplied through an explicit argument populated by the KV or credential
registry, rather than read from sourceEnvironment or process.env. Keep
process.env usage limited to non-secret configuration such as PATH, and include
the provided token in the child environment only when valid.

In `@test/hourly-commercial-readiness-script.test.ts`:
- Around line 300-307: Update the test describing bounded report and post-action
queue outputs to explicitly assert that GH_TOKEN and its token value are not
present in the script’s report, follow-up queue, or error output paths. Preserve
the existing protocol and bounded-length assertions for open_pull_request_count,
remaining_open_pull_request_count, report_path, remainingOpenPullRequestCount,
and MAX_ERROR_CHARS.

---

Nitpick comments:
In `@test/hourly-commercial-readiness-script.test.ts`:
- Around line 246-273: 보안 회귀 테스트의 입력에 공격자 제공 값인 GH_HOST: "evil.example"과
NO_COLOR: "0"을 추가하고, createGhSubprocessEnvironment의 결과가 여전히 GH_HOST를
"github.com", NO_COLOR를 "1"로 고정하는지 검증하십시오. 기존의 GH_TOKEN 전달 및 민감한 환경 변수 제외 검증은
그대로 유지하십시오.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 38261d6d-09b7-4cfe-a447-8113ec7921b8

📥 Commits

Reviewing files that changed from the base of the PR and between c85d710 and 05d6a0a.

📒 Files selected for processing (2)
  • scripts/hourly-commercial-readiness.mjs
  • test/hourly-commercial-readiness-script.test.ts

Comment on lines +32 to +44
export function createGhSubprocessEnvironment(sourceEnvironment = process.env) {
const childEnvironment = {
GH_HOST: "github.com",
NO_COLOR: "1",
};
if (typeof sourceEnvironment.PATH === "string" && sourceEnvironment.PATH.length > 0) {
childEnvironment.PATH = sourceEnvironment.PATH;
}
if (typeof sourceEnvironment.GH_TOKEN === "string" && sourceEnvironment.GH_TOKEN.length > 0) {
childEnvironment.GH_TOKEN = sourceEnvironment.GH_TOKEN;
}
return childEnvironment;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

GH_TOKENprocess.env에서 읽지 마십시오.

Line 32의 기본 인자와 Line 40의 접근은 process.env.GH_TOKEN을 직접 읽습니다. GH_TOKEN은 실제 자격 증명입니다. KV 또는 credential registry에서 토큰을 읽고, 명시적 입력으로 createGhSubprocessEnvironment에 전달하십시오. process.env를 기본 시크릿 소스로 사용하지 마십시오.

As per coding guidelines, scripts/*.mjs: Scripts may read process.env only for non-secret build-time configuration such as file paths and thresholds; real secrets must come from KV or a credential registry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/hourly-commercial-readiness.mjs` around lines 32 - 44, Update
createGhSubprocessEnvironment so GH_TOKEN is supplied through an explicit
argument populated by the KV or credential registry, rather than read from
sourceEnvironment or process.env. Keep process.env usage limited to non-secret
configuration such as PATH, and include the provided token in the child
environment only when valid.

Source: Coding guidelines

Comment thread test/hourly-commercial-readiness-script.test.ts Outdated
Comment thread test/hourly-commercial-readiness-script.test.ts Fixed
Comment thread test/hourly-commercial-readiness-script.test.ts Fixed
Comment thread test/hourly-commercial-readiness-script.test.ts Fixed

Copy link
Copy Markdown
Contributor Author

Superseded by fresh protected-main successor #225.

Fresh pre-closure proof:

The unresolved credential-source policy finding is not declared fixed or discarded by this supersession; issue #111 remains its explicit governance/architecture owner and blocks any claim that the current source satisfies the KV-only policy. #225 owns only the current-line child-environment and diagnostic-redaction repair. No checks, reviews, scanner/model output, or merge authority transfer from this stale-base PR.

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