Class: Terret::Composition::Visitor
- Inherits:
-
Psych::Visitors::ToRuby
- Object
- Psych::Visitors::ToRuby
- Terret::Composition::Visitor
- Defined in:
- lib/terret/composition.rb
Overview
YAML.safe_load DROPS a local tag silently: permitted_classes gates
Ruby-object tags like !ruby/object:Foo, not application tags like !env,
so a document loaded that way comes back with the tag gone and the bare
scalar in its place — !env OPENROUTER_API_KEY would resolve to the
STRING "OPENROUTER_API_KEY" and boot a service with a literal nonsense
key. So resolution is explicit: parse to the node tree, then walk it and
resolve our three tags by tag, refusing every other one. The class loader
stays restricted throughout, so this is safe_load's safety with our tags
intercepted before Psych can drop them. YAML.load appears nowhere.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(label) ⇒ Visitor
constructor
A new instance of Visitor.
-
#visit_Psych_Nodes_Mapping(node) ⇒ Object
A collection carrying one of our tags is refused rather than silently dropped, which is what the parent visitor does with one.
- #visit_Psych_Nodes_Scalar(node) ⇒ Object
- #visit_Psych_Nodes_Sequence(node) ⇒ Object
Constructor Details
#initialize(label) ⇒ Visitor
Returns a new instance of Visitor.
201 202 203 204 205 |
# File 'lib/terret/composition.rb', line 201 def initialize(label) loader = Psych::ClassLoader::Restricted.new([], []) super(Psych::ScalarScanner.new(loader), loader, symbolize_names: true) @label = label end |
Class Method Details
.load(text, label:) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/terret/composition.rb', line 180 def self.load(text, label:) stream = Psych.parse_stream(text) docs = stream.children if docs.length > 1 raise Error, "#{label}: #{docs.length} YAML documents; a Terret config is one " \ "document, and the rest would be dropped without saying so" end return nil if docs.empty? value = new(label).accept(docs.first) Composition.assert_acyclic!(value, label) value rescue Error raise rescue StandardError => e # Psych's own exceptions, and the ArgumentError/FrozenError/NoMethodError # its schema handlers raise on input they cannot honour. Whatever it is, # the operator needs the file name more than the class. raise Error, "#{label}: #{e.class}: #{e.message}" end |
Instance Method Details
#visit_Psych_Nodes_Mapping(node) ⇒ Object
A collection carrying one of our tags is refused rather than silently dropped, which is what the parent visitor does with one.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/terret/composition.rb', line 229 def visit_Psych_Nodes_Mapping(node) refuse_collection_tag!(node, CORE_MAPPING_TAGS) mapping = super # Psych merges `<<` when it points at a mapping or at a list of them. # Anything else it leaves as a literal "<<" key — a String among # symbols, and a merge the author believed had happened. Quoting the # key does not opt out: Psych reads `"<<"` as a merge too, so the # refusal names the spelling that does. if mapping.is_a?(Hash) && mapping.key?("<<") raise Error, "#{@label}: a << merge key must point at a mapping or a list of them; " \ "anything else is not a merge, and would land as a literal \"<<\" key. " \ "For a key that is genuinely the characters <<, write !!str \"<<\":." end mapping end |
#visit_Psych_Nodes_Scalar(node) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/terret/composition.rb', line 207 def visit_Psych_Nodes_Scalar(node) tag = terret_tag(node, CORE_SCALAR_TAGS) return register(node, Tagged.new(tag: tag, argument: node.value)) if tag value = begin super rescue Psych::DisallowedClass => e raise Error, needs_quoting(node, e) end # Unlike a Date, a sexagesimal (10:30 -> 37800, 1:2:3 -> 3723) is a plain # Integer the restricted loader builds without complaint, so it slips past # the DisallowedClass guard above. A colon-bearing plain scalar that came # back a number is almost never the base-60 number YAML made of it — # refuse it and say to quote it, the same fix the Date guard gives. raise Error, sexagesimal_needs_quoting(node) if value.is_a?(Numeric) && node.value.include?(":") value end |
#visit_Psych_Nodes_Sequence(node) ⇒ Object
246 247 248 249 |
# File 'lib/terret/composition.rb', line 246 def visit_Psych_Nodes_Sequence(node) refuse_collection_tag!(node, CORE_SEQUENCE_TAGS) super end |