puppetlabs/puppet · error · ArgumentError

invalid key

Error message

invalid key

What it means

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'.

Source

Thrown at lib/puppet/indirector/json.rb:44

  rescue => detail
    unless detail.is_a? Errno::ENOENT
      raise Puppet::Error, _("Could not destroy %{json} %{request}: %{detail}") % { json: name, request: request.key, detail: detail }, detail.backtrace
    end

    1 # emulate success...
  end

  def search(request)
    Dir.glob(path(request.key)).collect do |file|
      load_json_from_file(file, request.key)
    end
  end

  # Return the path to a given node's file.
  def path(name, ext = '.json')
    if name =~ Puppet::Indirector::BadNameRegexp then
      Puppet.crit(_("directory traversal detected in %{json}: %{name}") % { json: self.class, name: name.inspect })
      raise ArgumentError, _("invalid key")
    end

    base = data_dir
    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)
  end

  private

  def data_dir
    Puppet.run_mode.server? ? Puppet[:server_datadir] : Puppet[:client_datadir]
  end

  def load_json_from_file(file, key)
    json = nil

    begin
      json = Puppet::FileSystem.read(file, :encoding => Encoding::BINARY)
    rescue Errno::ENOENT

View on GitHub (pinned to e227c27540)

Solutions

  1. Sanitize the key before the call: strip or replace path separators, NULs, and leading '..' sequences.
  2. Encode identifiers that legitimately contain special characters (URL-encode or digest them) instead of passing them raw.
  3. Reject malformed names at your API boundary with a clear error rather than letting the crit log be the first signal.
  4. If slashes come from provisioning (certname strategy), change the naming scheme instead of working around the guard.

Example fix

# before
Puppet::Node.indirection.destroy('web/server01')
# crit: directory traversal detected ... ArgumentError: invalid key

# after
safe_key = 'web/server01'.tr('/', '_') # => 'web_server01'
Puppet::Node.indirection.destroy(safe_key)
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError, 'unsafe key ' + key.inspect if key.to_s.match?(Puppet::Indirector::BadNameRegexp)
# normalize instead of passing raw separators
safe_key = key.tr('/', '_')

Type guard

def safe_indirector_key?(name)
  name.is_a?(String) && !name.match?(Puppet::Indirector::BadNameRegexp)
end

Prevention

When it happens

Trigger: 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.

Common situations: Node/certname schemes that include slashes; callers feeding unvalidated upstream identifiers into the indirector; code migrating from file APIs and reusing paths as keys.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/7d54d7ab15a1ad9c. Report an issue: GitHub.