{"record":{"id":"349e3b12b2b93ade","repo":"n8n-io/n8n","slug":"invalid-package-specification","errorCode":null,"errorMessage":"Invalid package specification","messagePattern":"Invalid package specification","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/scan-community-package/scanner/scanner.mjs","lineNumber":58,"sourceCode":" *\n * @throws {UnexpectedError} If the resulting path is not contained within the parentPath.\n */\nexport function safeJoinPath(parentPath, ...paths) {\n\tconst candidate = path.join(parentPath, ...paths);\n\n\tif (!isContainedWithin(parentPath, candidate)) {\n\t\tthrow new Error(\n\t\t\t`Path traversal detected, refusing to join paths: ${parentPath} and ${JSON.stringify(paths)}`,\n\t\t);\n\t}\n\n\treturn candidate;\n}\n\nexport const resolvePackage = (packageSpec) => {\n\t// Validate input to prevent command injection\n\tif (!/^[a-zA-Z0-9@/_.-]+$/.test(packageSpec)) {\n\t\tthrow new Error('Invalid package specification');\n\t}\n\n\tlet packageName, version;\n\tif (packageSpec.startsWith('@')) {\n\t\tif (packageSpec.includes('@', 1)) {\n\t\t\t// Handle scoped packages with versions\n\t\t\tconst lastAtIndex = packageSpec.lastIndexOf('@');\n\t\t\treturn {\n\t\t\t\tpackageName: packageSpec.substring(0, lastAtIndex),\n\t\t\t\tversion: packageSpec.substring(lastAtIndex + 1),\n\t\t\t};\n\t\t} else {\n\t\t\t// Handle scoped packages without version\n\t\t\treturn { packageName: packageSpec, version: null };\n\t\t}\n\t}\n\t// Handle regular packages\n\tconst parts = packageSpec.split('@');","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/scan-community-package/scanner/scanner.mjs#L40-L76","documentation":"Thrown by resolvePackage when packageSpec fails the regex /^[a-zA-Z0-9@/_.-]+$/. The check is a command-injection guard: packageSpec is later interpolated into spawnSync('npm', ['-q', 'pack', `${packageName}@${version}`]) and must never carry shell metacharacters or whitespace.","triggerScenarios":"Calling resolvePackage with a spec containing spaces, quotes, semicolons, ampersands, pipe characters, backticks, '$', parentheses, or any non-ASCII character. Also rejects specs with backslashes or colons.","commonSituations":"Passing a git URL or tarball URL instead of a registry name (e.g. 'git+https://...'); a local file path spec ('./my-package'); a spec copied from a shell command that includes flags ('pkg --registry=...'); a typo introducing a disallowed character.","solutions":["Pass only a plain registry package spec: 'name', '@scope/name', 'name@version', or '@scope/name@version'.","If you need a git/url source, fetch and resolve it outside resolvePackage and feed the resulting name@version in.","Strip leading/trailing whitespace from user input before calling resolvePackage.","If a legitimate character is rejected, extend the allowlist regex deliberately rather than widening it broadly."],"exampleFix":"// before\nresolvePackage(userInput); // userInput = 'my pkg#' -> throws\n\n// after - normalize then validate\nconst spec = String(userInput ?? '').trim();\nif (!/^[a-zA-Z0-9@/_.-]+$/.test(spec)) {\n  throw new Error(`Unsupported package spec; use name@version form: ${JSON.stringify(spec)}`);\n}\nconst { packageName, version } = resolvePackage(spec);","handlingStrategy":"validation","validationCode":"const PACKAGE_SPEC_RE = /^[a-zA-Z0-9@/_.-]+$/;\n\nfunction isValidPackageSpec(spec: unknown): spec is string {\n  return typeof spec === 'string' && PACKAGE_SPEC_RE.test(spec) && spec.trim().length > 0;\n}\n\nif (!isValidPackageSpec(userInput)) {\n  throw new Error('Package spec must be name, @scope/name, name@version, or @scope/name@version');\n}","typeGuard":"function isRegistrySpec(spec: string): boolean {\n  // Reject git/url specs up front - they legitimately fail the regex but for a different reason.\n  return !spec.startsWith('git+') && !spec.startsWith('file:') && !spec.startsWith('http') && /^[a-zA-Z0-9@/_.-]+$/.test(spec);\n}","tryCatchPattern":null,"preventionTips":["Always validate package specs with the allowlist regex before calling resolvePackage.","Reject git/url/local specs explicitly with a clearer message than the generic regex rejection.","Trim whitespace from user input before validation.","Never broaden the regex to accept spaces or shell metacharacters - it exists to prevent command injection."],"tags":["security","command-injection","validation","scanner"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}