Class: Inquirex::SafeSource::Validator
- Inherits:
-
Object
- Object
- Inquirex::SafeSource::Validator
- Defined in:
- lib/inquirex/safe_source/validator.rb
Overview
Default-deny AST allowlist for the Inquirex flow DSL.
The validator parses the source with Prism and walks it by recursive descent against an expected shape rather than by visiting every node and asking "is this one forbidden?". At each position — top-level statement, flow-block statement, step-block statement, argument, keyword value, Hash element — only the handful of node types the real DSL produces there are accepted, and anything else is a violation. A node type nobody thought about is therefore rejected by construction, which is the whole point: a blocklist of forbidden methods would be defeated by the first construct that was overlooked.
What that rules out, without needing to name any of it: system,
backticks and %x, exec/spawn/fork, require/load, eval and
the *_eval family, send/__send__/public_send/method, every
constant reference other than the Inquirex entry point (so no File,
IO, Dir, Kernel, ENV, ObjectSpace, Process,
Object.const_get), instance, class and global variables,
def/class/module, begin/rescue, at_exit, BEGIN/END,
singleton definitions, __FILE__-style magic, assignments, conditionals,
loops, string/symbol/regexp interpolation (including inside heredocs),
splats and block-pass arguments.
Ruby blocks are accepted only where the DSL itself opens a nested scope
(Vocabulary block:), and those blocks are validated statement by
statement in turn. Every other block is rejected, which is why compute,
a block-form default, fallback and an action's run cannot be used in
safe mode: a compute block is indistinguishable from a payload.
Constant Summary collapse
- MAX_REPORTED =
Cap on reported violations, so a hostile 64 KiB payload cannot turn a validation error into a megabyte of flash message.
10- UNSAFE_HINT =
Appended to violations an author could legitimately have meant.
" — evaluate source you wrote yourself with `Inquirex.load_dsl(text, unsafe: true)`"- NODE_LABELS =
Node types worth naming explicitly in a violation message, because the humanized class name would not tell the author what they actually wrote.
{ Prism::XStringNode => "a backtick or %x command", Prism::InterpolatedXStringNode => "a backtick or %x command", Prism::InterpolatedStringNode => "an interpolated string (interpolation is never allowed, not even in a heredoc)", Prism::InterpolatedSymbolNode => "an interpolated symbol", Prism::InterpolatedRegularExpressionNode => "an interpolated regular expression", Prism::RegularExpressionNode => "a regular expression", Prism::ConstantReadNode => "a constant reference", Prism::ConstantPathNode => "a constant reference", Prism::InstanceVariableReadNode => "an instance variable", Prism::ClassVariableReadNode => "a class variable", Prism::GlobalVariableReadNode => "a global variable", Prism::DefNode => "a method definition", Prism::ClassNode => "a class definition", Prism::ModuleNode => "a module definition", Prism::SingletonClassNode => "a singleton class definition", Prism::BeginNode => "a begin/rescue block", Prism::LambdaNode => "a lambda", Prism::BlockNode => "a block", Prism::BlockArgumentNode => "a block-pass (&) argument", Prism::SplatNode => "a splat (*) argument", Prism::AssocSplatNode => "a double-splat (**) argument", Prism::SourceFileNode => "__FILE__", Prism::SourceLineNode => "__LINE__", Prism::SourceEncodingNode => "__ENCODING__", Prism::SelfNode => "self", Prism::PreExecutionNode => "a BEGIN block", Prism::PostExecutionNode => "an END block" }.freeze
Instance Method Summary collapse
-
#initialize(source, max_bytes: SafeSource::DEFAULT_MAX_SOURCE_BYTES, max_depth: SafeSource::DEFAULT_MAX_DEPTH) ⇒ Validator
constructor
A new instance of Validator.
-
#violations ⇒ Array<String>
Every reason this source falls outside the allowlist.
Constructor Details
#initialize(source, max_bytes: SafeSource::DEFAULT_MAX_SOURCE_BYTES, max_depth: SafeSource::DEFAULT_MAX_DEPTH) ⇒ Validator
Returns a new instance of Validator.
81 82 83 84 85 86 87 |
# File 'lib/inquirex/safe_source/validator.rb', line 81 def initialize(source, max_bytes: SafeSource::DEFAULT_MAX_SOURCE_BYTES, max_depth: SafeSource::DEFAULT_MAX_DEPTH) @source = source @max_bytes = max_bytes @max_depth = max_depth end |
Instance Method Details
#violations ⇒ Array<String>
Every reason this source falls outside the allowlist.
92 93 94 |
# File 'lib/inquirex/safe_source/validator.rb', line 92 def violations @violations ||= analyze.uniq.first(MAX_REPORTED) end |