{"record":{"id":"87122d308fab7816","repo":"abhigyanpatwari/GitNexus","slug":"source-entry-trimmed-must-be-an-identifier","errorCode":null,"errorMessage":"${source} entry \"${trimmed}\" must be an identifier or member name (letters, digits, _, $, . — e.g. \"client.get\").","messagePattern":"(.+?) entry \"(.+?)\" must be an identifier or member name \\(letters, digits, _, \\$, \\. — e\\.g\\. \"client\\.get\"\\)\\.","errorType":"validation","errorClass":"GitNexusRcError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/cli/analyze-config.ts","lineNumber":272,"sourceCode":"      // shared normalizer; #1589/#1852 review F7).\n      if (!Array.isArray(value)) {\n        throw new GitNexusRcError(`${source} must be an array of strings.`);\n      }\n      const names: string[] = [];\n      for (const item of value) {\n        if (typeof item !== 'string') {\n          throw new GitNexusRcError(`${source} entries must all be strings.`);\n        }\n        const trimmed = item.trim();\n        if (!trimmed) {\n          throw new GitNexusRcError(`${source} entries must not be empty.`);\n        }\n        assertNoHiddenChars(trimmed, source);\n        // Values may be interpolated into a RegExp downstream. Restrict to\n        // identifier / member-access shapes so a config value can never smuggle\n        // regex metacharacters into a consumer.\n        if (!/^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(trimmed)) {\n          throw new GitNexusRcError(\n            `${source} entry \"${trimmed}\" must be an identifier or member name ` +\n              `(letters, digits, _, $, . — e.g. \"client.get\").`,\n          );\n        }\n        names.push(trimmed);\n      }\n      if (names.length === 0) {\n        throw new GitNexusRcError(`${source} must list at least one string.`);\n      }\n      // De-duplicate and cap to a sane bound so a pathological config cannot\n      // blow up the consumer scan's alternation.\n      return Array.from(new Set(names)).slice(0, 100);\n    }\n    case 'numeric-string': {\n      // Mirror Commander's contract: these options reach the existing CLI\n      // validation as strings. Accept a JSON number or a string; normalize to a\n      // string and let the downstream per-flag validation enforce ranges so the\n      // error messages stay in one place.","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/cli/analyze-config.ts#L254-L290","documentation":"A `.gitnexusrc` key declared as a string-array (currently only `fetchWrappers`) received an entry that is not a valid identifier or dotted member name. GitNexus restricts these entries to the regex /^[A-Za-z_$][A-Za-z0-9_$.]*$/ because the values are interpolated into a RegExp used by the cross-file HTTP-consumer scan, so a free-form string could smuggle regex metacharacters and alter the scan. The `source` placeholder expands to the config key context, e.g. `.gitnexusrc \"fetchWrappers\"`.","triggerScenarios":"Setting { \"fetchWrappers\": [\"api.fetch(\"] } — the trailing `(` fails the identifier regex. Also `my-wrapper` (dash), `fetch ()` (space/parens), or `lib/http.get` (slash).","commonSituations":"Adding a custom axios/fetch wrapper and copying the call syntax (with parentheses) instead of the function name; using kebab-case or path separators for a wrapper declared elsewhere; attempting to pass a method signature.","solutions":["Use a bare identifier or dotted member-access path matching /^[A-Za-z_$][A-Za-z0-9_$.]*$/, e.g. `client.get` or `apiFetch`.","Strip any parentheses, spaces, dashes, slashes, or regex metacharacters from the entry.","Point at the wrapper function's declared name in source, not its call site."],"exampleFix":"// before\n\"fetchWrappers\": [\"api.fetch()\"]\n// after\n\"fetchWrappers\": [\"api.fetch\"]","handlingStrategy":"validation","validationCode":"// Validate every string-array entry before committing .gitnexusrc\nconst IDENT_OR_MEMBER = /^[A-Za-z_$][A-Za-z0-9_$.]*$/;\nfunction validateFetchWrappers(arr) {\n  if (!Array.isArray(arr)) throw new Error('fetchWrappers must be an array');\n  return arr.map((v) => {\n    if (typeof v !== 'string') throw new Error('entry must be a string: ' + v);\n    const t = v.trim();\n    if (!t || !IDENT_OR_MEMBER.test(t)) {\n      throw new Error('entry \"' + v + '\" must be an identifier or member name');\n    }\n    return t;\n  });\n}","typeGuard":"const isFetchWrapperName = (v) =>\n  typeof v === 'string' && /^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(v.trim()) && v.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Keep fetchWrappers entries as bare dotted identifiers — no parentheses, dashes, slashes, or spaces.","Lint .gitnexusrc in a pre-commit hook with the same regex the normalizer uses."],"tags":["config","gitnexusrc","validation","regex-injection","fetch-wrappers"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}