Skip to content

Harden review-only config isolation and close review submission TOCTOU window#28

Closed
dceoy wants to merge 6 commits into
mainfrom
fix/review-only-config-and-toctou
Closed

Harden review-only config isolation and close review submission TOCTOU window#28
dceoy wants to merge 6 commits into
mainfrom
fix/review-only-config-and-toctou

Conversation

@dceoy

@dceoy dceoy commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes two post-merge issues from #27 while preserving opencode github run, non-review behavior, the review-only permission boundary, and proposal-only /review-pr simplify.

1. Isolate review-only runs from caller-controlled OpenCode configuration

OPENCODE_DISABLE_PROJECT_CONFIG alone is insufficient. Review-only mode now:

  • unsets OPENCODE_CONFIG, OPENCODE_CONFIG_DIR, and OPENCODE_CONFIG_CONTENT, which upstream honors even when project config discovery is disabled;
  • unsets XDG_CONFIG_HOME and OPENCODE_TEST_HOME, preventing redirection to alternate global or home .opencode/ trees;
  • unsets OPENCODE_PERMISSION, preventing direct permission-policy overrides;
  • fails closed unless the resolved OpenCode version is a canonical x.y.z release at or above 1.1.29, the first release supporting OPENCODE_DISABLE_PROJECT_CONFIG.

The version-floor comparison is implemented with portable Bash numeric comparison and does not depend on GNU sort -V, so it also works on macOS runners.

2. Close the PR review submission TOCTOU window

review-pr-submit.sh now re-reads the live PR head and compares it with the pinned SHA immediately before both the initial review POST and review update PUT. A moved or unreadable head aborts the write, while the initial review remains pinned with commit_id.

Tests

Regression coverage verifies that:

  • a head move immediately before either review write prevents the API request;
  • all caller-controlled config path, content, home, and permission overrides are removed;
  • malicious redirected config trees are no longer selected after the guard runs;
  • the version floor accepts supported releases and rejects old or unparseable versions;
  • version comparison succeeds even when sort is unavailable;
  • action.yml applies the guard before opencode github run.

No GitHub permission scope changes are introduced.

🤖 Generated with Claude Code

…TOCTOU window

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dceoy dceoy self-assigned this Jul 11, 2026

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Found two issues that should be addressed before merge: review-only configuration isolation remains bypassable through additional environment-controlled config paths, and the version comparison introduces a macOS portability regression.

Comment thread .opencode/scripts/review-mode-guard.sh
Comment thread .opencode/scripts/review-mode-guard.sh

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Found one remaining configuration-isolation bypass. The previously reported environment-variable and macOS portability issues are resolved, and CI passes, but review-only mode can still load caller-controlled remote configuration through OpenCode's data directory.

Comment thread .opencode/scripts/review-mode-guard.sh
@dceoy
dceoy marked this pull request as ready for review July 11, 2026 17:07

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

PR Review Summary

No new actionable issues meeting the review’s reporting threshold were identified beyond the existing unresolved review thread.

Checked

  • Current diff for the review-only environment guard, OpenCode version-floor enforcement, and pre-submission PR-head revalidation.
  • Regression coverage for redirected configuration, macOS-portable version comparison, and head movement before review POST/PUT operations.
  • Existing review threads and current-head CI/CD status.
  • Repository ownership, archived status, PR author/state/draft status, and current full head SHA.

Notes

  • The existing unresolved critical thread concerning caller-controlled XDG_DATA_HOME and OpenCode data-directory configuration remains applicable and was not duplicated.
  • Current-head CI/CD completed successfully. The separate OpenCode review workflow was still in progress when checked.

Recommended Action

Address the existing unresolved data-directory isolation finding before merge. No additional automated blocking concerns were identified.

Co-authored-by: dceoy <dceoy@users.noreply.github.com>
@opencode-agent

Copy link
Copy Markdown

Triage Summary

Mode: normal (review feedback auto-pushed by opencode infrastructure).

Findings

# Reviewer File:Line Concern Disposition Action
1 dceoy (review 1) .opencode/scripts/review-mode-guard.sh:56 Must unset XDG_CONFIG_HOME, OPENCODE_TEST_HOME, OPENCODE_PERMISSION + regression test Already addressed (commit 0ac2e7a) resolve_only
2 dceoy (review 1) .opencode/scripts/review-mode-guard.sh:38 sort -V GNU-only; needs portable Bash comparison Already addressed (commit 0ac2e7a) resolve_only
3 dceoy (review 2) .opencode/scripts/review-mode-guard.sh:56 XDG_DATA_HOME still caller-controlled; needs fresh isolated data dir + test Valid, fixed locally reply_then_resolve after push

Changes Applied (review #3)

  • .opencode/scripts/review-mode-guard.sh: added XDG_DATA_HOME to the strip list; added new opencode_review_isolate_data_dir that creates a fresh mktemp -d directory (mode 700) and re-exports XDG_DATA_HOME to it. The unset-by-itself fallback to persistent $HOME/.local/share is the precise concern; pointing at an empty fresh dir is the smallest defensive fix.
  • action.yml: invokes the new function immediately after opencode_review_strip_config_env in the review-only branch of the Run OpenCode step.
  • README.md: documents the broader env-strip set and the XDG_DATA_HOME re-export.
  • .agents/skills/local-qa/scripts/test-review-mode-guard.bats: three new tests (malicious XDG_DATA_HOME/auth.json strip; re-export produces an empty 700 dir; malicious auth.json is not selected after the guard runs).
  • .agents/skills/local-qa/scripts/test-review-pr-read-only.bats: new test for XDG_DATA_HOME strip warning; extended the action.yml test to assert the new function is called.

Verification

  • bats for both suites: 12/12 + 5/5 = 17/17 pass
  • shellcheck on all changed shell/bats files: clean
  • yamllint on action.yml: clean
  • bash -n on both scripts: clean
  • git status: working tree contains the staged changes above; the opencode infrastructure will push.

Platform Action Queue (post-push)

  • Thread 1 (review 1, line 56): resolve_only — superseded by 0ac2e7a.
  • Thread 2 (review 1, line 38): resolve_only — superseded by 0ac2e7a.
  • Thread 3 (review 2, line 56): reply_then_resolve — reply with one sentence citing opencode_review_isolate_data_dir and the new regression test, then resolve.

No remaining open items on this PR.

github run

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Found one remaining review-only isolation bypass. The latest XDG_DATA_HOME fix is sound and current-head CI passes, but caller-controlled enable-toolkit: false skips installation/cleanup of the trusted global configuration while review-only mode still runs.

Comment thread action.yml Outdated

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Found one remaining review-only isolation bypass. The previously reported environment-variable, data-directory, portability, and head-revalidation issues are resolved, and current-head CI passes.

Comment thread action.yml

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

The review-only hardening is active, but the actual /review-pr execution path still bypasses the structured-review command and can succeed without producing inline comments. Two issues remain.

Comment thread action.yml
Comment thread action.yml
@dceoy
dceoy marked this pull request as draft July 11, 2026 17:55
@dceoy
dceoy marked this pull request as ready for review July 11, 2026 17:55

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Found one remaining review-only isolation bypass. Current CI jobs pass, and the previously reported configuration, dispatch, and submission-postcondition issues are resolved.

Comment thread action.yml
if: inputs.enable-toolkit == 'true'
if: inputs.enable-toolkit == 'true' || steps.review_mode.outputs.enabled == 'true'
shell: bash -euo pipefail {0}
env:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

critical: This now makes the existing fresh-copy branch the mandatory review-only security boundary, but that branch does not handle a symlinked ${HOME}/.opencode. [[ -d ... ]] succeeds for a directory symlink while the following find does not dereference the command-line symlink, so none of the target config, agents, commands, or plugins are removed. OpenCode still scans ${HOME}/.opencode and follows symlinks, allowing a pre-existing malicious tree on a persistent/self-hosted runner to bypass review-only permissions. Reject/remove a symlinked root and recreate a real directory while preserving only the trusted bin path; add a regression test for this case.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3fc69e08f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


if [[ "${prompt}" =~ ^/review-pr([[:space:]]+(.*))?$ ]]; then
arguments="${BASH_REMATCH[2]:-}"
elif [[ "${event_comment}" =~ /review-pr([[:space:]]+(.*))?$ ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require the comment to actually invoke /review-pr

In issue/PR comments such as /oc can you explain the /review-pr workflow?, review-only detection has already been enabled because /review-pr appears as a whitespace-delimited token, and this unanchored match then treats that mention as the trusted review command and replaces the user's prompt with the full review flow. That makes ordinary OpenCode requests that merely mention /review-pr run a structured PR review or fail the review-only gates; parse only an actual command position, e.g. /review-pr as the prompt or immediately after the configured mention.

Useful? React with 👍 / 👎.

@dceoy dceoy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

PR Review Summary

No new actionable issues meeting the review’s reporting threshold were identified beyond the existing unresolved review threads.

Checked

  • Current-head changes to review-only dispatch, trusted orchestrator selection, OpenCode configuration/data isolation, and PR-head revalidation before review writes.
  • Regression coverage for redirected configuration/data paths, command expansion, submission postconditions, and head movement before review POST/PUT operations.
  • Existing inline review threads and their current resolution/outdated state.
  • Current-head CI/CD status and the separate OpenCode review workflow state.
  • Repository ownership and archived status, PR author/state/draft status, base/head refs, changed files, current diff, and full head SHA.

Notes

  • Existing unresolved findings concerning a symlinked ${HOME}/.opencode bypass and overly broad /review-pr mention detection remain applicable and were not duplicated.
  • Current-head CI/CD completed successfully. The separate OpenCode review workflow was still in progress when checked.

Recommended Action

Address the existing unresolved review-only trust-boundary and command-detection findings before merge. No additional automated blocking concerns were identified.

@opencode-agent

Copy link
Copy Markdown

The structured submission cannot complete because the Edit/Write tools are blocked by the permission rules — the * deny override at the end of the rule list blocks writes to $HOME/.config/opencode/review-state/initial.json despite the earlier specific allow. The submit-initial helper validated the empty payload and rejected it as expected.

Per the instructions, I am failing the run rather than emitting the findings as a top-level completion comment.

Review status: failed at structured-submission step. The findings identified by the reviewers could not be written to the review-state payload file due to a write-permission override in the local environment (the * catch-all deny rule sits after the specific allow for the state files, and the Edit/Write tools are blocked). The context was pinned (repo dceoy/opencode-action, PR #28, head 3fc69e08f8b481ab3588db4894b6c9c7e5118c07), but the initial payload remained empty and the helper's jq -e validation aborted with ::error::Invalid initial review payload.

No review was posted to GitHub. To retry, the local permission configuration needs the * catch-all deny to be moved after (or the state-file allow moved before, depending on rule resolution) so that the Edit/Write tools can write to $HOME/.config/opencode/review-state/initial.json.

github run

@dceoy dceoy closed this Jul 11, 2026
@dceoy
dceoy deleted the fix/review-only-config-and-toctou branch July 13, 2026 04:19
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.

2 participants