mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:40:44 +00:00
fix(plugin-sdk): scan dependency tree before materialising openclaw symlink
The dependency-tree security scan rejects node_modules symlinks whose targets resolve outside the install root. Our trusted host-to-plugin symlink violates that rule by design, so running the scan AFTER linkOpenClawPeerDependencies would fail every install with SECURITY_SCAN_FAILED. Reorder afterInstall so the scan runs first (walking only the plugin's own staged source, catching any pre-existing malicious openclaw-named symlink a source might smuggle in), then the trusted link is materialised on the now-safe tree. Also use braces on guard clauses in the new unit tests to satisfy the oxlint no-unreachable-single-statement-if rule.
This commit is contained in:
committed by
Peter Steinberger
parent
56dd249a07
commit
44820f859e
@@ -2387,7 +2387,9 @@ describe("linkOpenClawPeerDependencies (via installPluginFromDir)", () => {
|
||||
const { result } = await installFromDirWithWarnings({ pluginDir, extensionsDir });
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const symlinkPath = path.join(result.targetDir, "node_modules", "openclaw");
|
||||
const stat = fs.lstatSync(symlinkPath);
|
||||
@@ -2404,7 +2406,9 @@ describe("linkOpenClawPeerDependencies (via installPluginFromDir)", () => {
|
||||
const { result } = await installFromDirWithWarnings({ pluginDir, extensionsDir });
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeModulesDir = path.join(result.targetDir, "node_modules");
|
||||
const symlinkPath = path.join(nodeModulesDir, "openclaw");
|
||||
@@ -2431,7 +2435,9 @@ describe("linkOpenClawPeerDependencies (via installPluginFromDir)", () => {
|
||||
expect(second.ok).toBe(true);
|
||||
expect(warnings).toHaveLength(0);
|
||||
|
||||
if (!second.ok) return;
|
||||
if (!second.ok) {
|
||||
return;
|
||||
}
|
||||
const symlinkPath = path.join(second.targetDir, "node_modules", "openclaw");
|
||||
expect(fs.lstatSync(symlinkPath).isSymbolicLink()).toBe(true);
|
||||
});
|
||||
|
||||
@@ -836,15 +836,13 @@ async function installPluginFromPackageDir(
|
||||
}
|
||||
},
|
||||
afterInstall: async (installedDir) => {
|
||||
// Symlink any openclaw peerDependencies into the plugin's node_modules/
|
||||
// so that plugins declaring openclaw as a peerDependency (rather than a
|
||||
// regular dependency) can resolve it at runtime.
|
||||
await linkOpenClawPeerDependencies({
|
||||
installedDir,
|
||||
peerDependencies: peerDeps,
|
||||
logger,
|
||||
});
|
||||
return await runInstallSourceScan({
|
||||
// Run the dependency-tree security scan BEFORE linking peer deps.
|
||||
// The scan rejects any node_modules/ symlink whose target resolves
|
||||
// outside the install root — a rule our trusted host-openclaw link
|
||||
// would fail by design. Running the scan first also keeps the check
|
||||
// honest against malicious plugins, because any pre-existing symlink
|
||||
// smuggled in by the source would still be present when we walk.
|
||||
const scanResult = await runInstallSourceScan({
|
||||
subject: `Plugin "${pluginId}"`,
|
||||
scan: async () =>
|
||||
await runtime.scanInstalledPackageDependencyTree({
|
||||
@@ -853,6 +851,15 @@ async function installPluginFromPackageDir(
|
||||
pluginId,
|
||||
}),
|
||||
});
|
||||
if (scanResult) {
|
||||
return scanResult;
|
||||
}
|
||||
await linkOpenClawPeerDependencies({
|
||||
installedDir,
|
||||
peerDependencies: peerDeps,
|
||||
logger,
|
||||
});
|
||||
return null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user