mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 19:40:41 +00:00
fix(plugins): canonicalize install provenance paths
This commit is contained in:
@@ -3,7 +3,7 @@ import { resolveUserPath } from "../utils.js";
|
||||
import type { PluginCandidate } from "./discovery.js";
|
||||
import { loadInstalledPluginIndexInstallRecordsSync } from "./installed-plugin-index-records.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { isPathInside, safeStatSync } from "./path-safety.js";
|
||||
import { isPathInside, safeRealpathSync, safeStatSync } from "./path-safety.js";
|
||||
import type { PluginRecord, PluginRegistry } from "./registry.js";
|
||||
import type { PluginLogger } from "./types.js";
|
||||
|
||||
@@ -44,15 +44,16 @@ function addPathToMatcher(
|
||||
if (!resolved) {
|
||||
return;
|
||||
}
|
||||
if (matcher.exact.has(resolved) || matcher.dirs.includes(resolved)) {
|
||||
const canonical = safeRealpathSync(resolved) ?? resolved;
|
||||
if (matcher.exact.has(canonical) || matcher.dirs.includes(canonical)) {
|
||||
return;
|
||||
}
|
||||
const stat = safeStatSync(resolved);
|
||||
const stat = safeStatSync(canonical);
|
||||
if (stat?.isDirectory()) {
|
||||
matcher.dirs.push(resolved);
|
||||
matcher.dirs.push(canonical);
|
||||
return;
|
||||
}
|
||||
matcher.exact.add(resolved);
|
||||
matcher.exact.add(canonical);
|
||||
}
|
||||
|
||||
function matchesPathMatcher(matcher: PathMatcher, sourcePath: string): boolean {
|
||||
@@ -101,16 +102,17 @@ function isTrackedByProvenance(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): boolean {
|
||||
const sourcePath = resolveUserPath(params.source, params.env);
|
||||
const canonicalSourcePath = safeRealpathSync(sourcePath) ?? sourcePath;
|
||||
const installRule = params.index.installRules.get(params.pluginId);
|
||||
if (installRule) {
|
||||
if (installRule.trackedWithoutPaths) {
|
||||
return true;
|
||||
}
|
||||
if (matchesPathMatcher(installRule.matcher, sourcePath)) {
|
||||
if (matchesPathMatcher(installRule.matcher, canonicalSourcePath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return matchesPathMatcher(params.index.loadPathMatcher, sourcePath);
|
||||
return matchesPathMatcher(params.index.loadPathMatcher, canonicalSourcePath);
|
||||
}
|
||||
|
||||
function matchesExplicitInstallRule(params: {
|
||||
@@ -120,11 +122,12 @@ function matchesExplicitInstallRule(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): boolean {
|
||||
const sourcePath = resolveUserPath(params.source, params.env);
|
||||
const canonicalSourcePath = safeRealpathSync(sourcePath) ?? sourcePath;
|
||||
const installRule = params.index.installRules.get(params.pluginId);
|
||||
if (!installRule || installRule.trackedWithoutPaths) {
|
||||
return false;
|
||||
}
|
||||
return matchesPathMatcher(installRule.matcher, sourcePath);
|
||||
return matchesPathMatcher(installRule.matcher, canonicalSourcePath);
|
||||
}
|
||||
|
||||
function resolveCandidateDuplicateRank(params: {
|
||||
|
||||
@@ -6273,6 +6273,74 @@ module.exports = {
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "does not warn when install paths resolve through a symlinked state root",
|
||||
loadRegistry: () => {
|
||||
useNoBundledPlugins();
|
||||
const stateDir = makeTempDir();
|
||||
const realHome = path.join(stateDir, "real-home");
|
||||
const linkedHome = path.join(stateDir, "linked-home");
|
||||
mkdirSafe(realHome);
|
||||
fs.symlinkSync(realHome, linkedHome, process.platform === "win32" ? "junction" : "dir");
|
||||
|
||||
const pluginDir = path.join(
|
||||
realHome,
|
||||
".openclaw",
|
||||
"npm",
|
||||
"node_modules",
|
||||
"@example",
|
||||
"tracked-symlink-install",
|
||||
);
|
||||
mkdirSafe(pluginDir);
|
||||
const plugin = writePlugin({
|
||||
id: "tracked-symlink-install",
|
||||
body: simplePluginBody("tracked-symlink-install"),
|
||||
dir: pluginDir,
|
||||
filename: "index.cjs",
|
||||
});
|
||||
writePersistedInstalledPluginIndexInstallRecordsSync(
|
||||
{
|
||||
[plugin.id]: {
|
||||
source: "npm",
|
||||
spec: "@example/tracked-symlink-install@1.0.0",
|
||||
installPath: path.join(
|
||||
linkedHome,
|
||||
".openclaw",
|
||||
"npm",
|
||||
"node_modules",
|
||||
"@example",
|
||||
"tracked-symlink-install",
|
||||
),
|
||||
version: "1.0.0",
|
||||
},
|
||||
},
|
||||
{ stateDir },
|
||||
);
|
||||
|
||||
const warnings: string[] = [];
|
||||
const registry = loadOpenClawPlugins({
|
||||
cache: false,
|
||||
logger: createWarningLogger(warnings),
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_STATE_DIR: stateDir,
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
|
||||
},
|
||||
config: {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
registry,
|
||||
warnings,
|
||||
pluginId: plugin.id,
|
||||
expectWarning: false,
|
||||
};
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
runScenarioCases(scenarios, (scenario) => {
|
||||
|
||||
Reference in New Issue
Block a user