{"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":262,"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":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/cli/analyze-config.ts#L244-L280","documentation":"The 'fetchWrappers' entries are interpolated into a RegExp alternation downstream, so the normalizer only accepts identifier or member-access shapes matching ^[A-Za-z_$][A-Za-z0-9_$.]*$. Anything else (brackets, spaces, dots in odd positions, glob or regex metacharacters) is rejected to prevent a config value from smuggling regex syntax into the consumer — a deliberate injection guard.","triggerScenarios":"{\"fetchWrappers\": [\"client['get']\"]}, [\"client.get.*\"], [\"my wrapper\"], [\"1abc\"] (starts with a digit), or [\"a b\"]; the regex test at analyze-config.ts:272 fails and throws with the offending value echoed back.","commonSituations":"Trying to configure wrappers with glob or regex patterns (\"fetch*\", \".*(get|post)\"), method-call syntax like \"client.get()\", or property-bracket access; users assume the config accepts patterns when it only accepts names.","solutions":["Use bare identifiers or dotted member paths: \"fetch\", \"client.get\", \"axiosInstance.request\"","Remove parentheses, brackets, wildcards, and spaces — the message's example (\"client.get\") shows the accepted shape","If you need broader matching, request a feature/config change rather than encoding a regex in the rc value"],"exampleFix":"// before (.gitnexusrc)\n{\"fetchWrappers\": [\"client.get(\", \"fetch.*\"]}\n// after\n{\"fetchWrappers\": [\"client.get\", \"fetch\"]}","handlingStrategy":"type-guard","validationCode":"const IDENT = /^[A-Za-z_$][A-Za-z0-9_$.]*$/;\nconst bad = fw.filter((n: string) => !IDENT.test(n.trim()));\nif (bad.length) throw new Error(`Invalid fetchWrappers entries: ${bad.join(', ')}`);","typeGuard":"type WrapperName = string;\nconst isWrapperName = (v: string): v is WrapperName =>\n  /^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(v);","tryCatchPattern":"catch (err) {\n  if (err instanceof GitNexusRcError && err.message.includes('identifier or member name')) {\n    // echo the accepted shape: bare identifiers or dotted member paths only\n  }\n}","preventionTips":["Treat fetchWrappers as names, never patterns — no globs, no regex, no call syntax","Mirror the same identifier regex in any tooling that writes the rc file"],"tags":["config","validation","regex-injection","security","gitnexusrc"],"backgroundTag":"config-value-rejected","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}