{"record":{"id":"7d54d7ab15a1ad9c","repo":"puppetlabs/puppet","slug":"invalid-key","errorCode":null,"errorMessage":"invalid key","messagePattern":"invalid key","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/indirector/json.rb","lineNumber":44,"sourceCode":"  rescue => detail\n    unless detail.is_a? Errno::ENOENT\n      raise Puppet::Error, _(\"Could not destroy %{json} %{request}: %{detail}\") % { json: name, request: request.key, detail: detail }, detail.backtrace\n    end\n\n    1 # emulate success...\n  end\n\n  def search(request)\n    Dir.glob(path(request.key)).collect do |file|\n      load_json_from_file(file, request.key)\n    end\n  end\n\n  # Return the path to a given node's file.\n  def path(name, ext = '.json')\n    if name =~ Puppet::Indirector::BadNameRegexp then\n      Puppet.crit(_(\"directory traversal detected in %{json}: %{name}\") % { json: self.class, name: name.inspect })\n      raise ArgumentError, _(\"invalid key\")\n    end\n\n    base = data_dir\n    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)\n  end\n\n  private\n\n  def data_dir\n    Puppet.run_mode.server? ? Puppet[:server_datadir] : Puppet[:client_datadir]\n  end\n\n  def load_json_from_file(file, key)\n    json = nil\n\n    begin\n      json = Puppet::FileSystem.read(file, :encoding => Encoding::BINARY)\n    rescue Errno::ENOENT","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/indirector/json.rb#L26-L62","documentation":"Before touching disk, the JSON terminus validates the key against Puppet::Indirector::BadNameRegexp, which matches a leading '..', any '/' or '\\\\', a NUL byte, or a leading Windows drive-letter prefix like 'C:'. A match means the key could escape the indirection's data directory (directory traversal), so Puppet logs crit 'directory traversal detected in <class>: <name>' and raises ArgumentError 'invalid key'.","triggerScenarios":"Calling find/save/destroy on a json-backed indirection with a key containing a slash (certname 'web/server01'), a leading '..', an absolute path, a NUL byte, or a 'C:'-style prefix - for example passing a file path or URI where a bare key is expected.","commonSituations":"Node/certname schemes that include slashes; callers feeding unvalidated upstream identifiers into the indirector; code migrating from file APIs and reusing paths as keys.","solutions":["Sanitize the key before the call: strip or replace path separators, NULs, and leading '..' sequences.","Encode identifiers that legitimately contain special characters (URL-encode or digest them) instead of passing them raw.","Reject malformed names at your API boundary with a clear error rather than letting the crit log be the first signal.","If slashes come from provisioning (certname strategy), change the naming scheme instead of working around the guard."],"exampleFix":"# before\nPuppet::Node.indirection.destroy('web/server01')\n# crit: directory traversal detected ... ArgumentError: invalid key\n\n# after\nsafe_key = 'web/server01'.tr('/', '_') # => 'web_server01'\nPuppet::Node.indirection.destroy(safe_key)","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'unsafe key ' + key.inspect if key.to_s.match?(Puppet::Indirector::BadNameRegexp)\n# normalize instead of passing raw separators\nsafe_key = key.tr('/', '_')","typeGuard":"def safe_indirector_key?(name)\n  name.is_a?(String) && !name.match?(Puppet::Indirector::BadNameRegexp)\nend","tryCatchPattern":null,"preventionTips":["Never accept raw user input as an indirector key.","Standardize certnames without slashes or drive-letter prefixes.","Treat the crit log 'directory traversal detected' as a security signal and alert on it."],"tags":["puppet","json-store","path-traversal","input-sanitization","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}