{"record":{"id":"bd4594dab9c8fe6e","repo":"puppetlabs/puppet","slug":"invalid-key-bd4594","errorCode":null,"errorMessage":"invalid key","messagePattern":"invalid key","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/indirector/yaml.rb","lineNumber":42,"sourceCode":"    file = path(request.key)\n\n    basedir = File.dirname(file)\n\n    # This is quite likely a bad idea, since we're not managing ownership or modes.\n    Dir.mkdir(basedir) unless Puppet::FileSystem.exist?(basedir)\n\n    begin\n      Puppet::Util::Yaml.dump(request.instance, file)\n    rescue TypeError => detail\n      Puppet.err _(\"Could not save %{indirection} %{request}: %{detail}\") % { indirection: name, request: request.key, detail: detail }\n    end\n  end\n\n  # Return the path to a given node's file.\n  def path(name, ext = '.yaml')\n    if name =~ Puppet::Indirector::BadNameRegexp then\n      Puppet.crit(_(\"directory traversal detected in %{indirection}: %{name}\") % { indirection: self.class, name: name.inspect })\n      raise ArgumentError, _(\"invalid key\")\n    end\n\n    base = Puppet.run_mode.server? ? Puppet[:yamldir] : Puppet[:clientyamldir]\n    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)\n  end\n\n  def destroy(request)\n    file_path = path(request.key)\n    Puppet::FileSystem.unlink(file_path) if Puppet::FileSystem.exist?(file_path)\n  end\n\n  def search(request)\n    Dir.glob(path(request.key, '')).collect do |file|\n      load_file(file)\n    end\n  end\n\n  protected","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/indirector/yaml.rb#L24-L60","documentation":"The YAML terminus's path() rejects indirection keys matching Puppet::Indirector::BadNameRegexp (patterns like a leading '../', '/..' inside the name, or a leading '/'), logs a critical message about directory traversal, and raises ArgumentError 'invalid key'. The check prevents keys from escaping the terminus's base directory, so it fires on absolute paths and dot-dot relative paths used as indirection keys — whether from a bug or a deliberate traversal attempt.","triggerScenarios":"Calling a yaml/msgpack/json-backed indirection with key '../../../etc/passwd', '/etc/shadow', or 'cert/../other'; commonly triggered when certnames or filenames containing slashes/dotdot segments are passed straight through as request keys (e.g., a node whose certname was issued with a '/' in it).","commonSituations":"Security testing / fuzzing of Puppet REST endpoints; bugs where File.basename is forgotten and full paths are passed as keys; compromised or malformed ENC output feeding crafted node names into yaml store paths.","solutions":["Sanitize keys before use: name = File.basename(key) (and reject empty results)","Validate certnames/identifiers at ingress: reject '/' and '..' in names that will become indirection keys","Find the caller passing the raw path — the crit log line names the indirection and the offending name.inspect","If traversal appeared in server logs from external traffic, treat as a probing attempt and check access controls (auth.conf) on the affected endpoints"],"exampleFix":"# before\nkey = params[:node]                 # attacker-controlled: '../../etc/passwd'\nPuppet::Node.indirection.find(key)  # crit + ArgumentError: invalid key\n\n# after\nkey = File.basename(params[:node].to_s)\nraise ArgumentError, 'bad node name' if key.empty? || key =~ /\\A\\.+\\z/\nPuppet::Node.indirection.find(key)","handlingStrategy":"validation","validationCode":"def safe_indirection_key?(name)\n  name.is_a?(String) && !name.empty? && name !~ Puppet::Indirector::BadNameRegexp && !name.start_with?('/')\nend\nraise ArgumentError, 'invalid key' unless safe_indirection_key?(user_input)","typeGuard":"def safe_indirection_key?(name)\n  name.is_a?(String) && !name.empty? && name !~ Puppet::Indirector::BadNameRegexp\nend","tryCatchPattern":null,"preventionTips":["Apply File.basename plus a charset whitelist to every externally supplied name used as a key","Reject certnames containing '/' or '..' at issuance time","Treat any occurrence of this error in server logs as a security signal and audit the source"],"tags":["puppet","path-traversal","security","validation","indirector"],"backgroundTag":"path-traversal","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}