Conversation
Reject UNC, absolute, and .. specifiers after resolve so inspect/check cannot read host files or contact a remote SMB share. Signed-off-by: Sebastien Tardif <SebTardif@ncf.ca>
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs real behavior proof before merge. Reviewed September 26, 2026, 4:31 AM ET / 08:31 UTC (Revision 6). ClawSweeper reviewWhat this changesThe branch rejects plugin-supplied path escapes during inspection, reports rejected OpenClaw checkout settings with an operator migration message, and adds documentation and tests. Merge readiness⛔ Blocked before merge - 6 items remain Current main still resolves plugin-controlled paths without these guards, so this PR remains useful. The earlier line-level findings are addressed, and the supplied live trace supports the lexical checks. Maintainer approval is still needed for the configuration break and the stated symlink and junction limit. Priority: P2 Review scores
Verification
How this fits togetherPlugin Inspector reads plugin manifests, package metadata, and optional OpenClaw checkout settings to produce inspection and compatibility reports. These path checks sit between plugin-supplied strings and filesystem reads. flowchart LR
A[Plugin files and config] --> B[Path checks]
B --> C{Inside plugin root?}
C -->|Yes| D[Filesystem inspection]
C -->|No| E[Rejection diagnostic]
D --> F[Inspection report]
E --> F
Decision needed
Why: The branch deliberately narrows the security guarantee and changes a previously documented configuration path; accepting both effects requires repository-owner judgment. Before merge
Findings
Agent review detailsSecurityNeeds attention: The guard blocks lexical escapes, but an accepted symlink or junction can still direct a filesystem read outside the plugin root. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Approve an explicit filesystem boundary and upgrade contract, then retain the narrow path guard with a tested migration route and final-I/O coverage appropriate to that boundary. Do we have a high-confidence way to reproduce the issue? Yes for the reported lexical escapes: the PR supplies a live production-entrypoint trace and focused tests. The remaining symlink or junction path was not exercised. Is this the best way to solve the issue? Unclear. Lexical rejection is a focused improvement, but it does not establish the full filesystem boundary suggested by the security goal, and the compatibility cost needs owner acceptance. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 6b7bad3d0c1d. LabelsLabel changes: No label changes. Label justifications:
EvidenceSecurity concerns:
What I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (5 earlier review cycles)
|
Do not scan the checkout when sourceRoot is outside the plugin. Do not fall through to ./openclaw when a plugin defaultCheckoutPath is rejected. Compare parent-directory segments so in-root names like ..generated stay valid. Operator configuredPath remains the sibling-checkout route. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
|
@clawsweeper re-review The three named findings are in the tip.
Live Owner decision still needed: should the public jail helpers guarantee realpath/symlink confinement before I/O, or stay lexical with that limit documented? This PR does not claim the stronger contract. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Resolve conflicts with main. Pass only options.openclawPath as the operator override so plugin defaultCheckoutPath stays in the manifest jail. Cover inspectPluginRoot rejection versus override. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
|
@clawsweeper re-review P1 keep-plugin-checkout-out-of-operator-override is in the tip.
Merged current Live Owner decision still needed on whether the public jail helpers guarantee realpath/symlink confinement. This PR does not claim that contract. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
Rejected plugin defaultCheckoutPath now carries a --openclaw / openclawPath diagnostic. Report that as target-openclaw-rejected instead of a generic unavailable log. Stop documenting ../openclaw as plugin checkout config. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
|
@clawsweeper re-review P1 make-rejected-legacy-checkout-actionable is in the tip. Rejected plugin Live production Owner decision still needed on whether the public jail helpers stay lexical or guarantee realpath/symlink confinement. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Stop exporting resolveJailedPluginPath and isWithinPluginRoot from the advanced public surface. They are lexical path checks, not a symlink-confinement API. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
|
@clawsweeper re-review Findings were already none on Crabpot source-mode smoke against this tip: FAIL, 60 fixtures, 83 breakages. The same smoke against plugin-inspector |
|
🦞🧹 I asked ClawSweeper to review this item again. |
What Problem This Solves
plugin-inspector inspect --no-openclawandcheckresolve plugin-owned path strings (package.jsonmain/module/exports, OpenClaw entrypoints,sourceRoot,openclaw.defaultCheckoutPath) withpath.resolveand thenexistsSync/readFile. There is no jail to the plugin root.On Windows,
path.resolve(pluginDir, "//evil.example/share/x.js")becomes a UNC path (\\evil.example\share\x.js).existsSyncon that specifier can contact a remote SMB share and leak NTLM material from a plugin the operator thought was offline and credential-free. Absolute paths and../also read host files outside the plugin.invalidPackageFileSpecalready rejects/and..forpackage.jsonfiles. This change applies the same idea after resolve: reject UNC, absolute, and escaped specifiers, and keep only paths inside the plugin root.A rejected
sourceRootno longer falls back to scanning the whole checkout. A rejected plugindefaultCheckoutPathno longer falls through to./openclaw. The report now emitstarget-openclaw-rejectedwith a--openclaw/openclawPathmigration. README and the copy-ready example no longer document../openclawas plugin checkout config. Parent-directory checks use the..segment, so in-root names like..generated/index.jsstay valid. Only--openclaw/options.openclawPathis the operator override. Jail helpers stay internal; they are lexical path checks, not a public symlink-confinement API.Evidence
Live
nodeof the patched helpers. A parent escape is rejected. An in-root..generatedname is kept. A badsourceRootthrows instead of scanning a replacement tree. A plugin-manifest../fake-openclawisrejected. The same path asconfiguredPathstill resolves as the operator route.Earlier Windows 11 inspect CLI at
f33adf2still stands: UNCmainand../entry were not added tosourceFiles.Real behavior proof
sourceRootfails instead of scanning the checkout. Rejected plugindefaultCheckoutPathis reported instead of silently selecting./openclaw./private/tmp/pi76-p1onfix/plugin-path-jail.node /tmp/proof-pi76-live.mjsagainst the patched helpers and against productioninspectPluginRootwith a plugin-owned../fake-openclawsetting, then again withopenclawPath: "../fake-openclaw".inspectPluginRootprintstarget-openclaw-rejectedand the--openclaw/openclawPathmigration.inspectPluginRoot_plugin_status rejected,inspectPluginRoot_operator_status ok.../openclawplugin setting is rejected with a migration message. Only an explicit operatoropenclawPathreads the sibling checkout. README and the copy-ready example no longer advertise that rejected setting.Crabpot source-mode smoke against this tip (
CRABPOT_PLUGIN_INSPECTOR_DIR=/private/tmp/pi76-p1 CRABPOT_PLUGIN_INSPECTOR_CLI=source npm run plugin-inspector:smoke): Status FAIL, 60 fixtures, 83 breakages. The same command against current plugin-inspectormainalso FAIL, 60 fixtures, 83 breakages, same top findings (agentchat/wecommissing-expected-seam). This PR does not change that consumer baseline.Summary
Jail plugin-controlled paths to the plugin root. Fail closed on invalid
sourceRoot. Report rejected plugin checkout paths instead of substituting another tree. Keep--openclaw/configuredPathas the operator sibling route.