Module: PWN::Plugins::MethodCatalog

Defined in:
lib/pwn/plugins/method_catalog.rb

Overview

JSON-schema descriptors for public PWN::Plugins methods, generated from YARD / Supported Method Parameters docs. pwn_eval validates kwargs first.

Constant Summary collapse

SIDE_EFFECTS =
%w[read_only active_scan exploit destructive].freeze

Class Method Summary collapse

Class Method Details

.authorsObject



70
71
72
# File 'lib/pwn/plugins/method_catalog.rb', line 70

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

.descriptors(opts = {}) ⇒ Object

List descriptors for every public PWN::Plugins class method (lazy per module).



63
64
65
66
67
68
# File 'lib/pwn/plugins/method_catalog.rb', line 63

public_class_method def self.descriptors(opts = {})
  mods = Array(opts[:modules] || plugin_modules)
  mods.flat_map do |mod|
    public_methods_for(mod: mod).map { |name| schema(mod: mod, method: name) }
  end
end

.guard_eval(opts = {}) ⇒ Object

Validate literal kwargs on PWN::Plugins calls; nil means the payload may eval.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/pwn/plugins/method_catalog.rb', line 43

public_class_method def self.guard_eval(opts = {})
  code = opts[:code].to_s
  extract_calls(code: code).each do |call|
    descriptor = schema(mod: call[:mod], method: call[:method])
    allowed = descriptor[:parameters][:properties].keys.map(&:to_s)
    unknown = call[:keys].map(&:to_s) - allowed
    next if unknown.empty?

    return {
      error: "unknown keyword: #{unknown.join(', ')}",
      schema: descriptor,
      method: descriptor[:name]
    }
  end
  nil
rescue SyntaxError
  nil
end

.helpObject



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/pwn/plugins/method_catalog.rb', line 74

public_class_method def self.help
  puts "USAGE:
    # List host binaries this module expects to be installed.
    #{self}.required_bins

    # Build a JSON-schema descriptor for one public plugin method.
    #{self}.schema(
      mod: 'optional - PWN::Plugins::Name or short Name',
      module: 'optional - alias for mod',
      method: 'required - public class method name',
      name: 'optional - alias for method'
    )

    # Validate literal kwargs on PWN::Plugins calls; nil means the payload may eval.
    #{self}.guard_eval(
      code: 'required - Ruby source that may call PWN::Plugins methods'
    )

    # List descriptors for every public PWN::Plugins class method (lazy per module).
    #{self}.descriptors(
      modules: 'optional - Array of PWN::Plugins constants to catalog'
    )

    # Print the AUTHOR(S) string for this module.
    #{self}.authors
  "
  constants.sort
end

.required_binsObject



14
15
16
# File 'lib/pwn/plugins/method_catalog.rb', line 14

public_class_method def self.required_bins
  []
end

.schema(opts = {}) ⇒ Object

Build a JSON-schema descriptor for one public plugin method.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/pwn/plugins/method_catalog.rb', line 19

public_class_method def self.schema(opts = {})
  mod = plugin_const(opts)
  name = (opts[:method] || opts[:name]).to_s
  raise 'ERROR: method is required' if name.empty?

  cache_key = "#{mod}##{name}"
  @schema_cache ||= {}
  return @schema_cache[cache_key] if @schema_cache[cache_key]

  keys = allowed_keys(mod: mod, method: name)
  props = keys.to_h { |key, hint| [key, property_schema(key: key, hint: hint)] }
  @schema_cache[cache_key] = {
    name: "#{mod}.#{name}",
    description: purpose_line(mod: mod, method: name),
    side_effect: classify_side_effect(mod: mod, method: name),
    parameters: {
      type: 'object',
      properties: props,
      additionalProperties: false
    }
  }
end