{"record":{"id":"a65359d36c198a3f","repo":"abhigyanpatwari/GitNexus","slug":"invalid-group-name-name-names-must-start-wit","errorCode":null,"errorMessage":"Invalid group name \"${name}\". Names must start with a letter or digit and contain only [a-zA-Z0-9_-].","messagePattern":"Invalid group name \"(.+?)\"\\. Names must start with a letter or digit and contain only \\[a-zA-Z0-9_-\\]\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/group/storage.ts","lineNumber":22,"sourceCode":"import * as os from 'node:os';\nimport type { ContractRegistry } from './types.js';\nimport { writeFileAtomic } from '../../storage/fs-atomic.js';\n\nconst CONTRACTS_FILE = 'contracts.json';\n\nexport function getDefaultGitnexusDir(): string {\n  return process.env.GITNEXUS_HOME || path.join(os.homedir(), '.gitnexus');\n}\n\nexport function getGroupsBaseDir(gitnexusDir?: string): string {\n  return path.join(gitnexusDir || getDefaultGitnexusDir(), 'groups');\n}\n\nconst GROUP_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;\n\nexport function validateGroupName(name: string): void {\n  if (!GROUP_NAME_RE.test(name)) {\n    throw new Error(\n      `Invalid group name \"${name}\". Names must start with a letter or digit and contain only [a-zA-Z0-9_-].`,\n    );\n  }\n}\n\nexport function getGroupDir(gitnexusDir: string, groupName: string): string {\n  validateGroupName(groupName);\n  return path.join(gitnexusDir, 'groups', groupName);\n}\n\nexport async function writeContractRegistry(\n  groupDir: string,\n  registry: ContractRegistry,\n): Promise<void> {\n  await writeFileAtomic(path.join(groupDir, CONTRACTS_FILE), JSON.stringify(registry, null, 2));\n}\n\nexport async function readContractRegistry(groupDir: string): Promise<ContractRegistry | null> {","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/group/storage.ts#L4-L40","documentation":"Thrown by validateGroupName (storage.ts) when the proposed group name fails GROUP_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/. Names must start with a letter or digit and contain only letters, digits, underscores, or hyphens. This runs inside getGroupDir and createGroupDir, so it guards every path that constructs a group directory — primarily to prevent path traversal and invalid filesystem characters, not merely aesthetics.","triggerScenarios":"Calling createGroupDir or getGroupDir with a name containing '.', '/', spaces, leading hyphen/underscore, or any non-[a-zA-Z0-9_-] character; an empty string; a name starting with '-' (looks like a CLI flag and is regex-invalid).","commonSituations":"User passes a dotted name like 'team.project' (dot not allowed); a name with a slash implying a nested path (traversal risk); a name starting with '_' or '-'; a name derived from untrusted input that wasn't sanitized.","solutions":["Use only [a-zA-Z0-9_-], starting with a letter or digit — e.g. 'team-project', not 'team.project'.","If the name comes from user input, sanitize it (replace disallowed chars with '-' and strip leading non-alphanumerics) before calling validateGroupName.","Avoid '.' especially: it would let a crafted name escape the groups/ base directory."],"exampleFix":"// before — dot and slash trigger the guard\nawait createGroupDir(home, 'team/sub.project', false);\n\n// after — sanitized, regex-valid name\nawait createGroupDir(home, 'team-sub-project', false);","handlingStrategy":"validation","validationCode":"const GROUP_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;\nfunction isValidGroupName(name: string): boolean {\n  return GROUP_NAME_RE.test(name);\n}\n// sanitize untrusted input before calling createGroupDir:\nfunction sanitizeGroupName(raw: string): string {\n  const cleaned = raw.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/^[^a-zA-Z0-9]+/, '');\n  return isValidGroupName(cleaned) ? cleaned : 'group';\n}","typeGuard":"function isValidGroupName(name: unknown): name is string {\n  return typeof name === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(name);\n}","tryCatchPattern":null,"preventionTips":["Validate names with the same regex before passing them to createGroupDir/getGroupDir.","Sanitize untrusted input by replacing disallowed chars with '-' and stripping leading non-alphanumerics.","Never allow '.', '/', or '..' segments — they enable path traversal out of the groups/ directory."],"tags":["group-config","validation","path-traversal","security","storage"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}