{"record":{"id":"aad143c16389cb81","repo":"bmad-code-org/BMAD-METHOD","slug":"unsafe-ref-name-json-stringify-ref-aad143","errorCode":null,"errorMessage":"Unsafe ref name: ${JSON.stringify(ref)}","messagePattern":"Unsafe ref name: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tools/installer/modules/external-manager.js","lineNumber":27,"sourceCode":"const { decideChannelForModule } = require('./channel-plan');\nconst { getProjectRoot } = require('../project-root');\n\nconst VALID_CHANNELS = new Set(['stable', 'next', 'pinned']);\n\nfunction normalizeChannelName(raw) {\n  if (typeof raw !== 'string') return null;\n  const lower = raw.trim().toLowerCase();\n  return VALID_CHANNELS.has(lower) ? lower : null;\n}\n\n/**\n * Conservative quoting for tag names passed to git commands. Tags are\n * user-typed (--pin) or come from the GitHub API. Only allow the semver\n * character class we use to tag BMad releases; anything else throws.\n */\nfunction quoteShell(ref) {\n  if (typeof ref !== 'string' || !/^[\\w.\\-+/]+$/.test(ref)) {\n    throw new Error(`Unsafe ref name: ${JSON.stringify(ref)}`);\n  }\n  return `\"${ref}\"`;\n}\n\nasync function readChannelMarker(markerPath) {\n  try {\n    if (!(await fs.pathExists(markerPath))) return null;\n    const content = await fs.readFile(markerPath, 'utf8');\n    return JSON.parse(content);\n  } catch {\n    return null;\n  }\n}\n\nasync function writeChannelMarker(markerPath, data) {\n  try {\n    await fs.writeFile(markerPath, JSON.stringify({ ...data, writtenAt: new Date().toISOString() }, null, 2));\n  } catch {","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/tools/installer/modules/external-manager.js#L9-L45","documentation":"Thrown by quoteShell() in external-manager.js when a Git tag/ref passed to git commands fails the whitelist regex ^[\\w.\\-+/]+$. This is the shell injection guard for external module refs (tags resolved from the GitHub API or user-supplied --pin values). It mirrors quoteCustomRef but operates on the external module code path.","triggerScenarios":"The GitHub tags API returns a tag name with unexpected characters; a user passes --pin with a malformed value; resolved.ref from resolveChannel() contains characters outside the allowed set. The ref is used in 'git clone --branch' and 'git fetch ... tag' commands.","commonSituations":"A tag name contains characters like spaces, parentheses, or colons (rare but possible on non-standard repos); the channel resolver returned a ref with a newline or null byte from a malformed API response; a user typos the --pin value with shell metacharacters.","solutions":["Check the tag name for unexpected characters and use a clean semver tag.","If the tag comes from the GitHub API, verify the repo's tags on the web UI.","Ensure --pin values are simple alphanumeric/semver strings.","Report upstream if a legitimately tagged release has an unusual name."],"exampleFix":"// before\n// --pin mymodule=my tag with spaces\n\n// after\n// --pin mymodule=v1.2.3","handlingStrategy":"validation","validationCode":"function isValidExternalRef(ref) {\n  return typeof ref === 'string' && /^[\\w.\\-+/]+$/.test(ref);\n}\n\n// Before passing --pin values to external module install:\nconst pinTag = options.pin;\nif (pinTag && !isValidExternalRef(pinTag)) {\n  throw new Error(`Invalid tag name for --pin: ${pinTag}`);\n}","typeGuard":"function isSafeExternalRef(ref) {\n  return typeof ref === 'string' && ref.length > 0 && /^[\\w.\\-+/]+$/.test(ref);\n}","tryCatchPattern":"try {\n  await extMgr.cloneExternalModule(code, options);\n} catch (e) {\n  if (e.message.startsWith('Unsafe ref name')) {\n    console.error('The tag/ref contains invalid characters. Only letters, digits, dots, hyphens, underscores, plus, and slashes are allowed.');\n  }\n  throw e;\n}","preventionTips":["Validate --pin tag values against ^[\\w.\\-+/]+$ at the CLI boundary.","Use semver-formatted tags (v1.2.3) which always pass the whitelist.","Report tags with unusual characters to the upstream module maintainer."],"tags":["security","shell-injection","git","input-validation","external-modules"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}