Skip to content

fix(review): harden offline coverage sandbox - #687

Merged
seonghobae merged 10 commits into
mainfrom
codex/coverage-sandbox-git-safe
Aug 1, 2026
Merged

fix(review): harden offline coverage sandbox#687
seonghobae merged 10 commits into
mainfrom
codex/coverage-sandbox-git-safe

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • scope Git safe-directory configuration to the unprivileged coverage user so copied repositories remain usable without weakening host/global Git policy
  • prefetch trusted base JavaScript locks, admit only integrity-bounded lock transitions, and keep the install phase fully offline with lifecycle hooks disabled
  • reject symlink, traversal, untrusted lock, integrity, and mutation paths fail closed
  • defer R testthat package-not-found failures only for the package under test or strictly parsed base Suggests, using a root-owned immutable DESCRIPTION snapshot plus exact-head successful R CMD check evidence
  • make the NIM combined-budget regression deterministic under full coverage instrumentation without changing its one-second provider caps
  • preserve compact coverage failure evidence when npm lock/cache validation fails; treat both npm lock names consistently and reuse trusted base Git metadata

Production evidence

Verification

Current head ea99bd7:

  • coverage run -m pytest -q && coverage report — 780 passed; 6,267/6,267 central Python statements covered (100%)
  • interrogate -c pyproject.toml scripts/ci — 100% docstring coverage
  • post-review focused JavaScript/workflow contracts — 83 passed
  • NIM budget regression remains bounded by one-second provider attempt caps
  • Ruff, Actionlint, workflow shell syntax, git diff --check, and post-edit CodeGraph exploration passed

Closes #686
Unblocks ContextualWisdomLab/scopeweave#386
Unblocks ContextualWisdomLab/aFIPC#193

Summary by CodeRabbit

  • 개선 사항

    • npm·pnpm 의존성 검증과 오프라인 설치 절차를 강화했습니다.
    • 현재 소스 변경을 확인하는 보안 증거와 아티팩트 무결성 검증을 추가했습니다.
    • 검토 결과의 스키마 복구 및 재시도 처리를 개선했습니다.
  • 버그 수정

    • 검증되지 않은 lockfile과 패키지 입력 사용을 방지했습니다.
    • 교차 저장소 검토 실패와 승인 대기가 성공으로 처리되지 않도록 수정했습니다.
    • 누락되거나 불일치하는 검토 증거는 자동으로 실패 처리됩니다.
  • 테스트

    • JavaScript 의존성, R 선택적 의존성, 샌드박스 보안 검증을 확대했습니다.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 656432e6-4f9f-49ab-a912-73bf29b715cd

📥 Commits

Reviewing files that changed from the base of the PR and between a2958e5 and cf51e04.

📒 Files selected for processing (1)
  • tests/test_opencode_adversarial_receipts.py

📝 Walkthrough

Walkthrough

JavaScript lockfile을 base와 검증된 HEAD 기준으로 materialize합니다. npm 설치는 검증된 lockfile과 오프라인 writable cache를 사용합니다. R coverage gate는 DESCRIPTIONSuggests와 읽기 전용 snapshot을 사용합니다. 현재 HEAD source-line receipt와 승인 상태 검증도 강화합니다.

Changes

오프라인 커버리지 샌드박스 및 OpenCode 리뷰 검증

Layer / File(s) Summary
JavaScript lockfile materialization
scripts/ci/materialize_base_javascript_packages.py, tests/test_materialize_base_javascript_packages.py
npm과 pnpm 프로젝트를 수집합니다. npm lockfile의 구조, 경로, registry URL, workspace link, SHA-512 integrity를 검증합니다. materialized 입력에 lock blob SHA와 revision SHA를 기록합니다.
검증된 JavaScript 오프라인 설치
.github/workflows/opencode-review-dispatch.yml, tests/test_opencode_agent_contract.py, scripts/ci/test_strix_quick_gate.sh
npm과 pnpm 설치를 분기합니다. npm은 writable cache로 npm ci --ignore-scripts --offline을 실행합니다. 검증된 base 또는 HEAD lockfile만 허용합니다.
R DESCRIPTION 기반 coverage gate
scripts/ci/r_coverage_peer_gate.py, .github/workflows/opencode-review-dispatch.yml, scripts/ci/test_strix_quick_gate.sh, tests/test_r_coverage_peer_gate.py, tests/test_opencode_agent_contract.py
유효한 Suggests 패키지만 누락 허용 대상으로 분류합니다. R 실행 전에 DESCRIPTION을 읽기 전용 snapshot으로 저장하고 --description으로 전달합니다.
샌드박스 실행 계약 및 모델 재시도
.github/workflows/opencode-review-dispatch.yml, tests/test_opencode_agent_contract.py, tests/test_opencode_model_pool_runner.py, scripts/ci/run_opencode_review_model_pool.sh
샌드박스, R, 자문 실행 경로에 safe.directory=/work를 적용합니다. 일반 재시도와 schema repair 시도를 구분합니다.
현재 HEAD adversarial 증거와 승인 상태
scripts/ci/opencode_adversarial_receipts.py, .github/workflows/opencode-review-dispatch.yml, scripts/ci/opencode_dispatch_status.py, scripts/ci/opencode_review_prompt_template.md, tests/test_opencode_adversarial_receipts.py, tests/test_opencode_security_boundaries.py
현재 HEAD 변경 라인의 SHA-256 receipt를 생성합니다. 모델은 trusted receipt만 사용합니다. receipt 누락, cross-repository 도구 실패, 현재 HEAD 승인 부재를 fail closed로 처리합니다.

Estimated code review effort: 5 (Critical) | ~100 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OpenCodeWorkflow
  participant Materializer
  participant TrustedManifest
  participant NpmCache
  participant npm
  OpenCodeWorkflow->>Materializer: base_sha와 PR_HEAD_SHA 전달
  Materializer->>TrustedManifest: lock blob SHA와 revision SHA 기록
  TrustedManifest-->>OpenCodeWorkflow: 검증된 JavaScript 입력 반환
  OpenCodeWorkflow->>NpmCache: trusted cache를 writable 경로에 복사
  OpenCodeWorkflow->>npm: npm ci --ignore-scripts --offline 실행
Loading
sequenceDiagram
  participant OpenCodeWorkflow
  participant ReceiptCLI
  participant OpenCodeModel
  participant DispatchStatus
  OpenCodeWorkflow->>ReceiptCLI: merge base, HEAD, 변경 파일 전달
  ReceiptCLI-->>OpenCodeWorkflow: 현재 HEAD source-line receipt 반환
  OpenCodeWorkflow->>OpenCodeModel: bounded evidence와 trusted receipt 전달
  OpenCodeModel-->>OpenCodeWorkflow: 검증된 승인 결과 반환
  OpenCodeWorkflow->>DispatchStatus: 승인, coverage, HEAD 상태 전달
  DispatchStatus-->>OpenCodeWorkflow: 승인 상태 반환
Loading

Possibly related PRs

🚥 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 제목은 오프라인 커버리지 샌드박스의 보안 강화라는 주요 변경 사항을 명확하게 요약합니다.
Linked Issues check ✅ Passed 변경 사항은 이슈 [#686]의 Git 격리, 신뢰된 잠금 파일, 오프라인 설치, R 의존성, fail-closed 요구 사항을 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항은 오프라인 커버리지 샌드박스의 무결성, 검증, 재시도 및 현재 HEAD 검증 목표와 관련됩니다.
Docstring Coverage ✅ Passed Docstring coverage is 95.56% which is sufficient. The required threshold is 80.00%.
✨ 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 codex/coverage-sandbox-git-safe

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

@seonghobae seonghobae moved this from Todo to In Progress in naruon Platform Roadmap Jul 31, 2026

@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: 3

🧹 Nitpick comments (1)
scripts/ci/materialize_base_javascript_packages.py (1)

353-374: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

base_npm_projects(repo_root, base_sha)를 두 번 호출합니다.

Line 355와 Line 371에서 같은 base 리비전에 대해 동일한 수집을 반복합니다. 각 호출은 git ls-tree와 프로젝트별 git show를 다시 실행합니다. Line 356의 루프에서 이미 base lock blob을 계산하므로, base npm 결과와 blob 값을 재사용하십시오.

♻️ 제안 리팩터
     manifest: list[dict[str, str]] = []
     projects: list[tuple[str, str, dict[str, bytes], str, str]] = []
-    for source_path, package_manager, base_inputs in base_pnpm_projects(
-        repo_root, base_sha
-    ) + base_npm_projects(repo_root, base_sha):
+    base_npm = base_npm_projects(repo_root, base_sha)
+    base_npm_blobs: dict[str, str] = {}
+    for source_path, package_manager, base_inputs in (
+        base_pnpm_projects(repo_root, base_sha) + base_npm
+    ):
+        lock_blob = _lock_blob_sha(repo_root, base_sha, source_path)
         projects.append(
             (
                 source_path,
                 package_manager,
                 base_inputs,
                 base_sha.lower(),
-                _lock_blob_sha(repo_root, base_sha, source_path),
+                lock_blob,
             )
         )
+    for source_path, _package_manager, _base_inputs in base_npm:
+        base_npm_blobs[source_path] = _lock_blob_sha(
+            repo_root, base_sha, source_path
+        )
 
     if head_sha is not None:
         if not SHA_RE.fullmatch(head_sha):
             raise ValueError("head SHA must be exactly 40 hexadecimal characters")
-        base_npm_blobs = {
-            source_path: _lock_blob_sha(repo_root, base_sha, source_path)
-            for source_path, _package_manager, _base_inputs in base_npm_projects(
-                repo_root, base_sha
-            )
-        }
🤖 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/ci/materialize_base_javascript_packages.py` around lines 353 - 374,
Update the loop over base_pnpm_projects and base_npm_projects to retain the base
npm project data and computed lock blob values while building projects. Use that
retained data to construct base_npm_blobs instead of calling
base_npm_projects(repo_root, base_sha) again inside the head_sha block,
preserving the existing source-path-to-blob mapping.
🤖 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 @.github/workflows/opencode-review-dispatch.yml:
- Around line 1393-1406: Update the npm dependency branch around
trusted_npm_lock_is_materialized, prepare_writable_npm_cache, and the npm ci
fallback so lock or preparation failures are captured as an aggregated
validation failure instead of terminating the step under set -euo pipefail.
Ensure the failure path continues through the summary and GITHUB_OUTPUT
publication logic, including replacing the direct return 1 at the missing-lock
branch with the existing failure-recording mechanism.

In `@scripts/ci/materialize_base_javascript_packages.py`:
- Around line 161-168: Update the ownership-exclusion condition in
base_pnpm_projects to check whether the project contains any lockfile named in
NPM_LOCK_NAMES, rather than checking only package-lock.json. Keep
base_npm_projects using the same shared criterion so projects with
npm-shrinkwrap.json are excluded from pnpm collection and avoid the ValueError
path.

In `@tests/test_materialize_base_javascript_packages.py`:
- Line 559: 이스케이프되지 않은 정규식 메타문자를 포함한 pytest의 match 패턴을 수정하십시오. 해당 pytest.raises
호출에서 리터럴 마침표가 정규식 와일드카드로 해석되지 않도록 raw 문자열 이스케이프 또는 re.escape를 사용하고, 오류 메시지 매칭
동작은 유지하십시오.

---

Nitpick comments:
In `@scripts/ci/materialize_base_javascript_packages.py`:
- Around line 353-374: Update the loop over base_pnpm_projects and
base_npm_projects to retain the base npm project data and computed lock blob
values while building projects. Use that retained data to construct
base_npm_blobs instead of calling base_npm_projects(repo_root, base_sha) again
inside the head_sha block, preserving the existing source-path-to-blob mapping.
🪄 Autofix (Beta)

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: 9595e6c4-d27c-4fde-85a3-b5b2836c2434

📥 Commits

Reviewing files that changed from the base of the PR and between 2b034ac and 95768ff.

📒 Files selected for processing (8)
  • .github/workflows/opencode-review-dispatch.yml
  • scripts/ci/materialize_base_javascript_packages.py
  • scripts/ci/r_coverage_peer_gate.py
  • scripts/ci/test_strix_quick_gate.sh
  • tests/test_materialize_base_javascript_packages.py
  • tests/test_opencode_agent_contract.py
  • tests/test_opencode_model_pool_runner.py
  • tests/test_r_coverage_peer_gate.py

Comment thread .github/workflows/opencode-review-dispatch.yml
Comment thread scripts/ci/materialize_base_javascript_packages.py
Comment thread tests/test_materialize_base_javascript_packages.py Outdated

@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 (2)
tests/test_opencode_security_boundaries.py (1)

275-280: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

fixture 반환 타입 주석을 generator 타입으로 고치십시오.

trusted_dispatch_status_artifacts는 Line 310에서 yield를 사용합니다. 따라서 실제 반환 타입은 Iterator[None]입니다. 현재 주석 -> None은 부정확하며 정적 타입 검사가 오류를 보고할 수 있습니다.

♻️ 제안 수정
+from collections.abc import Iterator
+
 `@pytest.fixture`
 def trusted_dispatch_status_artifacts(
     tmp_path: Path,
     monkeypatch: pytest.MonkeyPatch,
-) -> None:
+) -> Iterator[None]:
     """Seal the source and changed-file evidence used by dispatch-status review validation."""
🤖 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 `@tests/test_opencode_security_boundaries.py` around lines 275 - 280, Update
the return annotation of the trusted_dispatch_status_artifacts pytest fixture to
Iterator[None], matching its yield-based implementation. Add or reuse the
appropriate typing import for Iterator if needed, without changing the fixture
behavior.
tests/test_opencode_adversarial_receipts.py (1)

26-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

임시 Git 저장소에 격리된 Git 환경을 사용하십시오.

로컬 identity 설정만으로는 전역 및 시스템 Git 설정을 차단하지 못합니다. 모든 Git 서브프로세스에 GIT_CONFIG_GLOBAL=/dev/nullGIT_CONFIG_SYSTEM=/dev/null을 적용하고, commit.gpgsign=falsecore.hooksPath=/dev/null을 설정하십시오. git init 전에 격리 환경을 적용하여 init.templateDir도 차단하십시오.

🤖 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 `@tests/test_opencode_adversarial_receipts.py` around lines 26 - 39, Update
initialized_repo to run every Git subprocess with an isolated environment
setting GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM to /dev/null before git init,
preventing inherited global, system, and init-template configuration; also
configure commit.gpgsign=false and core.hooksPath=/dev/null in the temporary
repository while preserving the deterministic local identity.
🤖 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 @.github/workflows/opencode-review-dispatch.yml:
- Around line 3294-3296: Update the review instructions so missing or
contradictory trusted evidence produces a schema-valid fail-closed result using
REQUEST_CHANGES, not NEEDS_INFO or a bare status substitution. Require that
result to include a finding and a confirmed adversarial probe, with all cited
path, line, and source-line-sha256 values copied only from the Adversarial probe
source-line receipts section.

In `@scripts/ci/opencode_review_prompt_template.md`:
- Line 49: Update the receipt-copying instruction near the example JSON in the
prompt template to include the example probe’s line value, preserving line: 1 as
a JSON number and requiring it to be replaced with the exact positive integer
from the same trusted receipt entry as path and source-line-sha256. Ensure the
copied path:line and hash remain consistent.

---

Nitpick comments:
In `@tests/test_opencode_adversarial_receipts.py`:
- Around line 26-39: Update initialized_repo to run every Git subprocess with an
isolated environment setting GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM to
/dev/null before git init, preventing inherited global, system, and
init-template configuration; also configure commit.gpgsign=false and
core.hooksPath=/dev/null in the temporary repository while preserving the
deterministic local identity.

In `@tests/test_opencode_security_boundaries.py`:
- Around line 275-280: Update the return annotation of the
trusted_dispatch_status_artifacts pytest fixture to Iterator[None], matching its
yield-based implementation. Add or reuse the appropriate typing import for
Iterator if needed, without changing the fixture behavior.
🪄 Autofix (Beta)

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: 2d6a365b-ae55-499c-8383-52574409ca0b

📥 Commits

Reviewing files that changed from the base of the PR and between ea99bd7 and a00959f.

📒 Files selected for processing (10)
  • .github/workflows/opencode-review-dispatch.yml
  • scripts/ci/opencode_adversarial_receipts.py
  • scripts/ci/opencode_dispatch_status.py
  • scripts/ci/opencode_review_prompt_template.md
  • scripts/ci/run_opencode_review_model_pool.sh
  • scripts/ci/test_strix_quick_gate.sh
  • tests/test_opencode_adversarial_receipts.py
  • tests/test_opencode_agent_contract.py
  • tests/test_opencode_model_pool_runner.py
  • tests/test_opencode_security_boundaries.py

Comment thread .github/workflows/opencode-review-dispatch.yml Outdated
Comment thread scripts/ci/opencode_review_prompt_template.md

@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: 1

🤖 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 `@tests/test_opencode_adversarial_receipts.py`:
- Around line 15-27: Update isolated_git_environment() to remove repository,
index, object-database, author, committer, and EMAIL environment variables that
can leak host state, including GIT_DIR, GIT_WORK_TREE, GIT_COMMON_DIR,
GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY, GIT_ALTERNATE_OBJECT_DIRECTORIES, all
GIT_AUTHOR_* and GIT_COMMITTER_* variables. Set a fixed test author and
committer identity and dates, and set GIT_TERMINAL_PROMPT to 0 while preserving
the existing Git configuration isolation.
🪄 Autofix (Beta)

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: 7d226c5a-321c-46d5-81bd-0f85a3a0a8c0

📥 Commits

Reviewing files that changed from the base of the PR and between a00959f and a2958e5.

📒 Files selected for processing (5)
  • .github/workflows/opencode-review-dispatch.yml
  • scripts/ci/opencode_review_prompt_template.md
  • tests/test_opencode_adversarial_receipts.py
  • tests/test_opencode_agent_contract.py
  • tests/test_opencode_security_boundaries.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • scripts/ci/opencode_review_prompt_template.md
  • tests/test_opencode_security_boundaries.py
  • tests/test_opencode_agent_contract.py
  • .github/workflows/opencode-review-dispatch.yml

Comment thread tests/test_opencode_adversarial_receipts.py
@seonghobae
seonghobae merged commit 2a83043 into main Aug 1, 2026
46 checks passed
@seonghobae
seonghobae deleted the codex/coverage-sandbox-git-safe branch August 1, 2026 01:18
@github-project-automation github-project-automation Bot moved this from In Progress to Done in naruon Platform Roadmap Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(review): harden cross-language offline coverage sandbox

1 participant