Class: RailsAiBridge::Tools::ReadLogs::LogLocator

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_bridge/tools/read_logs/log_locator.rb

Overview

Resolves a client-supplied relative file name to a path under the app's +log/ directory and rejects anything that would escape it.

Constant Summary collapse

EMPTY_FILE_ERROR =
'error: file name must not be empty'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file, root) ⇒ LogLocator

Returns a new instance of LogLocator.

Parameters:

  • file (Object) —

    raw client-provided file name (relative to log/)

  • root (String) —

    application root path



15
16
17
18
# File 'lib/rails_ai_bridge/tools/read_logs/log_locator.rb', line 15

def initialize(file, root)
  @file = file.to_s
  @log_dir = Pathname.new(File.expand_path(File.join(root, 'log')))
end

Class Method Details

.open_file(candidate) ⇒ Array(File, nil)

Opens the validated candidate file in binary mode without following a final-component symlink. The log directory is application-owned and must not be writable by untrusted users; Ruby has no portable openat API for atomically resolving every parent component from a directory descriptor.

rubocop:disable Style/FileOpen

Parameters:

  • candidate (Pathname) —

    validated file path

Returns:

  • (Array(File, nil)) —

    opened file descriptor plus nil



42
43
44
45
46
# File 'lib/rails_ai_bridge/tools/read_logs/log_locator.rb', line 42

def self.open_file(candidate)
  file_io = File.open(candidate, File::RDONLY | File::NOFOLLOW)
  # rubocop:enable Style/FileOpen
  [file_io, nil]
end

Instance Method Details

#locate ⇒ Array(File, nil), Array(nil, String)

Resolves the file against the log directory and opens it in binary mode without following a final-component symlink.

Returns:

  • (Array(File, nil), Array(nil, String)) —

    opened file descriptor plus nil, or nil plus an error message



25
26
27
28
29
30
31
# File 'lib/rails_ai_bridge/tools/read_logs/log_locator.rb', line 25

def locate
  return empty_error if @file.strip.empty?

  validate_and_open
rescue Errno::ENOENT
  [nil, "error: log file not found: #{sanitize_filename}"]
end