fix(cursor): add path traversal checks and require per-plugin manifest

Address two issues flagged in PR review:

1. Validate pluginRoot and source with isSafeRelativePath before using
   them in path.resolve. Without this, absolute paths or .. traversals
   in marketplace.json would pass validation silently.

2. Error when the per-plugin .cursor-plugin/plugin.json is missing
   instead of silently skipping it. A missing manifest would prevent
   the plugin from loading, so the validator should catch it.

Made-with: Cursor
This commit is contained in:
DimoHG
2026-03-10 13:27:23 +02:00
parent 2e3d42fc51
commit eba2af2c64
+14 -2
View File
@@ -187,6 +187,11 @@ async function validateMarketplace(marketplacePath) {
const pluginRoot = marketplace.metadata?.pluginRoot || "";
if (pluginRoot && !isSafeRelativePath(pluginRoot)) {
addError(`marketplace: metadata.pluginRoot has invalid path "${pluginRoot}".`);
return;
}
for (const entry of marketplace.plugins) {
if (
typeof entry.name !== "string" ||
@@ -207,6 +212,11 @@ async function validateMarketplace(marketplacePath) {
continue;
}
if (!isSafeRelativePath(source)) {
addError(`${entry.name}: source has invalid path "${source}".`);
continue;
}
const pluginDir = path.resolve(repoRoot, pluginRoot, source);
if (!(await pathExists(pluginDir))) {
addError(`${entry.name}: resolved plugin directory does not exist: ${pluginDir}`);
@@ -214,9 +224,11 @@ async function validateMarketplace(marketplacePath) {
}
const perPluginManifest = path.join(pluginDir, ".cursor-plugin", "plugin.json");
if (await pathExists(perPluginManifest)) {
await validatePluginManifest(perPluginManifest, pluginDir, `${entry.name} plugin.json`);
if (!(await pathExists(perPluginManifest))) {
addError(`${entry.name}: per-plugin manifest is missing: ${perPluginManifest}`);
continue;
}
await validatePluginManifest(perPluginManifest, pluginDir, `${entry.name} plugin.json`);
}
}