{"record":{"id":"eccc9a9158cb7a37","repo":"jashkenas/underscore","slug":"variable-is-not-a-bare-identifier","errorCode":null,"errorMessage":"variable is not a bare identifier: ","messagePattern":"variable is not a bare identifier: ","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"modules/template.js","lineNumber":72,"sourceCode":"    index = offset + match.length;\n\n    if (escape) {\n      source += \"'+\\n((__t=(\" + escape + \"))==null?'':_.escape(__t))+\\n'\";\n    } else if (interpolate) {\n      source += \"'+\\n((__t=(\" + interpolate + \"))==null?'':__t)+\\n'\";\n    } else if (evaluate) {\n      source += \"';\\n\" + evaluate + \"\\n__p+='\";\n    }\n\n    // Adobe VMs need the match returned to produce the correct offset.\n    return match;\n  });\n  source += \"';\\n\";\n\n  var argument = settings.variable;\n  if (argument) {\n    // Insure against third-party code injection. (CVE-2021-23358)\n    if (!bareIdentifier.test(argument)) throw new Error(\n      'variable is not a bare identifier: ' + argument\n    );\n  } else {\n    // If a variable is not specified, place data values in local scope.\n    source = 'with(obj||{}){\\n' + source + '}\\n';\n    argument = 'obj';\n  }\n\n  source = \"var __t,__p='',__j=Array.prototype.join,\" +\n    \"print=function(){__p+=__j.call(arguments,'');};\\n\" +\n    source + 'return __p;\\n';\n\n  var render;\n  try {\n    render = new Function(argument, '_', source);\n  } catch (e) {\n    e.source = source;\n    throw e;","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jashkenas/underscore/blob/e70d5bd070f1d883b40e786a955a61e4f4b3c2c6/modules/template.js#L54-L90","documentation":"When you compile an Underscore template with a {variable: 'name'} setting, the name is interpolated verbatim into generated JS, so it must be a single bare identifier (e.g. 'data'), not a dotted path ('a.b') or an expression ('data; process.exit(1)'). The bareIdentifier test guards against code injection via the variable name (CVE-2021-23358), and throws an Error when it fails.","triggerScenarios":"Calling _.template(text, {variable: 'some.path'}) or {variable: 'data, other'} — any variable string that is not a plain [A-Za-z_$][\\w$]* identifier, including names containing dots, brackets, dashes, spaces, or semicolons (typically from untrusted input).","commonSituations":"Passing a config-derived or user-supplied variable name (e.g. a dotted property path) straight into the template settings; copying the {variable:'data'} idiom but substituting 'obj.prop' hoping to scope the data; an attacker-controlled variable name triggering the CVE-2021-23358 injection guard.","solutions":["Use a single valid JavaScript identifier for the variable setting, e.g. _.template(t, {variable: 'data'}).","If you need a nested path, pre-resolve it yourself: pass {variable: 'data'} and read data.prop inside the template, or compute the object before compiling.","Sanitize or validate the variable name with /^[A-Za-z_$][A-Za-z0-9_$]*$/ before passing it, especially when it comes from user input.","Never interpolate user input into the variable setting; treat it as code, not data.","For trusted static templates, hardcode the variable name in source rather than deriving it from runtime config."],"exampleFix":"// before\n_.template(html, {variable: 'ctx.model'});\n// Error: variable is not a bare identifier: ctx.model\n\n// after\nconst t = _.template(html, {variable: 'ctx'});\nt({ model: ctx.model });","handlingStrategy":"validation","validationCode":"const BARE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nfunction compileTemplate(text, settings = {}) {\n  if (settings.variable && !BARE_IDENTIFIER.test(settings.variable)) {\n    throw new Error('variable must be a bare identifier: ' + settings.variable);\n  }\n  return _.template(text, settings);\n}","typeGuard":"const isSafeTemplateVariable = (v) =>\n  typeof v === 'string' && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(v);","tryCatchPattern":"try {\n  return _.template(source, { variable: name });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('variable is not a bare identifier')) {\n    return _.template(source, { variable: 'data' }); // safe default\n  }\n  throw e;\n}","preventionTips":["Never pass user input as the variable setting; validate with /^[A-Za-z_$][\\w$]*$/ first.","Use a single identifier and access nested properties inside the template (data.prop) instead of 'a.b'.","For untrusted template sources, omit variable entirely and accept the with(obj) sandbox, or use a hardened template engine.","Code-review any template settings built from config or environment values.","Pin Underscore >= 1.13.0-2 so the CVE-2021-23358 guard is present."],"tags":["security","code-injection","cve-2021-23358","template","validation"],"backgroundTag":"invalid-template-variable","analyzedSha":"e70d5bd070f1d883b40e786a955a61e4f4b3c2c6","analyzedAt":"2026-08-29T10:08:07.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}