{"record":{"id":"c0cb41c596567f8c","repo":"neoclide/coc.nvim","slug":"invalid-extension-name-name","errorCode":null,"errorMessage":"Invalid extension name: ${name}","messagePattern":"Invalid extension name: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/extension/installer.ts","lineNumber":18,"sourceCode":"'use strict'\nimport { EventEmitter } from 'events'\nimport { createLogger } from '../logger'\nimport download, { DownloadOptions } from '../model/download'\nimport fetch, { FetchOptions } from '../model/fetch'\nimport { loadJson } from '../util/fs'\nimport { child_process, fs, minimatch, os, path, readline, semver } from '../util/node'\nimport { toText } from '../util/string'\nimport workspace from '../workspace'\nconst logger = createLogger('extension-installer')\nconst local_dependencies = ['coc.nvim', 'esbuild', 'webpack', '@types/node']\n\nfunction extensionPath(root: string, name: string | undefined): string {\n  // npm package names contain either one path component, or two for a scoped\n  // package.  Reject path syntax before using registry-controlled metadata in\n  // destructive filesystem operations.\n  if (typeof name !== 'string' || !/^(?:@[^/\\\\]+\\/)?[^/\\\\]+$/.test(name) || name.includes('\\0') || name.split('/').some(part => part === '.' || part === '..')) {\n    throw new Error(`Invalid extension name: ${name}`)\n  }\n  let resolvedRoot = path.resolve(root)\n  let target = path.resolve(resolvedRoot, name)\n  let relative = path.relative(resolvedRoot, target)\n  if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {\n    throw new Error(`Invalid extension name: ${name}`)\n  }\n  return target\n}\n\nexport interface Info {\n  'dist.tarball'?: string\n  'engines.coc'?: string\n  version?: string\n  name?: string\n}\n\nexport type Dependencies = Record<string, string>","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/extension/installer.ts#L1-L36","documentation":"extensionPath validates npm package names before touching the filesystem: the name must be a single path component or a scoped '@scope/pkg' name, without path separators, NUL bytes, '.'/'..' segments, or traversal. The first regex/NUL check throws for syntactically invalid names to prevent registry-controlled metadata from driving destructive filesystem operations.","triggerScenarios":"Calling extensionPath (directly or via getInfo, getInfoFromUri, install, folder, dest) with name = undefined, an empty string, a name containing '/' or '\\' beyond scope syntax, embedded NUL, or '.'/'..' segments.","commonSituations":"Parsing a malformed coc-extension URI; user typed 'coc-tsserver/' or '@scope/' in an install command; split on '/' producing empty parts; programmatic callers passing unparsed 'pkg@version' strings with slashes.","solutions":["Validate/normalize the extension name before calling: single component or '@scope/pkg'.","Strip version suffixes and URI encoding before passing the name.","Ensure the name is a non-empty string (guard undefined).","If the source is a URI, parse it properly to extract just the package name."],"exampleFix":"// before\nlet info = await installer.getInfo('coc-tsserver/../evil') // throws\n// after\nlet name = 'coc-tsserver' // clean npm name: /^(?:@[^/\\\\]+\\/)?[^/\\\\]+$/\nlet info = await installer.getInfo(name)","handlingStrategy":"validation","validationCode":"function isValidExtName(name: unknown): name is string {\n  return typeof name === 'string' &&\n    /^(?:@[^/\\\\]+\\/)?[^/\\\\]+$/.test(name) &&\n    !name.includes('\\0') &&\n    !name.split('/').some(p => p === '.' || p === '..')\n}","typeGuard":"function isExtensionName(v: unknown): v is string {\n  return typeof v === 'string' && /^(?:@[^/\\\\]+\\/)?[^/\\\\]+$/.test(v)\n}","tryCatchPattern":"try {\n  const p = extensionPath(root, name)\n} catch (e) {\n  if (e.message.startsWith('Invalid extension name')) {\n    throw new UserInputError(`'${name}' is not a valid npm/coc package name`)\n  } else throw e\n}","preventionTips":["Strip version specifiers ('pkg@1.2.3') and URI encoding before passing names.","Never pass filesystem paths or user-typed paths to install/info APIs.","Validate names with the same npm-name regex before calling.","Parse extension URIs properly to extract the bare package name."],"tags":["security","path-traversal","validation","input"],"backgroundTag":"invalid-package-name","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}