[GH-ISSUE #2582] fix(verification): OpenCode setup-plan check accepts sibling skill paths #899

Closed
opened 2026-10-15 18:29:25 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2582

Summary

The OpenCode verification lane accepts a setup plan that adds a file outside its selected managed skill directory when the sibling directory shares the managed directory's name prefix. Its setup-plan target check therefore passes an out-of-tree action alongside the expected registration and skill actions. This is a failure of the verifier's containment check; no production setup write outside the selected directory was observed.

Steps to reproduce

From the repository root on the stated source baseline, run the following isolated probe. It reads the current verifier predicate and supplies a plan containing both required exact targets plus an additional sibling target. Native OpenCode and the complete client lane are not required for this check.

node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
const source = readFileSync('scripts/verify-opencode-client.mjs', 'utf8');
const expression = source.match(/\(a\) =>\s*a.target === configPath \|\| a.target.startsWith\(skillDirectory\)/)[0];
const configPath = '/fixture/profile/opencode.jsonc';
const skillDirectory = '/fixture/account/.agents/skills/reverse-engineer-anything';
const accepts = new Function('configPath', 'skillDirectory', `return ${expression}`)(configPath, skillDirectory);
const parsed = { status: 'planned', plannedActions: [
  { target: configPath },
  { target: skillDirectory },
  { target: skillDirectory + '-sibling/AGENTS.md' },
] };
assert.equal(parsed.status, 'planned');
assert(parsed.plannedActions.some(a => a.target === configPath));
assert(parsed.plannedActions.some(a => a.target === skillDirectory));
console.log('target check accepted:', parsed.plannedActions.every(accepts));
console.log('sibling is a descendant:', parsed.plannedActions[2].target.startsWith(skillDirectory + '/'));
JS

Expected behavior

The setup-plan check should reject the additional sibling action. It should accept the exact selected registration file, the managed skill directory itself, and files within that directory. Sharing a directory-name prefix does not make a sibling part of the managed directory.

Actual behavior

The current predicate printed target check accepted: true and sibling is a descendant: false. The plan also satisfied the preceding exact-target presence and status guards. Only the dry-run target check was executed; a native client run and production out-of-tree file writes were not performed.

Evidence

  • Expected source: docs/client-verification.md:46-53 documents setup-plan checks for this lane; scripts/verify-opencode-client.mjs:262-270 defines its required selected targets and attempted target restriction. scripts/verify-copilot-client.mjs:300-307 expresses the corresponding managed-directory containment rule with an exact root or directory separator.
  • Failure source: The source-extracted predicate at scripts/verify-opencode-client.mjs:265-269 accepted the three-target fixture above because its bare string-prefix comparison admits a sibling directory.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The optional installed-package runtime root supplies the setup JSON being checked. The false acceptance is in the verifier, rather than evidence that the current setup implementation produces this plan.

Restoration check

Run the same three-target check and require rejection of the sibling action. Controls containing only the selected registration file and managed root must pass, as must a plan that adds an actual child such as the root's SKILL.md. Preserve both required-target presence checks. Confirm the native lane still accepts a valid setup plan when its client prerequisites are available.

Runtime or environment

Source baseline: 8b181716ac. The isolated Node probe does not invoke OpenCode, install a package, or write configuration.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The target restriction accepts a plan containing an action outside the selected managed directory, so the lane can miss a setup-placement regression. The observed impact is limited to verification coverage.
  • Workaround status: unverified
  • Workaround basis: Independently reviewing all dry-run targets could detect the sibling, but a manual workaround was not exercised and no alternative lane-wide check was established.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2582 ## Summary The OpenCode verification lane accepts a setup plan that adds a file outside its selected managed skill directory when the sibling directory shares the managed directory's name prefix. Its setup-plan target check therefore passes an out-of-tree action alongside the expected registration and skill actions. This is a failure of the verifier's containment check; no production setup write outside the selected directory was observed. ## Steps to reproduce From the repository root on the stated source baseline, run the following isolated probe. It reads the current verifier predicate and supplies a plan containing both required exact targets plus an additional sibling target. Native OpenCode and the complete client lane are not required for this check. ```bash node --input-type=module <<'JS' import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; const source = readFileSync('scripts/verify-opencode-client.mjs', 'utf8'); const expression = source.match(/\(a\) =>\s*a.target === configPath \|\| a.target.startsWith\(skillDirectory\)/)[0]; const configPath = '/fixture/profile/opencode.jsonc'; const skillDirectory = '/fixture/account/.agents/skills/reverse-engineer-anything'; const accepts = new Function('configPath', 'skillDirectory', `return ${expression}`)(configPath, skillDirectory); const parsed = { status: 'planned', plannedActions: [ { target: configPath }, { target: skillDirectory }, { target: skillDirectory + '-sibling/AGENTS.md' }, ] }; assert.equal(parsed.status, 'planned'); assert(parsed.plannedActions.some(a => a.target === configPath)); assert(parsed.plannedActions.some(a => a.target === skillDirectory)); console.log('target check accepted:', parsed.plannedActions.every(accepts)); console.log('sibling is a descendant:', parsed.plannedActions[2].target.startsWith(skillDirectory + '/')); JS ``` ## Expected behavior The setup-plan check should reject the additional sibling action. It should accept the exact selected registration file, the managed skill directory itself, and files within that directory. Sharing a directory-name prefix does not make a sibling part of the managed directory. ## Actual behavior The current predicate printed `target check accepted: true` and `sibling is a descendant: false`. The plan also satisfied the preceding exact-target presence and status guards. Only the dry-run target check was executed; a native client run and production out-of-tree file writes were not performed. ## Evidence - Expected source: docs/client-verification.md:46-53 documents setup-plan checks for this lane; scripts/verify-opencode-client.mjs:262-270 defines its required selected targets and attempted target restriction. scripts/verify-copilot-client.mjs:300-307 expresses the corresponding managed-directory containment rule with an exact root or directory separator. - Failure source: The source-extracted predicate at scripts/verify-opencode-client.mjs:265-269 accepted the three-target fixture above because its bare string-prefix comparison admits a sibling directory. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The optional installed-package runtime root supplies the setup JSON being checked. The false acceptance is in the verifier, rather than evidence that the current setup implementation produces this plan. ## Restoration check Run the same three-target check and require rejection of the sibling action. Controls containing only the selected registration file and managed root must pass, as must a plan that adds an actual child such as the root's `SKILL.md`. Preserve both required-target presence checks. Confirm the native lane still accepts a valid setup plan when its client prerequisites are available. ## Runtime or environment Source baseline: 8b181716aca2cb467be499cba226abfb5ae19141. The isolated Node probe does not invoke OpenCode, install a package, or write configuration. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The target restriction accepts a plan containing an action outside the selected managed directory, so the lane can miss a setup-placement regression. The observed impact is limited to verification coverage. - Workaround status: unverified - Workaround basis: Independently reviewing all dry-run targets could detect the sibling, but a manual workaround was not exercised and no alternative lane-wide check was established.
bsakaguchi 2026-10-15 18:29:25 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Addressed on main in 4d4631ac9.

OpenCode verification accepts the selected skill directory or separator-delimited descendants and rejects sibling prefixes and parent escapes.

Validation: Negative containment assertions added; syntax/static/package gates passed.

Verification scope: The complete external OpenCode client lane was not rerun.

<!-- gh-comment-id:6158665379 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [4d4631ac9](https://github.com/morluto/rea/commit/4d4631ac9). OpenCode verification accepts the selected skill directory or separator-delimited descendants and rejects sibling prefixes and parent escapes. Validation: Negative containment assertions added; syntax/static/package gates passed. Verification scope: The complete external OpenCode client lane was not rerun.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#899
No description provided.