Module: Kobako::Transport::Reflection
- Defined in:
- lib/kobako/transport/reflection.rb,
sig/kobako/transport/reflection.rbs
Overview
The reflection floor a guest→host dispatch must clear before the
Dispatcher reaches public_send: which method names on a resolved
target count as Service behaviour, and which are Ruby's ambient
metaprogramming surface.
Answers with a refusal reason rather than raising, so the error taxonomy stays with the Dispatcher and this module holds only the policy.
Constant Summary collapse
- META_OWNERS =
Modules whose instance methods are ambient Ruby reflection / metaprogramming surface (+send+,
public_send,instance_eval,method,tap,instance_variable_get, ...) rather than Service behaviour. A guest-supplied method name resolving to one of these is rejected: only methods the bound object itself exposes as Service behaviour are reachable, andpublic_send(:send, ...)would otherwise let a guest pivot throughsendinto the privateKernel#eval/#systemsurface (host RCE). [BasicObject, Kernel, Object, Module, Class].freeze
- GADGET_OWNERS =
Callable gadget types whose own public methods are reflection surface (+Proc#binding+ reaches
Binding#eval,Method#receiver/#unbindhand back the underlying object) rather than Service behaviour. Only CALLABLE_ALLOW is reachable on a target of these types; a bound lambda stays invocable, its reflective surface does not. [Proc, Method, UnboundMethod, Binding].freeze
- CALLABLE_ALLOW =
The sole methods reachable on a GADGET_OWNERS target: invoking it (+call+ /
[]/yield) and the harmlessarity/lambda?describers that aid guest-side debugging. %i[call [] yield arity lambda?].freeze
Class Method Summary collapse
-
.ambient_refusal(target, name) ⇒ Object
Guard against ambient reflection methods.
-
.narrowing_refusal(target, name) ⇒ Object
Consult the target's opt-in narrowing predicate.
-
.refusal(target, name) ⇒ Object
The reason
nameis unreachable ontarget, ornilwhen the dispatch may proceed.
Instance Method Summary collapse
Class Method Details
.ambient_refusal(target, name) ⇒ Object
Guard against ambient reflection methods. A public method whose
owner is a META_OWNERS or GADGET_OWNERS module is rejected, except
CALLABLE_ALLOW on a gadget target (a bound lambda stays invocable).
A name with no concrete public method is allowed only when the
target opts into it via respond_to? (dynamic method_missing
Services), since the dangerous methods are all concretely defined
and therefore never reach that branch.
56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/kobako/transport/reflection.rb', line 56 def ambient_refusal(target, name) owner = target.public_method(name).owner gadget = GADGET_OWNERS.include?(owner) return nil unless META_OWNERS.include?(owner) || gadget return nil if gadget && CALLABLE_ALLOW.include?(name) "method #{name.inspect} is not a Service method" rescue NameError return nil if target.respond_to?(name) "no public method #{name.inspect} on target" end |
.narrowing_refusal(target, name) ⇒ Object
Consult the target's opt-in narrowing predicate. A bound object may
define a private respond_to_guest?(name) to restrict which of its
methods the guest reaches; a falsy answer refuses the dispatch. It
is consulted with the private surface included so the guest's
public_send dispatch can never reach respond_to_guest? itself.
74 75 76 77 78 79 |
# File 'lib/kobako/transport/reflection.rb', line 74 def narrowing_refusal(target, name) return nil unless target.respond_to?(:respond_to_guest?, true) return nil if target.__send__(:respond_to_guest?, name) "method #{name.inspect} is not exposed to the guest" end |
.refusal(target, name) ⇒ Object
The reason name is unreachable on target, or nil when the
dispatch may proceed. Composes the ambient-surface floor with the
target's own opt-in narrowing, in that order: the predicate only
narrows and can never re-open what the floor rejects.
45 46 47 |
# File 'lib/kobako/transport/reflection.rb', line 45 def refusal(target, name) ambient_refusal(target, name) || narrowing_refusal(target, name) end |
Instance Method Details
#self?.ambient_refusal ⇒ String?
10 |
# File 'sig/kobako/transport/reflection.rbs', line 10
def self?.ambient_refusal: (untyped target, Symbol name) -> String?
|
#self?.narrowing_refusal ⇒ String?
12 |
# File 'sig/kobako/transport/reflection.rbs', line 12
def self?.narrowing_refusal: (untyped target, Symbol name) -> String?
|
#self?.refusal ⇒ String?
8 |
# File 'sig/kobako/transport/reflection.rbs', line 8
def self?.refusal: (untyped target, Symbol name) -> String?
|