{"record":{"id":"8854ac877ee908c7","repo":"can1357/oh-my-pi","slug":"invalid-package-name-in-package-json-pkg-name","errorCode":null,"errorMessage":"Invalid package name in package.json: ${pkg.name}","messagePattern":"Invalid package name in package\\.json: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/installer.ts","lineNumber":174,"sourceCode":"\t\tthrow new Error(`package.json not found at ${absolutePath}`);\n\t}\n\n\tlet pkg: { name?: string };\n\ttry {\n\t\tpkg = await pkgFile.json();\n\t} catch (err) {\n\t\tthrow new Error(`Invalid package.json at ${absolutePath}: ${err}`);\n\t}\n\n\tif (!pkg.name || typeof pkg.name !== \"string\") {\n\t\tthrow new Error(\"package.json must have a valid name field\");\n\t}\n\n\t// Validate package name to prevent path traversal via pkg.name\n\tif (pkg.name.includes(\"..\") || pkg.name.includes(\"/\") || pkg.name.includes(\"\\\\\")) {\n\t\t// Exception: scoped packages have one slash\n\t\tif (!pkg.name.startsWith(\"@\") || (pkg.name.match(/\\//g) || []).length !== 1) {\n\t\t\tthrow new Error(`Invalid package name in package.json: ${pkg.name}`);\n\t\t}\n\t}\n\n\tawait ensurePluginsDir();\n\n\t// Create symlink in plugins/node_modules\n\tconst linkPath = path.join(PLUGINS_DIR, \"node_modules\", pkg.name);\n\n\t// For scoped packages, ensure the scope directory exists\n\tif (pkg.name.startsWith(\"@\")) {\n\t\tconst scopeDir = path.join(PLUGINS_DIR, \"node_modules\", pkg.name.split(\"/\")[0]);\n\t\tawait fs.mkdir(scopeDir, { recursive: true });\n\t}\n\n\t// Remove existing if present\n\ttry {\n\t\tconst stats = await fs.lstat(linkPath);\n\t\tif (stats.isSymbolicLink() || stats.isDirectory()) {","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/installer.ts#L156-L192","documentation":"The parsed name is checked for path-traversal payloads: `..`, `/`, or `\\`. Scoped npm names (starting with `@`) are allowed but only with exactly one slash. This error is thrown when the manifest's name would be unsafe to use as a directory name under plugins/node_modules.","triggerScenarios":"linkPlugin(localPath, cwd) reads a package.json whose name is `../../evil`, `a\\\\b`, contains slashes in non-scoped form, or a scoped name with more than one slash (e.g. `@scope/deep/name`).","commonSituations":"Malicious or typo'd manifests with traversal names; deeply scoped private registries using multi-slash names, which npm itself forbids; Windows-style names pasted into package.json.","solutions":["Rename the package in package.json to a valid npm name (lowercase, no `..`/`\\`, at most one slash only for `@scope/name` scoped form)","Move nested path components out of the name field into the actual directory structure","Re-run linkPlugin with the corrected manifest"],"exampleFix":"// before\n{ \"name\": \"@scope/team/plugin\" }\n// after\n{ \"name\": \"@scope-plugin\" } or { \"name\": \"@scope/plugin\" }","handlingStrategy":"validation","validationCode":"const name = (await Bun.file(path.join(dir, \"package.json\")).json()).name;\nconst unsafe = name.includes(\"..\") || name.includes(\"\\\\\") ||\n\t(name.includes(\"/\") && !(name.startsWith(\"@\") && (name.match(/\\//g) || []).length === 1));\nif (unsafe) throw new Error(`unsafe package name: ${name}`);","typeGuard":"function isSafePkgName(name: string): boolean {\n\tif (name.includes(\"..\") || name.includes(\"\\\\\")) return false;\n\tif (!name.includes(\"/\")) return true;\n\treturn name.startsWith(\"@\") && (name.match(/\\//g) || []).length === 1;\n}","tryCatchPattern":"try {\n\tawait linkPlugin(dir);\n} catch (err) {\n\tif (err instanceof Error && err.message.startsWith(\"Invalid package name in package.json\")) {\n\t\t// rename the package in its package.json\n\t}\n\tthrow err;\n}","preventionTips":["Follow npm naming rules: `@scope/pkg` with exactly one slash, or unscoped plain names","Never embed paths, `..`, or backslashes in the name field","Validate manifests from third parties before linking them"],"tags":["path-traversal","security","package-name-invalid","validation"],"backgroundTag":"unsafe-package-name","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}