Module: PWN::AI::Agent::Verification

Defined in:
lib/pwn/ai/agent/verification.rb

Overview

Explicit host-owned acceptance checks, not a model-facing tool.

Class Method Summary collapse

Class Method Details

.authorsObject



173
174
175
# File 'lib/pwn/ai/agent/verification.rb', line 173

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <[email protected]>\n"
end

.helpObject



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/pwn/ai/agent/verification.rb', line 177

public_class_method def self.help
  puts "USAGE:
    # Execute host-owned checks; coverage refers to explicit original-request clauses.
    #{self}.run(
      request: 'required - original request, unchanged',
      requirements: 'required - distinct verbatim request clauses to check',
      root: 'required - allowed artifact directory',
      checks: 'required - host-defined file/json/command/http checks naming a requirement',
      allow_commands: 'optional - explicitly enable literal argv test commands (default false)',
      allowed_urls: 'optional - exact HTTP GET URLs permitted; redirects never followed',
      timeout: 'optional - per-command or HTTP timeout seconds (default 10, capped at 60)',
      actions: 'optional - host-observed action_id and changed artifact digest maps, never model claims'
    )
    # Capture declared artifact digests around an action for provenance.
    #{self}.snapshot(
      root: 'required - allowed artifact directory',
      checks: 'required - declared file/json checks'
    )
    # Display module authors.
    #{self}.authors
  "
end

.run(opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/pwn/ai/agent/verification.rb', line 14

public_class_method def self.run(opts = {})
  request = opts[:request].to_s
  requirements = Array(opts[:requirements]).map(&:to_s)
  raise ArgumentError, 'original request requirements required' if request.empty? || requirements.empty? || requirements.any? { |r| r.strip.empty? || !request.include?(r) } || requirements.uniq != requirements

  root = File.realpath(opts[:root].to_s)
  definitions = Array(opts[:checks])
  definitions.each do |check|
    raise ArgumentError, 'check must name a declared requirement' unless check.is_a?(Hash) && requirements.include?(check[:requirement])
  end

  # Commands may change artifacts. Inspect final bytes only after all
  # active checks have completed, regardless of contract list order.
  active, artifacts = definitions.partition { |check| !%w[file json].include?(check[:kind].to_s) }
  checks = (active + artifacts).map do |check|
    verify_check(opts.merge(check: check, root: root))
  end
  missing = requirements - checks.map { |check| check[:criterion] }
  status = if checks.any? { |check| check[:passed] == false }
             :fail
           elsif missing.any? || checks.any? { |check| check[:passed].nil? }
             :unknown
           else
             :pass
           end
  ids = checks.filter_map do |check|
    artifact = check[:artifact]
    next unless artifact && !check[:passed].nil?

    writer = Array(opts[:actions]).reverse.find { |action| action[:artifacts].is_a?(Hash) && action[:artifacts].key?(artifact[:path_digest]) }
    writer[:action_id] if writer && writer[:artifacts][artifact[:path_digest]] == artifact[:sha256]
  end.uniq
  { runner_version: 1, request_digest: Digest::SHA256.hexdigest(request), requirements: requirements, missing: missing, checks: checks, status: status,
    attribution: { source: 'independent_verifier', verified_action_ids: ids } }
end

.snapshot(opts = {}) ⇒ Object



50
51
52
53
54
55
56
57
58
59
# File 'lib/pwn/ai/agent/verification.rb', line 50

public_class_method def self.snapshot(opts = {})
  root = File.realpath(opts[:root].to_s)
  Array(opts[:checks]).each_with_object({}) do |check, artifacts|
    next unless %w[file json].include?(check[:kind].to_s)

    path = File.expand_path(check[:path].to_s, root)
    row = verify_check(root: root, check: check.merge(kind: :file, expected: nil))
    artifacts[Digest::SHA256.hexdigest(path)] = row.dig(:artifact, :sha256)
  end
end