ruby-mana 0.5.8 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +114 -195
- data/exe/mana +12 -0
- data/lib/mana/backends/anthropic.rb +47 -0
- data/lib/mana/backends/base.rb +41 -0
- data/lib/mana/backends/openai.rb +15 -0
- data/lib/mana/binding_helpers.rb +106 -0
- data/lib/mana/chat.rb +301 -0
- data/lib/mana/config.rb +0 -19
- data/lib/mana/engine.rb +102 -359
- data/lib/mana/knowledge.rb +203 -0
- data/lib/mana/logger.rb +10 -0
- data/lib/mana/prompt_builder.rb +157 -0
- data/lib/mana/tool_handler.rb +180 -0
- data/lib/mana/version.rb +1 -1
- data/lib/mana.rb +10 -1
- metadata +38 -4
- data/lib/mana/security_policy.rb +0 -195
data/lib/mana/security_policy.rb
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "set"
|
|
4
|
-
|
|
5
|
-
module Mana
|
|
6
|
-
# Configurable security policy for LLM tool calls.
|
|
7
|
-
#
|
|
8
|
-
# Five levels (higher = more permissions, each includes all below):
|
|
9
|
-
# 0 :sandbox — variables and user functions only
|
|
10
|
-
# 1 :strict — + safe stdlib (Time, Date, Math)
|
|
11
|
-
# 2 :standard — + read filesystem (File.read, Dir.glob) [default]
|
|
12
|
-
# 3 :permissive — + write files, network, require
|
|
13
|
-
# 4 :danger — no restrictions
|
|
14
|
-
#
|
|
15
|
-
# Usage:
|
|
16
|
-
# Mana.configure { |c| c.security = :standard }
|
|
17
|
-
# Mana.configure { |c| c.security = 2 }
|
|
18
|
-
# Mana.configure do |c|
|
|
19
|
-
# c.security = :strict
|
|
20
|
-
# c.security.allow_receiver "File", only: %w[read exist?]
|
|
21
|
-
# end
|
|
22
|
-
class SecurityPolicy
|
|
23
|
-
LEVELS = { sandbox: 0, strict: 1, standard: 2, permissive: 3, danger: 4 }.freeze
|
|
24
|
-
|
|
25
|
-
# Methods blocked at each level. Higher levels remove restrictions.
|
|
26
|
-
PRESETS = {
|
|
27
|
-
sandbox: {
|
|
28
|
-
blocked_methods: %w[
|
|
29
|
-
methods singleton_methods private_methods protected_methods public_methods
|
|
30
|
-
instance_variables instance_variable_get instance_variable_set remove_instance_variable
|
|
31
|
-
local_variables global_variables
|
|
32
|
-
send __send__ public_send eval instance_eval instance_exec class_eval module_eval
|
|
33
|
-
system exec fork spawn ` require require_relative load
|
|
34
|
-
exit exit! abort at_exit
|
|
35
|
-
],
|
|
36
|
-
blocked_receivers: :all
|
|
37
|
-
},
|
|
38
|
-
strict: {
|
|
39
|
-
blocked_methods: %w[
|
|
40
|
-
methods singleton_methods private_methods protected_methods public_methods
|
|
41
|
-
instance_variables instance_variable_get instance_variable_set remove_instance_variable
|
|
42
|
-
local_variables global_variables
|
|
43
|
-
send __send__ public_send eval instance_eval instance_exec class_eval module_eval
|
|
44
|
-
system exec fork spawn ` require require_relative load
|
|
45
|
-
exit exit! abort at_exit
|
|
46
|
-
],
|
|
47
|
-
blocked_receivers: {
|
|
48
|
-
"File" => :all, "Dir" => :all, "IO" => :all,
|
|
49
|
-
"Kernel" => :all, "Process" => :all, "ObjectSpace" => :all, "ENV" => :all
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
standard: {
|
|
53
|
-
blocked_methods: %w[
|
|
54
|
-
methods singleton_methods private_methods protected_methods public_methods
|
|
55
|
-
instance_variables instance_variable_get instance_variable_set remove_instance_variable
|
|
56
|
-
local_variables global_variables
|
|
57
|
-
send __send__ public_send eval instance_eval instance_exec class_eval module_eval
|
|
58
|
-
system exec fork spawn `
|
|
59
|
-
exit exit! abort at_exit
|
|
60
|
-
require require_relative load
|
|
61
|
-
],
|
|
62
|
-
blocked_receivers: {
|
|
63
|
-
"File" => Set.new(%w[delete write open chmod chown rename unlink]),
|
|
64
|
-
"Dir" => Set.new(%w[delete rmdir mkdir chdir]),
|
|
65
|
-
"IO" => :all, "Kernel" => :all, "Process" => :all,
|
|
66
|
-
"ObjectSpace" => :all, "ENV" => :all
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
permissive: {
|
|
70
|
-
blocked_methods: %w[
|
|
71
|
-
eval instance_eval instance_exec class_eval module_eval
|
|
72
|
-
system exec fork spawn `
|
|
73
|
-
exit exit! abort at_exit
|
|
74
|
-
],
|
|
75
|
-
blocked_receivers: {
|
|
76
|
-
"ObjectSpace" => :all
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
danger: {
|
|
80
|
-
blocked_methods: [],
|
|
81
|
-
blocked_receivers: {}
|
|
82
|
-
}
|
|
83
|
-
}.freeze
|
|
84
|
-
|
|
85
|
-
attr_reader :preset
|
|
86
|
-
|
|
87
|
-
# Initialize security policy from a preset name (Symbol) or numeric level (Integer)
|
|
88
|
-
def initialize(preset = :strict)
|
|
89
|
-
# Convert numeric level to its corresponding symbol name
|
|
90
|
-
preset = LEVELS.key(preset) if preset.is_a?(Integer)
|
|
91
|
-
raise ArgumentError, "unknown security level: #{preset.inspect}. Use: #{LEVELS.keys.join(', ')}" unless PRESETS.key?(preset)
|
|
92
|
-
|
|
93
|
-
@preset = preset
|
|
94
|
-
data = PRESETS[preset]
|
|
95
|
-
@blocked_methods = Set.new(data[:blocked_methods])
|
|
96
|
-
|
|
97
|
-
if data[:blocked_receivers] == :all
|
|
98
|
-
# Sandbox mode: block all receiver calls by default
|
|
99
|
-
@block_all_receivers = true
|
|
100
|
-
@blocked_receivers = {}
|
|
101
|
-
else
|
|
102
|
-
# Other modes: block only specific methods on specific receivers
|
|
103
|
-
@block_all_receivers = false
|
|
104
|
-
@blocked_receivers = data[:blocked_receivers].transform_values { |v|
|
|
105
|
-
v == :all ? :all : Set.new(v)
|
|
106
|
-
}
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Allowlist overrides added via allow_receiver(name, only: [...])
|
|
110
|
-
@allowed_overrides = {}
|
|
111
|
-
yield self if block_given?
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# --- Mutators ---
|
|
115
|
-
|
|
116
|
-
def allow_method(name)
|
|
117
|
-
@blocked_methods.delete(name.to_s)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def block_method(name)
|
|
121
|
-
@blocked_methods.add(name.to_s)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Allow a receiver's calls. With `only:`, allowlist specific methods;
|
|
125
|
-
# without it, fully unblock the receiver.
|
|
126
|
-
def allow_receiver(name, only: nil)
|
|
127
|
-
name = name.to_s
|
|
128
|
-
if only
|
|
129
|
-
# Allowlist only the specified methods (override takes priority over block rules)
|
|
130
|
-
@allowed_overrides[name] = Set.new(only.map(&:to_s))
|
|
131
|
-
else
|
|
132
|
-
# Fully unblock the receiver and remove any override
|
|
133
|
-
@blocked_receivers.delete(name)
|
|
134
|
-
@allowed_overrides.delete(name)
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Block a receiver's calls. With `only:`, block specific methods;
|
|
139
|
-
# without it, block all methods on the receiver.
|
|
140
|
-
def block_receiver(name, only: nil)
|
|
141
|
-
name = name.to_s
|
|
142
|
-
if only
|
|
143
|
-
existing = @blocked_receivers[name]
|
|
144
|
-
if existing == :all
|
|
145
|
-
# Already fully blocked — nothing to add
|
|
146
|
-
elsif existing.is_a?(Set)
|
|
147
|
-
# Merge new blocked methods into the existing set
|
|
148
|
-
existing.merge(only.map(&:to_s))
|
|
149
|
-
else
|
|
150
|
-
# First partial block rule for this receiver
|
|
151
|
-
@blocked_receivers[name] = Set.new(only.map(&:to_s))
|
|
152
|
-
end
|
|
153
|
-
else
|
|
154
|
-
# Block all methods on this receiver
|
|
155
|
-
@blocked_receivers[name] = :all
|
|
156
|
-
@allowed_overrides.delete(name)
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# --- Queries ---
|
|
161
|
-
|
|
162
|
-
def method_blocked?(name)
|
|
163
|
-
@blocked_methods.include?(name.to_s)
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
# Check whether calling `method` on `receiver` is blocked by the policy
|
|
167
|
-
def receiver_call_blocked?(receiver, method)
|
|
168
|
-
# Danger mode has no restrictions at all
|
|
169
|
-
return false if @preset == :danger
|
|
170
|
-
|
|
171
|
-
r, m = receiver.to_s, method.to_s
|
|
172
|
-
|
|
173
|
-
# Sandbox mode: block everything unless explicitly allowlisted
|
|
174
|
-
if @block_all_receivers
|
|
175
|
-
return !(@allowed_overrides.key?(r) && @allowed_overrides[r].include?(m))
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# Receiver not in the block list — allow
|
|
179
|
-
rule = @blocked_receivers[r]
|
|
180
|
-
return false if rule.nil?
|
|
181
|
-
|
|
182
|
-
# Allowlist override takes priority: if user explicitly allowed this method, pass
|
|
183
|
-
if @allowed_overrides.key?(r) && @allowed_overrides[r].include?(m)
|
|
184
|
-
return false
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Blocked if receiver is fully blocked (:all) or method is in the blocked set
|
|
188
|
-
rule == :all || rule.include?(m)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def level
|
|
192
|
-
LEVELS[@preset]
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
end
|