Class: Ace::Review::Atoms::PresetValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/review/atoms/preset_validator.rb

Overview

Validates preset references and detects circular dependencies

Constant Summary collapse

MAX_DEPTH =

Maximum recursion depth for preset composition A sane limit to prevent stack overflows from deep, non-circular nesting

10

Class Method Summary collapse

Class Method Details

.check_circular_dependency(preset_name, preset_chain) ⇒ Object

Detect circular dependencies in preset composition Returns { success: true } if no circular dependency Returns { success: false, error: “…” } if circular dependency found



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ace/review/atoms/preset_validator.rb', line 52

def self.check_circular_dependency(preset_name, preset_chain)
  if preset_chain.include?(preset_name)
    {
      success: false,
      error: "Circular dependency detected: #{(preset_chain + [preset_name]).join(" -> ")}"
    }
  elsif preset_chain.size >= MAX_DEPTH
    {
      success: false,
      error: "Maximum preset nesting depth (#{MAX_DEPTH}) exceeded: #{preset_chain.join(" -> ")}"
    }
  else
    {success: true}
  end
end

.extract_preset_references(preset_data) ⇒ Object

Extract preset references from a preset’s configuration Returns array of preset names referenced in the ‘presets:’ key at root level



91
92
93
94
95
96
97
98
99
# File 'lib/ace/review/atoms/preset_validator.rb', line 91

def self.extract_preset_references(preset_data)
  return [] unless preset_data

  # Look for root-level 'presets' key (both string and symbol)
  presets = preset_data["presets"] || preset_data[:presets] || []

  # Ensure we return an array of strings
  Array(presets).map(&:to_s)
end

.preset_exists?(preset_name, preset_manager) ⇒ Boolean

Check if a preset exists in the preset manager

Returns:

  • (Boolean)


45
46
47
# File 'lib/ace/review/atoms/preset_validator.rb', line 45

def self.preset_exists?(preset_name, preset_manager)
  preset_manager.preset_exists?(preset_name)
end

.validate_preset_name(preset_name) ⇒ Object

Validate preset name format Returns { success: true } if valid Returns { success: false, error: “…” } if invalid



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/ace/review/atoms/preset_validator.rb', line 15

def self.validate_preset_name(preset_name)
  return {success: false, error: "Preset name cannot be nil or empty"} if preset_name.nil? || preset_name.empty?

  # Check for path traversal attempts
  if preset_name.start_with?("/", "\\")
    return {
      success: false,
      error: "Invalid preset name '#{preset_name}': absolute paths are not allowed"
    }
  end

  if preset_name.include?("..") || preset_name.include?("/") || preset_name.include?("\\")
    return {
      success: false,
      error: "Invalid preset name '#{preset_name}': cannot contain path separators or '..' sequences"
    }
  end

  # Check for reasonable length
  if preset_name.length > 100
    return {
      success: false,
      error: "Preset name too long (max 100 characters): '#{preset_name[0..20]}...'"
    }
  end

  {success: true}
end

.validate_presets(preset_names, preset_manager) ⇒ Object

Validate a list of preset names Returns { success: true, valid: [], missing: [] }



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ace/review/atoms/preset_validator.rb', line 70

def self.validate_presets(preset_names, preset_manager)
  valid = []
  missing = []

  preset_names.each do |name|
    if preset_exists?(name, preset_manager)
      valid << name
    else
      missing << name
    end
  end

  {
    success: missing.empty?,
    valid: valid,
    missing: missing
  }
end