{"record":{"id":"13a3ac0526c06558","repo":"can1357/oh-my-pi","slug":"invalid-marketplace-name-for-cache-marketplace","errorCode":null,"errorMessage":"Invalid marketplace name for cache: \"${marketplace}\"","messagePattern":"Invalid marketplace name for cache: \"(.+?)\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/cache.ts","lineNumber":33,"sourceCode":"import * as path from \"node:path\";\n\nimport { isEnoent } from \"@oh-my-pi/pi-utils\";\n\nimport { isValidNameSegment } from \"./types\";\n\n// Reject anything that could be used for path traversal or shell injection in\n// version strings. Only printable, unambiguous characters are allowed.\nconst VERSION_RE = /^[a-zA-Z0-9._+-]+$/;\n\n/** Return true when `version` is safe for use as a cache path component. */\nexport function isValidVersionForCache(version: string): boolean {\n\t// prevent path-traversal sequences like \"..\" or \"1..2\"\n\treturn version.length > 0 && version.length <= 128 && VERSION_RE.test(version) && !version.includes(\"..\");\n}\n\nfunction validateCacheComponents(marketplace: string, pluginName: string, version: string): void {\n\tif (!isValidNameSegment(marketplace)) {\n\t\tthrow new Error(`Invalid marketplace name for cache: \"${marketplace}\"`);\n\t}\n\tif (!isValidNameSegment(pluginName)) {\n\t\tthrow new Error(`Invalid plugin name for cache: \"${pluginName}\"`);\n\t}\n\tif (!isValidVersionForCache(version)) {\n\t\tthrow new Error(`Invalid version for cache: \"${version}\"`);\n\t}\n}\n\n/**\n * Return the absolute path for a cached plugin directory.\n * Throws if any component fails validation.\n */\nexport function getCachedPluginPath(\n\tcacheDir: string,\n\tmarketplace: string,\n\tpluginName: string,\n\tversion: string,","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/cache.ts#L15-L51","documentation":"The marketplace cache builds directory names as <marketplace>___<pluginName>___<version>, so every component is validated with isValidNameSegment (lowercase alnum + hyphens, max 64 chars) to prevent path traversal. A marketplace value failing that check throws this error before any filesystem operation.","triggerScenarios":"Calling getCachedPluginPath (directly or via cache lookup during plugin install) with a marketplace containing uppercase letters, slashes, dots, spaces, or exceeding 64 chars.","commonSituations":"Marketplace name parsed from a URL with protocol/slashes included; user-supplied marketplace string not normalized; case-preserved names from an upstream catalog.","solutions":["Normalize the marketplace identifier to lowercase alphanumeric + hyphens before caching.","Strip protocols, whitespace, and path separators from the marketplace string.","Check length is ≤ 64 characters.","Fix the source that produced the marketplace name (catalog field or CLI argument)."],"exampleFix":"// before\ngetCachedPluginPath(dir, \"https://github.com/acme/mkt\", \"plugin\", \"1.0.0\");\n// after\ngetCachedPluginPath(dir, \"acme-mkt\", \"plugin\", \"1.0.0\");","handlingStrategy":"validation","validationCode":"import { isValidNameSegment } from \".../marketplace/types\";\nif (!isValidNameSegment(marketplace)) {\n  throw new Error(`Sanitize marketplace name before caching: ${marketplace}`);\n}","typeGuard":"function isCacheSafeName(s: string): boolean {\n  return s.length > 0 && s.length <= 64 && /^[a-z0-9-]+$/.test(s);\n}","tryCatchPattern":"try {\n  const p = getCachedPluginPath(dir, marketplace, plugin, version);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Invalid marketplace name\")) {\n    console.error(`Marketplace id \"${marketplace}\" must be lowercase alnum/hyphens (≤64)`);\n  } else throw err;\n}","preventionTips":["Normalize marketplace ids to lowercase alnum + hyphens at ingestion time","Strip protocols and separators from URLs before deriving a marketplace name","Enforce the ≤64-char limit when creating marketplaces","Never pass raw user/URL strings as cache path components"],"tags":["plugins","marketplace","validation","path-traversal","cache"],"backgroundTag":"invalid-path-component","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}