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

.authors ⇒ Object



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

.help ⇒ Object



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_bins ⇒ Object



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