Class: SwarmSDK::Permissions::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/swarm_sdk/permissions/config.rb

Overview

Config parses and validates permission configuration for tools

Handles:

  • Allowed path patterns (allowlist)
  • Denied path patterns (explicit denylist)
  • Allowed command patterns (regex for Bash tool)
  • Denied command patterns (regex for Bash tool)
  • Relative paths converted to absolute based on agent directory
  • Glob pattern matching with absolute paths

All paths and patterns are converted to absolute:

  • Patterns starting with / are kept as-is
  • Relative patterns are expanded against the agent's base directory
  • Paths starting with / are kept as-is
  • Relative paths are expanded against the agent's base directory

Example:

config = Config.new(
{
  allowed_paths: ["tmp/**/*"],
  denied_paths: ["tmp/secrets/**"],
  allowed_commands: ["^git (status|diff|log)$"],
  denied_commands: ["^rm -rf"]
},
base_directories: ["/home/user/project"]
)
config.allowed?("tmp/file.txt")  # => true (checks /home/user/project/tmp/file.txt)
config.allowed?("tmp/secrets/key.pem")  # => false (denied takes precedence)
config.command_allowed?("git status")  # => true
config.command_allowed?("rm -rf /")  # => false (denied takes precedence)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_hash, base_directory:) ⇒ Config

Initialize permission configuration

Parameters:

  • config_hash (Hash)

    Permission configuration with :allowed_paths, :denied_paths, :allowed_commands, :denied_commands

  • base_directory (String)

    Base directory for the agent



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/swarm_sdk/permissions/config.rb', line 42

def initialize(config_hash, base_directory:)
  # Use agent's directory as the base for path resolution
  @base_directory = File.expand_path(base_directory)

  # Expand all patterns to absolute paths
  @allowed_patterns = expand_patterns(config_hash[:allowed_paths] || [])
  @denied_patterns = expand_patterns(config_hash[:denied_paths] || [])

  # Parse command patterns (regex strings)
  @allowed_commands = compile_regex_patterns(config_hash[:allowed_commands] || [])
  @denied_commands = compile_regex_patterns(config_hash[:denied_commands] || [])
end

Instance Attribute Details

#allowed_commandsObject (readonly)

Returns the value of attribute allowed_commands.



36
37
38
# File 'lib/swarm_sdk/permissions/config.rb', line 36

def allowed_commands
  @allowed_commands
end

#allowed_patternsObject (readonly)

Returns the value of attribute allowed_patterns.



36
37
38
# File 'lib/swarm_sdk/permissions/config.rb', line 36

def allowed_patterns
  @allowed_patterns
end

#denied_commandsObject (readonly)

Returns the value of attribute denied_commands.



36
37
38
# File 'lib/swarm_sdk/permissions/config.rb', line 36

def denied_commands
  @denied_commands
end

#denied_patternsObject (readonly)

Returns the value of attribute denied_patterns.



36
37
38
# File 'lib/swarm_sdk/permissions/config.rb', line 36

def denied_patterns
  @denied_patterns
end

Instance Method Details

#allowed?(path, directory_search: false) ⇒ Boolean

Check if a path is allowed according to this configuration

Rules:

  1. Denied patterns take precedence and always block
  2. If allowed_paths specified: must match at least one pattern (allowlist)
  3. If allowed_paths NOT specified: allow everything (except denied)
  4. All paths are converted to absolute for consistent matching
  5. For directories used as search bases (Glob/Grep), allow if any pattern would match inside

Parameters:

  • path (String)

    Path to check (relative or absolute)

  • directory_search (Boolean) (defaults to: false)

    True if this is a directory search base (Glob/Grep)

Returns:

  • (Boolean)

    True if path is allowed



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/swarm_sdk/permissions/config.rb', line 67

def allowed?(path, directory_search: false)
  # Convert path to absolute
  absolute_path = to_absolute_path(path)

  # Denied patterns take precedence - check first
  return false if matches_any?(@denied_patterns, absolute_path)

  # If no allowed patterns, allow everything (except denied)
  return true if @allowed_patterns.empty?

  # For directory searches, check if directory is a prefix of any allowed pattern
  # Don't check if directory exists - allow non-existent directories as search bases
  if directory_search
    return true if allowed_as_search_base?(absolute_path)
  end

  # Must match at least one allowed pattern
  matches_any?(@allowed_patterns, absolute_path)
end

#command_allowed?(command) ⇒ Boolean

Check if a command is allowed according to this configuration

Rules:

  1. Denied command patterns take precedence and always block
  2. If allowed_commands specified: must match at least one pattern (allowlist)
  3. If allowed_commands NOT specified: allow everything (except denied)

Parameters:

  • command (String)

    Command to check

Returns:

  • (Boolean)

    True if command is allowed



134
135
136
137
138
139
140
141
142
143
# File 'lib/swarm_sdk/permissions/config.rb', line 134

def command_allowed?(command)
  # Denied patterns take precedence - check first
  return false if matches_any_regex?(@denied_commands, command)

  # If no allowed patterns, allow everything (except denied)
  return true if @allowed_commands.empty?

  # Must match at least one allowed pattern
  matches_any_regex?(@allowed_commands, command)
end

#find_blocking_command_pattern(command) ⇒ String?

Find the specific pattern that denies or doesn't allow a command

Parameters:

  • command (String)

    Command to check

Returns:

  • (String, nil)

    The pattern that blocks this command, or nil if allowed



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/swarm_sdk/permissions/config.rb', line 149

def find_blocking_command_pattern(command)
  # Check denied patterns first
  denied_match = @denied_commands.find { |pattern| pattern.match?(command) }
  return denied_match.source if denied_match

  # Check allowed patterns
  if @allowed_commands.any?
    # Check if command matches any allowed pattern
    return if @allowed_commands.any? { |pattern| pattern.match?(command) }

    # Command doesn't match any allowed pattern
    return "(not in allowed list)"
  end

  nil
end

#find_blocking_pattern(path, directory_search: false) ⇒ String?

Find the specific pattern that denies or doesn't allow a path

Parameters:

  • path (String)

    Path to check (relative or absolute)

  • directory_search (Boolean) (defaults to: false)

    True if this is a directory search base (Glob/Grep)

Returns:

  • (String, nil)

    The pattern that blocks this path, or nil if allowed



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/swarm_sdk/permissions/config.rb', line 92

def find_blocking_pattern(path, directory_search: false)
  absolute_path = to_absolute_path(path)

  # Check denied patterns first
  denied_match = @denied_patterns.find { |pattern| PathMatcher.matches?(pattern, absolute_path) }
  return denied_match if denied_match

  # Check allowed patterns
  if @allowed_patterns.any?
    # For directory searches, check if allowed as search base
    # Don't check if directory exists - allow non-existent directories as search bases
    if directory_search
      return if allowed_as_search_base?(absolute_path)
    end

    # Check if path matches any allowed pattern
    return if @allowed_patterns.any? { |pattern| PathMatcher.matches?(pattern, absolute_path) }

    # Path doesn't match any allowed pattern
    return "(not in allowed list)"
  end

  nil
end

#to_absolute(path) ⇒ String

Convert a path to absolute form

Parameters:

  • path (String)

    Path to convert

Returns:

  • (String)

    Absolute path



121
122
123
# File 'lib/swarm_sdk/permissions/config.rb', line 121

def to_absolute(path)
  to_absolute_path(path)
end