pwn 0.5.652 → 0.5.656

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fd0197b1fa729406ea99bbe598d3730d27e3c6881251c374af0f894ea541729
4
- data.tar.gz: 492617f851780cf17c04fcdecdd4eb491e54b2bcf115b04997f3972507528d3a
3
+ metadata.gz: ba4560cb04bae940cc23704a47b54f60a33a47587531101f340beb12897b12e9
4
+ data.tar.gz: 7635dbe7cb031343f1df74c430b22c1a9db3b2a23d3990812573e0785dd4aaa1
5
5
  SHA512:
6
- metadata.gz: ad8894f7551643e63ec4819d7a2513e3114d0f2649eb21ea8b9554e150319a7df0d62e6c46d9979d5c0462c5617f054f93f2140c81704b74fa3d8f763c428d90
7
- data.tar.gz: f84e73f55040588558281b2941a41b602b2f3a6471c5cbbacdd7afc42a9b2dbf91c8c9c01e68079dcaedee03a25aad25eb17558d3ffec62f0ce8539f5e6ef1a5
6
+ metadata.gz: e21ee1d6b4e66d692b6ad5896f1ec0f87426258854911d9dc7759d567dd3b472ab8dc99409ca33e7412afc7bab00d0174d56fa9cc979074b0192cc2bc43e0459
7
+ data.tar.gz: fdbed596d420d497b1874c1f5473c513327813b2625b2b9bedf7f21d5dbe51da26598c6e5a2082ebc7d2735f4f56e69a3f8feb9f99d2883899230c39b02e6b09
data/Gemfile CHANGED
@@ -11,7 +11,7 @@ gemspec
11
11
  # In some circumstances custom flags are passed to gems in order
12
12
  # to build appropriately. Defer to ./reinstall_pwn_gemset.sh
13
13
  # to review these custom flags (e.g. pg, serialport, etc).
14
- gem 'activesupport', '<8.1.3'
14
+ gem 'activesupport', '<8.1.3.1'
15
15
  gem 'anemone', '0.7.2'
16
16
  gem 'authy', '3.0.1'
17
17
  gem 'aws-sdk', '3.3.0'
@@ -46,11 +46,11 @@ gem 'json', '>=2.13.2'
46
46
  gem 'jsonpath', '1.1.5'
47
47
  gem 'json_schemer', '2.5.0'
48
48
  gem 'jwt', '3.2.0'
49
- gem 'libusb', '0.7.2'
49
+ gem 'libusb', '0.8.0'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
- gem 'mcp', '1.0.0'
53
- gem 'meshtastic', '0.0.172'
52
+ gem 'mcp', '1.1.0'
53
+ gem 'meshtastic', '0.0.173'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.25.0'
56
56
  gem 'msfrpc-client', '1.1.2'
@@ -77,11 +77,11 @@ gem 'rbvmomi2', '3.10.0'
77
77
  gem 'rdoc', '7.0.4'
78
78
  gem 'rest-client', '2.1.0'
79
79
  gem 'rex', '2.0.13'
80
- gem 'rmagick', '7.0.5'
80
+ gem 'rmagick', '7.1.0'
81
81
  gem 'rqrcode', '3.2.0'
82
82
  gem 'rspec', '3.13.2'
83
83
  gem 'rtesseract', '3.1.4'
84
- gem 'rubocop', '1.88.2'
84
+ gem 'rubocop', '1.89.0'
85
85
  gem 'rubocop-rake', '0.7.1'
86
86
  gem 'rubocop-rspec', '3.10.2'
87
87
  gem 'ruby-audio', '1.6.1'
@@ -327,7 +327,7 @@ module PWN
327
327
  body = r[:body].to_s
328
328
  absent = site[:absent_status].include?(code) ||
329
329
  # substring match against page body — NOT a set intersection despite the cop name.
330
- site[:absent_body].any? { |s| body.include?(s) } || # rubocop:disable Style/ArrayIntersect
330
+ site[:absent_body].any? { |s| body.include?(s) } ||
331
331
  !code.between?(200, 399)
332
332
  absent ? :absent : :present
333
333
  end
@@ -100,6 +100,36 @@ module PWN
100
100
  false
101
101
  end
102
102
 
103
+ # P28 — incomplete / handoff finals: model emitted text-only before the
104
+ # goal was done ("shall I proceed?", "next step:", "want me to…").
105
+ # Loop.run treats no-tool_calls as FINAL; this detector lets us refuse
106
+ # that handoff and keep the tool loop alive for multi-step autonomy.
107
+ INCOMPLETE_FINAL_RX = /
108
+ \b(shall\s+i|should\s+i|may\s+i|can\s+i|want\s+me\s+to|do\s+you\s+want\s+me|
109
+ next\s+single\s+step|next\s+step\s*:|awaiting\s+your\s+(ok|approval|go-ahead|confirmation)|
110
+ if\s+you(?:'d|\s+would)\s+like\s+me\s+to|say\s+the\s+word|confirm\s+(before|and\s+i)|
111
+ ready\s+to\s+proceed|ok\s+to\s+(proceed|continue|apply)|proceed\?|
112
+ continue\?|before\s+i\s+(apply|change|run|continue|proceed)|
113
+ once\s+you\s+(confirm|approve)|let\s+me\s+know\s+if|
114
+ i(?:'ll|\s+will)\s+wait\b|waiting\s+for\s+(your\s+)?(go|ok|approval|confirmation)
115
+ )\b
116
+ /ix
117
+
118
+ private_class_method def self.incomplete_final?(opts = {})
119
+ text = opts[:text].to_s
120
+ return false if text.strip.empty?
121
+ # Hard last-iter forces a real final; do not bounce that forever.
122
+ return false if opts[:last_iter]
123
+ return true if text.match?(INCOMPLETE_FINAL_RX)
124
+ # Short status-only dumps with a trailing question are handoffs.
125
+ return true if text.include?('?') && text.length < 900 &&
126
+ text.match?(/\b(proceed|continue|confirm|apply|next)\b/i)
127
+
128
+ false
129
+ rescue StandardError
130
+ false
131
+ end
132
+
103
133
  private_class_method def self.max_iters
104
134
  v = (PWN::Env.dig(:ai, :agent, :max_iters) if defined?(PWN::Env))
105
135
  n = v.to_i.positive? ? v.to_i : DEFAULT_MAX_ITERS
@@ -116,11 +146,12 @@ module PWN
116
146
  # is "iteration budget exhausted" ×N; more headroom only produces more
117
147
  # empty terminal failures for ORM/PRM/DPO.
118
148
  if budget_exhaustion_hot?
119
- # P17 deepen²remote was still burning full 12-step plans even when
120
- # overconfidence sat just under the 0.25 gate (0.242 on grok). Always
121
- # cap to 8 for ALL engines while budget fingerprints dominate; finish-
122
- # under-N is the skill gap, more headroom only yields empty terminals.
123
- n = [n, 8].min
149
+ # P17 + P28 finish-under-N while budget fingerprints dominate, but
150
+ # do NOT collapse multi-step remote work to 8 (that re-creates the
151
+ # polite handoff / early empty-final failure mode P28 fixed).
152
+ # Local/ollama stays harsh at 8; remote engines keep the W3 runway.
153
+ hot_cap = active_engine == :ollama ? 8 : 40
154
+ n = [n, hot_cap].min
124
155
  end
125
156
  n
126
157
  rescue StandardError
@@ -141,11 +172,22 @@ module PWN
141
172
  # P17 — gate lowered 0.25→0.20: grok lived at 0.242 and never tripped,
142
173
  # leaving force_plan off while still thrashing tool budgets.
143
174
  bad = brier > 0.35 || over > 0.20
175
+ # P28 — autonomy: overconfidence must force plan+critic and shrink thrash,
176
+ # but must NOT collapse multi-step remote work to 8 iters (user-visible
177
+ # "stop to confirm next step" / early text-only handoffs). Local models
178
+ # keep the harsh 8; remote engines keep a usable multi-step runway.
179
+ remote_cap = 40
180
+ local_cap = 8
181
+ cap = if bad
182
+ (eng == :ollama ? local_cap : remote_cap)
183
+ else
184
+ 25
185
+ end
144
186
  {
145
187
  overconfident: bad,
146
188
  force_plan: bad,
147
189
  force_critic: bad,
148
- max_iters_cap: bad ? 8 : 25,
190
+ max_iters_cap: cap,
149
191
  cal: cal
150
192
  }
151
193
  rescue StandardError
@@ -281,7 +323,7 @@ module PWN
281
323
  plan_prompt = if hot
282
324
  'Before acting: write AT MOST 3 numbered tool calls (name + key args) that finish the ask. Prefer fewer. LAST line: "p(success)=<0.0-1.0>". Reply ONLY with the plan + that line — no tools, no prose.'
283
325
  else
284
- 'Before acting: (1) list the exact tool calls (name + key args) you will make, in order; (2) on the LAST line write "p(success)=<0.0-1.0>". Reply ONLY with the numbered plan + that line — do NOT call any tool yet.'
326
+ 'Before acting: (1) list the exact tool calls (name + key args) that FULLY finish the user goal, in order — do not stop at a checkpoint for confirmation; (2) on the LAST line write "p(success)=<0.0-1.0>". Reply ONLY with the numbered plan + that line — do NOT call any tool yet.'
285
327
  end
286
328
  plan_msg = call_engine(
287
329
  messages: messages + [{ role: 'user', content: plan_prompt }],
@@ -599,8 +641,10 @@ module PWN
599
641
  messages << {
600
642
  role: 'user',
601
643
  content: "[pwn-ai/p17] #{tag} — do NOT call any more tools. " \
602
- 'Write the best answer you can from evidence already in this ' \
603
- 'transcript. If blocked, say what failed and the next single step.'
644
+ 'Write the best complete answer you can from evidence already in this ' \
645
+ 'transcript. If the goal is unfinished, report exactly what is done, ' \
646
+ 'what is blocked, and the concrete remaining work — do NOT ask the ' \
647
+ 'user to confirm the next step.'
604
648
  }
605
649
  end
606
650
 
@@ -630,6 +674,19 @@ module PWN
630
674
  messages << msg
631
675
 
632
676
  if calls.empty?
677
+ # P28 — refuse polite mid-goal handoffs so multi-step tasks stay autonomous.
678
+ if incomplete_final?(text: text, last_iter: last_iter) && turn_fails['incomplete_final'].to_i < 2
679
+ turn_fails['incomplete_final'] += 1
680
+ warn "[pwn-ai/loop] incomplete final on iter=#{i}; continuing autonomously"
681
+ messages << {
682
+ role: 'user',
683
+ content: '[pwn-ai/p28] That reply handed control back before the goal was done. ' \
684
+ 'Do NOT ask the user to confirm the next step. Continue with the ' \
685
+ 'necessary tool calls now and finish the goal autonomously. Only ' \
686
+ 'emit a final answer when the request is complete or truly blocked.'
687
+ }
688
+ next
689
+ end
633
690
  append_session(session_id: session_id, role: 'assistant', content: text)
634
691
  Learning.auto_introspect(session_id: session_id, request: request, final: text, predicted: predicted) if defined?(Learning) && should_auto_introspect?(local: local, turn_fails: turn_fails, iter: i)
635
692
  return text
@@ -771,6 +828,9 @@ module PWN
771
828
  :hindsight - C3 HER-relabel failures (Boolean, default true)
772
829
  :verify_as_reward - E3 ground every final via extro_verify (Boolean)
773
830
 
831
+ P28 autonomy: incomplete-final detector refuses mid-goal handoffs;
832
+ W3 overconf max_iters_cap is 40 on remote engines (8 on ollama).
833
+
774
834
  #{self}.authors
775
835
  USAGE
776
836
  end
@@ -54,6 +54,15 @@ module PWN
54
54
  no tool_calls is treated as your FINAL answer to the user.
55
55
  Prefer `pwn_eval` for anything in the PWN:: namespace and `shell`
56
56
  for OS commands. Save durable facts with `memory_remember`.
57
+
58
+ AUTONOMY
59
+ Multi-step goals must be finished in one Loop.run. Keep calling
60
+ tools until the request is done or truly blocked. Do NOT stop to
61
+ ask the user to confirm the next step, approve a partial plan, or
62
+ green-light the obvious continuation. Only ask when a credential,
63
+ irreversible destructive action, or missing external decision is
64
+ strictly required. Partial progress reports without completing the
65
+ goal are incorrect behavior.
57
66
  "
58
67
  end
59
68
 
@@ -1,113 +1,632 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
3
  require 'base64'
4
+ require 'json'
5
+ require 'rest-client'
5
6
 
6
7
  module PWN
7
8
  module Plugins
8
- # This plugin is used for interacting w/ HackerOne's REST API using
9
+ # This plugin is used for interacting w/ HackerOne's Hacker REST API using
9
10
  # the 'rest' browser type of PWN::Plugins::TransparentBrowser.
11
+ #
12
+ # Spec: https://api.hackerone.com/getting-started-hacker-api/#getting-started-hacker-api
13
+ #
14
+ # Auth: HTTP Basic on every request (username + API token).
15
+ # Base: https://api.hackerone.com/v1/hackers/
16
+ #
17
+ # Credentials are sourced from PWN::Env[:plugins][:hackerone]:
18
+ # plugins:
19
+ # hackerone:
20
+ # username: your-h1-username
21
+ # api_key: your-h1-api-token
10
22
  module HackerOne
11
23
  @@logger = PWN::Plugins::PWNLogger.create
12
24
 
25
+ BASE_H1_API_URI = 'https://api.hackerone.com/v1/hackers'
26
+
27
+ # Resolve username: explicit opt -> PWN::Env -> ENV
28
+ private_class_method def self.resolve_username(opts = {})
29
+ opts[:username] ||
30
+ PWN::Env.dig(:plugins, :hackerone, :username) ||
31
+ ENV.fetch('HACKERONE_USERNAME', nil)
32
+ end
33
+
34
+ # Resolve API token: explicit opt -> PWN::Env -> ENV
35
+ # Accepts :api_key, :token, or :api_token aliases.
36
+ private_class_method def self.resolve_api_key(opts = {})
37
+ opts[:api_key] ||
38
+ opts[:token] ||
39
+ opts[:api_token] ||
40
+ PWN::Env.dig(:plugins, :hackerone, :api_key) ||
41
+ PWN::Env.dig(:plugins, :hackerone, :token) ||
42
+ ENV.fetch('HACKERONE_API_KEY', nil) ||
43
+ ENV.fetch('HACKERONE_TOKEN', nil)
44
+ end
45
+
13
46
  # Supported Method Parameters::
14
- # h1_obj = PWN::Plugins::HackerOne.login(
15
- # username: 'required - username',
16
- # token: 'optional - api token (will prompt if nil)'
47
+ # h1_rest_call(
48
+ # http_method: 'optional HTTP method (defaults to GET)',
49
+ # rest_call: 'required rest path relative to /v1/hackers (e.g. "me/reports")',
50
+ # params: 'optional query params Hash',
51
+ # http_body: 'optional HTTP body (Hash/Array auto-JSONified; String passed through)',
52
+ # username: 'optional - HackerOne username (default PWN::Env)',
53
+ # api_key: 'optional - HackerOne API token (default PWN::Env)',
54
+ # raw: 'optional - return raw RestClient::Response (default false)'
17
55
  # )
18
56
 
19
- public_class_method def self.login(opts = {})
20
- username = opts[:username].to_s.scrub
21
- base_h1_api_uri = 'https://api.hackerone.com/v1/'.to_s.scrub
57
+ private_class_method def self.h1_rest_call(opts = {})
58
+ http_method = if opts[:http_method].nil?
59
+ :get
60
+ else
61
+ opts[:http_method].to_s.scrub.to_sym
62
+ end
63
+ rest_call = opts[:rest_call].to_s.scrub.sub(%r{\A/}, '')
64
+ params = opts[:params]
65
+ http_body = opts[:http_body]
66
+ http_body = http_body.to_json if http_body.is_a?(Hash) || http_body.is_a?(Array)
67
+ raw = opts[:raw]
22
68
 
23
- token = if opts[:token].nil?
24
- PWN::Plugins::AuthenticationHelper.mask_password
25
- else
26
- opts[:token].to_s.scrub
27
- end
69
+ username = resolve_username(opts).to_s.scrub
70
+ api_key = resolve_api_key(opts).to_s.scrub
71
+ raise 'ERROR: HackerOne username missing - set plugins.hackerone.username in ~/.pwn/pwn.yaml' if username.empty?
72
+ raise 'ERROR: HackerOne api_key missing - set plugins.hackerone.api_key in ~/.pwn/pwn.yaml' if api_key.empty?
28
73
 
29
- auth_payload = {}
30
- auth_payload[:username] = username
31
- auth_payload[:token] = token
74
+ basic_auth_header = "Basic #{Base64.strict_encode64("#{username}:#{api_key}")}"
32
75
 
33
- base64_str = "#{username}:#{token}"
34
- base64_encoded_auth = Base64.strict_encode64(base64_str).to_s.chomp
35
- basic_auth_header = "Basic #{base64_encoded_auth}"
76
+ headers = {
77
+ authorization: basic_auth_header,
78
+ accept: 'application/json',
79
+ content_type: 'application/json; charset=UTF-8'
80
+ }
81
+ headers[:params] = params if params
36
82
 
37
- @@logger.info("Logging into HackerOne REST API: #{base_h1_api_uri}")
38
83
  browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
39
84
  rest_client = browser_obj[:browser]::Request
85
+
86
+ url = rest_call.empty? ? BASE_H1_API_URI : "#{BASE_H1_API_URI}/#{rest_call}"
87
+
40
88
  response = rest_client.execute(
41
- method: :get,
42
- url: base_h1_api_uri,
43
- headers: {
44
- authorization: basic_auth_header,
45
- content_type: 'application/json; charset=UTF-8'
46
- }
89
+ method: http_method,
90
+ url: url,
91
+ headers: headers,
92
+ payload: http_body,
93
+ verify_ssl: false
47
94
  )
48
95
 
49
- # Return array containing the post-authenticated HackerOne REST API token
50
- json_response = JSON.parse(response, symbolize_names: true)
51
- h1_success = json_response['success']
52
- api_token = json_response['token']
53
- h1_obj = {}
54
- h1_obj[:h1_success] = h1_success
55
- h1_obj[:api_token] = api_token
56
- h1_obj[:raw_response] = response
96
+ return response if raw
57
97
 
58
- h1_obj
98
+ JSON.parse(response.body, symbolize_names: true)
99
+ rescue ::RestClient::TooManyRequests
100
+ @@logger.warn('HackerOne rate limit (429). Sleeping 10s then retrying...')
101
+ sleep 10
102
+ retry
103
+ rescue ::RestClient::Unauthorized, ::RestClient::Forbidden,
104
+ ::RestClient::BadRequest, ::RestClient::NotFound,
105
+ ::RestClient::UnprocessableEntity => e
106
+ @@logger.error("HackerOne #{e.class}: #{e.response&.body}")
107
+ raise e
59
108
  rescue StandardError => e
60
109
  raise e
61
110
  end
62
111
 
63
112
  # Supported Method Parameters::
64
- # h1_rest_call(
65
- # h1_obj: 'required h1_obj returned from #login method',
66
- # http_method: 'optional HTTP method (defaults to GET)
67
- # rest_call: 'required rest call to make per the schema',
68
- # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST'
113
+ # h1_obj = PWN::Plugins::HackerOne.login(
114
+ # username: 'optional - username (default PWN::Env[:plugins][:hackerone][:username])',
115
+ # api_key: 'optional - api token (default PWN::Env; will prompt if still nil)',
116
+ # token: 'optional - alias for api_key'
69
117
  # )
118
+ # Backward-compatible session object. Auth is HTTP Basic on every subsequent
119
+ # call; login simply resolves + validates credentials against me/reports.
70
120
 
71
- private_class_method def self.h1_rest_call(opts = {})
72
- h1_obj = opts[:h1_obj]
73
- http_method = if opts[:http_method].nil?
74
- :get
75
- else
76
- opts[:http_method].to_s.scrub.to_sym
77
- end
78
- rest_call = opts[:rest_call].to_s.scrub
79
- http_body = opts[:http_body].to_s.scrub
80
- h1_success = h1_obj[:h1_success].to_s.scrub
81
- base_h1_api_uri = 'https://api.hackerone.com/v1/'.to_s.scrub
82
- api_token = h1_obj[:api_token]
121
+ public_class_method def self.login(opts = {})
122
+ username = resolve_username(opts)
123
+ api_key = resolve_api_key(opts)
83
124
 
84
- browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)::Request
85
- rest_client = browser_obj[:browser]::Request
125
+ api_key = PWN::Plugins::AuthenticationHelper.mask_password if api_key.nil? || api_key.to_s.empty?
86
126
 
87
- case http_method
88
- when :get
89
- response = rest_client.execute(
90
- method: :get,
91
- url: "#{base_h1_api_uri}/#{rest_call}",
92
- headers: {
93
- content_type: 'application/json; charset=UTF-8',
94
- params: { token: api_token }
95
- }
96
- )
127
+ username = username.to_s.scrub
128
+ api_key = api_key.to_s.scrub
129
+ raise 'ERROR: HackerOne username required' if username.empty?
130
+ raise 'ERROR: HackerOne api_key required' if api_key.empty?
97
131
 
98
- when :post
99
- response = rest_client.execute(
100
- method: :post,
101
- url: "#{base_h1_api_uri}/#{rest_call}",
102
- headers: {
103
- content_type: 'application/json; charset=UTF-8'
104
- },
105
- payload: http_body
106
- )
132
+ @@logger.info("Validating HackerOne Hacker API credentials for #{username}")
133
+ # Prefer a cheap authenticated endpoint (balance is small; me/reports is list)
134
+ h1_rest_call(
135
+ rest_call: 'payments/balance',
136
+ username: username,
137
+ api_key: api_key
138
+ )
139
+
140
+ {
141
+ username: username,
142
+ api_key: api_key,
143
+ base_h1_api_uri: BASE_H1_API_URI
144
+ }
145
+ rescue StandardError => e
146
+ raise e
147
+ end
148
+
149
+ # Supported Method Parameters::
150
+ # response = PWN::Plugins::HackerOne.api(
151
+ # path: 'required - path relative to /v1/hackers (e.g. "me/reports")',
152
+ # method: 'optional - :get|:post|:put|:patch|:delete (default :get)',
153
+ # params: 'optional - query params Hash',
154
+ # body: 'optional - request body Hash/Array/String',
155
+ # username: 'optional',
156
+ # api_key: 'optional',
157
+ # h1_obj: 'optional - session from #login (supplies username/api_key)',
158
+ # raw: 'optional - return raw response (default false)'
159
+ # )
160
+
161
+ public_class_method def self.api(opts = {})
162
+ creds = creds_from(opts)
163
+ h1_rest_call(
164
+ http_method: opts[:method] || opts[:http_method] || :get,
165
+ rest_call: opts[:path].to_s.delete_prefix('/'),
166
+ params: opts[:params],
167
+ http_body: opts[:body] || opts[:http_body],
168
+ username: creds[:username],
169
+ api_key: creds[:api_key],
170
+ raw: opts[:raw]
171
+ )
172
+ rescue StandardError => e
173
+ raise e
174
+ end
175
+
176
+ # Pull username/api_key from opts or h1_obj session.
177
+ private_class_method def self.creds_from(opts = {})
178
+ h1_obj = opts[:h1_obj] || {}
179
+ {
180
+ username: opts[:username] || h1_obj[:username],
181
+ api_key: opts[:api_key] || opts[:token] || opts[:api_token] || h1_obj[:api_key] || h1_obj[:token]
182
+ }
183
+ end
184
+
185
+ # ---- Reports -------------------------------------------------------
186
+
187
+ # Supported Method Parameters::
188
+ # reports = PWN::Plugins::HackerOne.get_me_reports(
189
+ # params: 'optional - query params Hash (e.g. page/filter)',
190
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
191
+ # )
192
+
193
+ public_class_method def self.get_me_reports(opts = {})
194
+ creds = creds_from(opts)
195
+ h1_rest_call(
196
+ rest_call: 'me/reports',
197
+ params: opts[:params],
198
+ username: creds[:username],
199
+ api_key: creds[:api_key]
200
+ )
201
+ rescue StandardError => e
202
+ raise e
203
+ end
107
204
 
108
- else
109
- raise @@logger.error("Unsupported HTTP Method #{http_method} for #{self} Plugin")
205
+ # Supported Method Parameters::
206
+ # report = PWN::Plugins::HackerOne.get_report(
207
+ # id: 'required - report id',
208
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
209
+ # )
210
+
211
+ public_class_method def self.get_report(opts = {})
212
+ id = opts[:id]
213
+ raise 'ERROR: report id required' if id.nil? || id.to_s.empty?
214
+
215
+ creds = creds_from(opts)
216
+ h1_rest_call(
217
+ rest_call: "reports/#{id}",
218
+ params: opts[:params],
219
+ username: creds[:username],
220
+ api_key: creds[:api_key]
221
+ )
222
+ rescue StandardError => e
223
+ raise e
224
+ end
225
+
226
+ # Supported Method Parameters::
227
+ # report = PWN::Plugins::HackerOne.create_report(
228
+ # team_handle: 'required - program handle',
229
+ # title: 'required - report title',
230
+ # vulnerability_information: 'required - detailed description',
231
+ # impact: 'optional - impact text',
232
+ # severity_rating: 'optional - none|low|medium|high|critical',
233
+ # weakness_id: 'optional - weakness id integer',
234
+ # structured_scope_id: 'optional - structured scope id',
235
+ # body: 'optional - full JSON:API body Hash (overrides attribute kwargs)',
236
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
237
+ # )
238
+
239
+ public_class_method def self.create_report(opts = {})
240
+ creds = creds_from(opts)
241
+ http_body = opts[:body]
242
+ if http_body.nil?
243
+ attrs = {
244
+ team_handle: opts[:team_handle],
245
+ title: opts[:title],
246
+ vulnerability_information: opts[:vulnerability_information]
247
+ }
248
+ attrs[:impact] = opts[:impact] if opts.key?(:impact)
249
+ attrs[:severity_rating] = opts[:severity_rating] if opts.key?(:severity_rating)
250
+ attrs[:weakness_id] = opts[:weakness_id] if opts.key?(:weakness_id)
251
+ attrs[:structured_scope_id] = opts[:structured_scope_id] if opts.key?(:structured_scope_id)
252
+
253
+ raise 'ERROR: team_handle required' if attrs[:team_handle].to_s.empty?
254
+ raise 'ERROR: title required' if attrs[:title].to_s.empty?
255
+ raise 'ERROR: vulnerability_information required' if attrs[:vulnerability_information].to_s.empty?
256
+
257
+ http_body = {
258
+ data: {
259
+ type: 'report',
260
+ attributes: attrs
261
+ }
262
+ }
110
263
  end
264
+
265
+ h1_rest_call(
266
+ http_method: :post,
267
+ rest_call: 'reports',
268
+ http_body: http_body,
269
+ username: creds[:username],
270
+ api_key: creds[:api_key]
271
+ )
272
+ rescue StandardError => e
273
+ raise e
274
+ end
275
+
276
+ # ---- Payments ------------------------------------------------------
277
+
278
+ # Supported Method Parameters::
279
+ # balance = PWN::Plugins::HackerOne.get_balance(
280
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
281
+ # )
282
+
283
+ public_class_method def self.get_balance(opts = {})
284
+ creds = creds_from(opts)
285
+ h1_rest_call(
286
+ rest_call: 'payments/balance',
287
+ username: creds[:username],
288
+ api_key: creds[:api_key]
289
+ )
290
+ rescue StandardError => e
291
+ raise e
292
+ end
293
+
294
+ # Supported Method Parameters::
295
+ # earnings = PWN::Plugins::HackerOne.get_earnings(
296
+ # params: 'optional - query params Hash',
297
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
298
+ # )
299
+
300
+ public_class_method def self.get_earnings(opts = {})
301
+ creds = creds_from(opts)
302
+ h1_rest_call(
303
+ rest_call: 'payments/earnings',
304
+ params: opts[:params],
305
+ username: creds[:username],
306
+ api_key: creds[:api_key]
307
+ )
308
+ rescue StandardError => e
309
+ raise e
310
+ end
311
+
312
+ # Supported Method Parameters::
313
+ # payouts = PWN::Plugins::HackerOne.get_payouts(
314
+ # params: 'optional - query params Hash',
315
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
316
+ # )
317
+
318
+ public_class_method def self.get_payouts(opts = {})
319
+ creds = creds_from(opts)
320
+ h1_rest_call(
321
+ rest_call: 'payments/payouts',
322
+ params: opts[:params],
323
+ username: creds[:username],
324
+ api_key: creds[:api_key]
325
+ )
326
+ rescue StandardError => e
327
+ raise e
328
+ end
329
+
330
+ # ---- Programs -------------------------------------------------------
331
+
332
+ # Supported Method Parameters::
333
+ # programs = PWN::Plugins::HackerOne.get_programs(
334
+ # params: 'optional - query params Hash',
335
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
336
+ # )
337
+
338
+ public_class_method def self.get_programs(opts = {})
339
+ creds = creds_from(opts)
340
+ h1_rest_call(
341
+ rest_call: 'programs',
342
+ params: opts[:params],
343
+ username: creds[:username],
344
+ api_key: creds[:api_key]
345
+ )
346
+ rescue StandardError => e
347
+ raise e
348
+ end
349
+
350
+ # Supported Method Parameters::
351
+ # program = PWN::Plugins::HackerOne.get_program(
352
+ # handle: 'required - program handle',
353
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
354
+ # )
355
+
356
+ public_class_method def self.get_program(opts = {})
357
+ handle = opts[:handle].to_s.scrub
358
+ raise 'ERROR: program handle required' if handle.empty?
359
+
360
+ creds = creds_from(opts)
361
+ h1_rest_call(
362
+ rest_call: "programs/#{handle}",
363
+ params: opts[:params],
364
+ username: creds[:username],
365
+ api_key: creds[:api_key]
366
+ )
367
+ rescue StandardError => e
368
+ raise e
369
+ end
370
+
371
+ # Supported Method Parameters::
372
+ # scopes = PWN::Plugins::HackerOne.get_structured_scopes(
373
+ # handle: 'required - program handle',
374
+ # params: 'optional',
375
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
376
+ # )
377
+
378
+ public_class_method def self.get_structured_scopes(opts = {})
379
+ handle = opts[:handle].to_s.scrub
380
+ raise 'ERROR: program handle required' if handle.empty?
381
+
382
+ creds = creds_from(opts)
383
+ h1_rest_call(
384
+ rest_call: "programs/#{handle}/structured_scopes",
385
+ params: opts[:params],
386
+ username: creds[:username],
387
+ api_key: creds[:api_key]
388
+ )
389
+ rescue StandardError => e
390
+ raise e
391
+ end
392
+
393
+ # Supported Method Parameters::
394
+ # exclusions = PWN::Plugins::HackerOne.get_scope_exclusions(
395
+ # handle: 'required - program handle',
396
+ # params: 'optional',
397
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
398
+ # )
399
+
400
+ public_class_method def self.get_scope_exclusions(opts = {})
401
+ handle = opts[:handle].to_s.scrub
402
+ raise 'ERROR: program handle required' if handle.empty?
403
+
404
+ creds = creds_from(opts)
405
+ h1_rest_call(
406
+ rest_call: "programs/#{handle}/scope_exclusions",
407
+ params: opts[:params],
408
+ username: creds[:username],
409
+ api_key: creds[:api_key]
410
+ )
411
+ rescue StandardError => e
412
+ raise e
413
+ end
414
+
415
+ # Supported Method Parameters::
416
+ # weaknesses = PWN::Plugins::HackerOne.get_weaknesses(
417
+ # handle: 'required - program handle',
418
+ # params: 'optional',
419
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
420
+ # )
421
+
422
+ public_class_method def self.get_weaknesses(opts = {})
423
+ handle = opts[:handle].to_s.scrub
424
+ raise 'ERROR: program handle required' if handle.empty?
425
+
426
+ creds = creds_from(opts)
427
+ h1_rest_call(
428
+ rest_call: "programs/#{handle}/weaknesses",
429
+ params: opts[:params],
430
+ username: creds[:username],
431
+ api_key: creds[:api_key]
432
+ )
433
+ rescue StandardError => e
434
+ raise e
435
+ end
436
+
437
+ # ---- Hacktivity -----------------------------------------------------
438
+
439
+ # Supported Method Parameters::
440
+ # items = PWN::Plugins::HackerOne.get_hacktivity(
441
+ # params: 'optional - query params Hash',
442
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
443
+ # )
444
+
445
+ public_class_method def self.get_hacktivity(opts = {})
446
+ creds = creds_from(opts)
447
+ h1_rest_call(
448
+ rest_call: 'hacktivity',
449
+ params: opts[:params],
450
+ username: creds[:username],
451
+ api_key: creds[:api_key]
452
+ )
453
+ rescue StandardError => e
454
+ raise e
455
+ end
456
+
457
+ # ---- Report Intents ------------------------------------------------
458
+
459
+ # Supported Method Parameters::
460
+ # intents = PWN::Plugins::HackerOne.get_report_intents(
461
+ # params: 'optional',
462
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
463
+ # )
464
+
465
+ public_class_method def self.get_report_intents(opts = {})
466
+ creds = creds_from(opts)
467
+ h1_rest_call(
468
+ rest_call: 'report_intents',
469
+ params: opts[:params],
470
+ username: creds[:username],
471
+ api_key: creds[:api_key]
472
+ )
473
+ rescue StandardError => e
474
+ raise e
475
+ end
476
+
477
+ # Supported Method Parameters::
478
+ # intent = PWN::Plugins::HackerOne.get_report_intent(
479
+ # id: 'required - report intent id',
480
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
481
+ # )
482
+
483
+ public_class_method def self.get_report_intent(opts = {})
484
+ id = opts[:id]
485
+ raise 'ERROR: report intent id required' if id.nil? || id.to_s.empty?
486
+
487
+ creds = creds_from(opts)
488
+ h1_rest_call(
489
+ rest_call: "report_intents/#{id}",
490
+ params: opts[:params],
491
+ username: creds[:username],
492
+ api_key: creds[:api_key]
493
+ )
494
+ rescue StandardError => e
495
+ raise e
496
+ end
497
+
498
+ # Supported Method Parameters::
499
+ # intent = PWN::Plugins::HackerOne.create_report_intent(
500
+ # body: 'required - full JSON:API body Hash',
501
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
502
+ # )
503
+
504
+ public_class_method def self.create_report_intent(opts = {})
505
+ body = opts[:body]
506
+ raise 'ERROR: body required for create_report_intent' if body.nil?
507
+
508
+ creds = creds_from(opts)
509
+ h1_rest_call(
510
+ http_method: :post,
511
+ rest_call: 'report_intents',
512
+ http_body: body,
513
+ username: creds[:username],
514
+ api_key: creds[:api_key]
515
+ )
516
+ rescue StandardError => e
517
+ raise e
518
+ end
519
+
520
+ # Supported Method Parameters::
521
+ # intent = PWN::Plugins::HackerOne.update_report_intent(
522
+ # id: 'required - report intent id',
523
+ # body: 'required - full JSON:API body Hash',
524
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
525
+ # )
526
+
527
+ public_class_method def self.update_report_intent(opts = {})
528
+ id = opts[:id]
529
+ body = opts[:body]
530
+ raise 'ERROR: report intent id required' if id.nil? || id.to_s.empty?
531
+ raise 'ERROR: body required for update_report_intent' if body.nil?
532
+
533
+ creds = creds_from(opts)
534
+ h1_rest_call(
535
+ http_method: :put,
536
+ rest_call: "report_intents/#{id}",
537
+ http_body: body,
538
+ username: creds[:username],
539
+ api_key: creds[:api_key]
540
+ )
541
+ rescue StandardError => e
542
+ raise e
543
+ end
544
+
545
+ # Supported Method Parameters::
546
+ # result = PWN::Plugins::HackerOne.delete_report_intent(
547
+ # id: 'required - report intent id',
548
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
549
+ # )
550
+
551
+ public_class_method def self.delete_report_intent(opts = {})
552
+ id = opts[:id]
553
+ raise 'ERROR: report intent id required' if id.nil? || id.to_s.empty?
554
+
555
+ creds = creds_from(opts)
556
+ h1_rest_call(
557
+ http_method: :delete,
558
+ rest_call: "report_intents/#{id}",
559
+ username: creds[:username],
560
+ api_key: creds[:api_key]
561
+ )
562
+ rescue StandardError => e
563
+ raise e
564
+ end
565
+
566
+ # Supported Method Parameters::
567
+ # result = PWN::Plugins::HackerOne.submit_report_intent(
568
+ # id: 'required - report intent id',
569
+ # body: 'optional - JSON:API body Hash',
570
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
571
+ # )
572
+
573
+ public_class_method def self.submit_report_intent(opts = {})
574
+ id = opts[:id]
575
+ raise 'ERROR: report intent id required' if id.nil? || id.to_s.empty?
576
+
577
+ creds = creds_from(opts)
578
+ h1_rest_call(
579
+ http_method: :post,
580
+ rest_call: "report_intents/#{id}/submit",
581
+ http_body: opts[:body],
582
+ username: creds[:username],
583
+ api_key: creds[:api_key]
584
+ )
585
+ rescue StandardError => e
586
+ raise e
587
+ end
588
+
589
+ # Supported Method Parameters::
590
+ # attachments = PWN::Plugins::HackerOne.get_report_intent_attachments(
591
+ # report_intent_id: 'required - report intent id',
592
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
593
+ # )
594
+
595
+ public_class_method def self.get_report_intent_attachments(opts = {})
596
+ rid = opts[:report_intent_id] || opts[:id]
597
+ raise 'ERROR: report_intent_id required' if rid.nil? || rid.to_s.empty?
598
+
599
+ creds = creds_from(opts)
600
+ h1_rest_call(
601
+ rest_call: "report_intents/#{rid}/attachments",
602
+ params: opts[:params],
603
+ username: creds[:username],
604
+ api_key: creds[:api_key]
605
+ )
606
+ rescue StandardError => e
607
+ raise e
608
+ end
609
+
610
+ # Supported Method Parameters::
611
+ # result = PWN::Plugins::HackerOne.delete_report_intent_attachment(
612
+ # report_intent_id: 'required - report intent id',
613
+ # id: 'required - attachment id',
614
+ # username: 'optional', api_key: 'optional', h1_obj: 'optional'
615
+ # )
616
+
617
+ public_class_method def self.delete_report_intent_attachment(opts = {})
618
+ rid = opts[:report_intent_id]
619
+ id = opts[:id]
620
+ raise 'ERROR: report_intent_id required' if rid.nil? || rid.to_s.empty?
621
+ raise 'ERROR: attachment id required' if id.nil? || id.to_s.empty?
622
+
623
+ creds = creds_from(opts)
624
+ h1_rest_call(
625
+ http_method: :delete,
626
+ rest_call: "report_intents/#{rid}/attachments/#{id}",
627
+ username: creds[:username],
628
+ api_key: creds[:api_key]
629
+ )
111
630
  rescue StandardError => e
112
631
  raise e
113
632
  end
@@ -116,11 +635,13 @@ module PWN
116
635
  # PWN::Plugins::HackerOne.logout(
117
636
  # h1_obj: 'required h1_obj returned from #login method'
118
637
  # )
638
+ # Clears the session hash in-place (no server-side session to destroy).
119
639
 
120
640
  public_class_method def self.logout(opts = {})
121
641
  h1_obj = opts[:h1_obj]
122
- @@logger.info('Logging out...')
123
- h1_obj = nil
642
+ @@logger.info('Clearing HackerOne session object...')
643
+ h1_obj.clear if h1_obj.respond_to?(:clear)
644
+ nil
124
645
  rescue StandardError => e
125
646
  raise e
126
647
  end
@@ -138,14 +659,46 @@ module PWN
138
659
  public_class_method def self.help
139
660
  puts "USAGE:
140
661
  h1_obj = #{self}.login(
141
- username: 'required username',
142
- token: 'optional api token (will prompt if nil)'
662
+ username: 'optional - default PWN::Env[:plugins][:hackerone][:username]',
663
+ api_key: 'optional - default PWN::Env[:plugins][:hackerone][:api_key]'
143
664
  )
144
665
 
145
- h1_obj = #{self}.logout(
146
- h1_obj: 'required h1_obj returned from #login method'
666
+ reports = #{self}.get_me_reports(h1_obj: h1_obj)
667
+ report = #{self}.get_report(id: 12345)
668
+ report = #{self}.create_report(
669
+ team_handle: 'security',
670
+ title: 'Example',
671
+ vulnerability_information: 'Details...'
672
+ )
673
+
674
+ balance = #{self}.get_balance
675
+ earnings = #{self}.get_earnings
676
+ payouts = #{self}.get_payouts
677
+
678
+ programs = #{self}.get_programs
679
+ program = #{self}.get_program(handle: 'security')
680
+ scopes = #{self}.get_structured_scopes(handle: 'security')
681
+ excl = #{self}.get_scope_exclusions(handle: 'security')
682
+ weak = #{self}.get_weaknesses(handle: 'security')
683
+
684
+ items = #{self}.get_hacktivity
685
+
686
+ intents = #{self}.get_report_intents
687
+ intent = #{self}.get_report_intent(id: 1)
688
+ intent = #{self}.create_report_intent(body: { data: { type: 'report-intent', attributes: {} } })
689
+ intent = #{self}.update_report_intent(id: 1, body: { data: { type: 'report-intent', attributes: {} } })
690
+ #{self}.submit_report_intent(id: 1)
691
+ #{self}.delete_report_intent(id: 1)
692
+ atts = #{self}.get_report_intent_attachments(report_intent_id: 1)
693
+ #{self}.delete_report_intent_attachment(report_intent_id: 1, id: 2)
694
+
695
+ json = #{self}.api(
696
+ path: 'me/reports',
697
+ method: :get,
698
+ params: { page: { number: 1 } }
147
699
  )
148
700
 
701
+ #{self}.logout(h1_obj: h1_obj)
149
702
  #{self}.authors
150
703
  "
151
704
  end
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.652'
4
+ VERSION = '0.5.656'
5
5
  end
@@ -16,6 +16,7 @@ pwn_provider=`echo $PWN_PROVIDER`
16
16
  $screen_cmd "${apt} install -y apache2 ${assess_update_errors}"
17
17
  $screen_cmd "a2enmod proxy ${assess_update_errors}"
18
18
  $screen_cmd "a2enmod proxy_http ${assess_update_errors}"
19
+ $screen_cmd "a2enmod proxy_wstunnel ${assess_update_errors}"
19
20
  $screen_cmd "a2enmod rewrite ${assess_update_errors}"
20
21
  $screen_cmd "a2enmod ssl ${assess_update_errors}"
21
22
  $screen_cmd "a2enmod headers ${assess_update_errors}"
@@ -1502,5 +1502,35 @@ RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
1502
1502
  expect(doc).to match(/Last-iter force-final/)
1503
1503
  end
1504
1504
  end
1505
+
1506
+ describe 'P28 · autonomy (remote overconf runway + incomplete-final)' do
1507
+ it 'sets W3 overconf max_iters_cap to 40 on remote, 8 on ollama' do
1508
+ src = File.read(loop_mod.method(:run).source_location.first)
1509
+ expect(src).to match(/P28/)
1510
+ expect(src).to match(/remote_cap = 40/)
1511
+ expect(src).to match(/local_cap\s*=\s*8/)
1512
+ expect(src).to match(/eng == :ollama \? local_cap : remote_cap/)
1513
+ # budget-hot still always 8 (P17)
1514
+ expect(src).to match(/n = \[n, 8\]\.min/)
1515
+ end
1516
+
1517
+ it 'defines incomplete_final? and continues on mid-goal handoff' do
1518
+ src = File.read(loop_mod.method(:run).source_location.first)
1519
+ expect(src).to match(/incomplete_final\?/)
1520
+ expect(src).to match(/INCOMPLETE_FINAL_RX/)
1521
+ expect(src).to match(%r{\[pwn-ai/p28\]})
1522
+ expect(src).to match(/continuing autonomously/)
1523
+ # last-iter wording no longer coaches "next single step" handoffs
1524
+ expect(src).not_to match(/next single step/)
1525
+ expect(src).to match(/do NOT ask the/)
1526
+ end
1527
+
1528
+ it 'PromptBuilder injects AUTONOMY block' do
1529
+ src = File.read(File.expand_path('../../lib/pwn/ai/agent/prompt_builder.rb', __dir__))
1530
+ expect(src).to match(/AUTONOMY/)
1531
+ expect(src).to match(/Do NOT stop to/)
1532
+ expect(src).to match(/Multi-step goals must be finished in one Loop\.run/)
1533
+ end
1534
+ end
1505
1535
  end
1506
1536
  # rubocop:enable Metrics/BlockLength
@@ -301,6 +301,7 @@
301
301
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.expose_current_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.expose_current_session`: "}]}
302
302
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.guard_repeated_failure Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.guard_repeated_failure`: "}]}
303
303
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.help`: "}]}
304
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.incomplete_final? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.incomplete_final?`: "}]}
304
305
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.max_iters Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.max_iters`: "}]}
305
306
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.normalize_llm Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.normalize_llm`: Supported Method Parameters\n\nmsg = PWN::AI::Agent::Loop.normalize_llm(\n\nresponse: 'required - chat_with_tools response Hash from any provider'\n\n)\n"}]}
306
307
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.plan_first Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.plan_first`: "}]}
@@ -1422,11 +1423,35 @@
1422
1423
  {"messages":[{"role":"user","content":"PWN::Plugins::Github.resolve_username Usage"},{"role":"assistant","content":"`PWN::Plugins::Github.resolve_username`: "}]}
1423
1424
  {"messages":[{"role":"user","content":"PWN::Plugins::Github.workflow_run_jobs Usage"},{"role":"assistant","content":"`PWN::Plugins::Github.workflow_run_jobs`: Supported Method Parameters\n\njobs = PWN::Plugins::Github.workflow_run_jobs(\n\nowner: 'required - repo owner (default PWN::Env[:plugins][:github][:username])',\nrepo: 'required - repo name',\nrun_id: 'required - workflow run id'\n\n)\n"}]}
1424
1425
  {"messages":[{"role":"user","content":"PWN::Plugins::Github.workflow_runs Usage"},{"role":"assistant","content":"`PWN::Plugins::Github.workflow_runs`: Supported Method Parameters\n\nruns = PWN::Plugins::Github.workflow_runs(\n\nowner: 'required - repo owner (default PWN::Env[:plugins][:github][:username])',\nrepo: 'required - repo name',\nworkflow: 'optional - workflow file name or id (e.g. install-matrix.yml). Omit for all runs.',\nparams: 'optional - {branch:, status:, per_page:, page:}'\n\n)\n"}]}
1426
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.api Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.api`: Supported Method Parameters\n\nresponse = PWN::Plugins::HackerOne.api(\n\npath: 'required - path relative to /v1/hackers (e.g. \"me/reports\")',\nmethod: 'optional - :get|:post|:put|:patch|:delete (default :get)',\nparams: 'optional - query params Hash',\nbody: 'optional - request body Hash/Array/String',\nusername: 'optional',\napi_key: 'optional',\nh1_obj: 'optional - session from #login (supplies username/api_key)',\nraw: 'optional - return raw response (default false)'\n\n)\n"}]}
1425
1427
  {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
1426
- {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.h1_rest_call Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.h1_rest_call`: Supported Method Parameters\n\nh1_rest_call(\n\nh1_obj: 'required h1_obj returned from #login method',\nhttp_method: 'optional HTTP method (defaults to GET)\nrest_call: 'required rest call to make per the schema',\nhttp_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST'\n\n)\n"}]}
1428
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.create_report Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.create_report`: Supported Method Parameters\n\nreport = PWN::Plugins::HackerOne.create_report(\n\nteam_handle: 'required - program handle',\ntitle: 'required - report title',\nvulnerability_information: 'required - detailed description',\nimpact: 'optional - impact text',\nseverity_rating: 'optional - none|low|medium|high|critical',\nweakness_id: 'optional - weakness id integer',\nstructured_scope_id: 'optional - structured scope id',\nbody: 'optional - full JSON:API body Hash (overrides attribute kwargs)',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1429
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.create_report_intent Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.create_report_intent`: Supported Method Parameters\n\nintent = PWN::Plugins::HackerOne.create_report_intent(\n\nbody: 'required - full JSON:API body Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1430
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.creds_from Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.creds_from`: "}]}
1431
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.delete_report_intent Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.delete_report_intent`: Supported Method Parameters\n\nresult = PWN::Plugins::HackerOne.delete_report_intent(\n\nid: 'required - report intent id',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1432
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.delete_report_intent_attachment Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.delete_report_intent_attachment`: Supported Method Parameters\n\nresult = PWN::Plugins::HackerOne.delete_report_intent_attachment(\n\nreport_intent_id: 'required - report intent id',\nid: 'required - attachment id',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1433
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_balance Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_balance`: Supported Method Parameters\n\nbalance = PWN::Plugins::HackerOne.get_balance(\n\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1434
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_earnings Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_earnings`: Supported Method Parameters\n\nearnings = PWN::Plugins::HackerOne.get_earnings(\n\nparams: 'optional - query params Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1435
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_hacktivity Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_hacktivity`: Supported Method Parameters\n\nitems = PWN::Plugins::HackerOne.get_hacktivity(\n\nparams: 'optional - query params Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1436
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_me_reports Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_me_reports`: Supported Method Parameters\n\nreports = PWN::Plugins::HackerOne.get_me_reports(\n\nparams: 'optional - query params Hash (e.g. page/filter)',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1437
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_payouts Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_payouts`: Supported Method Parameters\n\npayouts = PWN::Plugins::HackerOne.get_payouts(\n\nparams: 'optional - query params Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1438
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_program Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_program`: Supported Method Parameters\n\nprogram = PWN::Plugins::HackerOne.get_program(\n\nhandle: 'required - program handle',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1439
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_programs Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_programs`: Supported Method Parameters\n\nprograms = PWN::Plugins::HackerOne.get_programs(\n\nparams: 'optional - query params Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1440
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_report Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_report`: Supported Method Parameters\n\nreport = PWN::Plugins::HackerOne.get_report(\n\nid: 'required - report id',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1441
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_report_intent Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_report_intent`: Supported Method Parameters\n\nintent = PWN::Plugins::HackerOne.get_report_intent(\n\nid: 'required - report intent id',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1442
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_report_intent_attachments Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_report_intent_attachments`: Supported Method Parameters\n\nattachments = PWN::Plugins::HackerOne.get_report_intent_attachments(\n\nreport_intent_id: 'required - report intent id',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1443
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_report_intents Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_report_intents`: Supported Method Parameters\n\nintents = PWN::Plugins::HackerOne.get_report_intents(\n\nparams: 'optional',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1444
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_scope_exclusions Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_scope_exclusions`: Supported Method Parameters\n\nexclusions = PWN::Plugins::HackerOne.get_scope_exclusions(\n\nhandle: 'required - program handle',\nparams: 'optional',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1445
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_structured_scopes Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_structured_scopes`: Supported Method Parameters\n\nscopes = PWN::Plugins::HackerOne.get_structured_scopes(\n\nhandle: 'required - program handle',\nparams: 'optional',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1446
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.get_weaknesses Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.get_weaknesses`: Supported Method Parameters\n\nweaknesses = PWN::Plugins::HackerOne.get_weaknesses(\n\nhandle: 'required - program handle',\nparams: 'optional',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1447
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.h1_rest_call Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.h1_rest_call`: Supported Method Parameters\n\nh1_rest_call(\n\nhttp_method: 'optional HTTP method (defaults to GET)',\nrest_call: 'required rest path relative to /v1/hackers (e.g. \"me/reports\")',\nparams: 'optional query params Hash',\nhttp_body: 'optional HTTP body (Hash/Array auto-JSONified; String passed through)',\nusername: 'optional - HackerOne username (default PWN::Env)',\napi_key: 'optional - HackerOne API token (default PWN::Env)',\nraw: 'optional - return raw RestClient::Response (default false)'\n\n)\n"}]}
1427
1448
  {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.help Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.help`: "}]}
1428
- {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.login Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.login`: Supported Method Parameters\n\nh1_obj = PWN::Plugins::HackerOne.login(\n\nusername: 'required - username',\ntoken: 'optional - api token (will prompt if nil)'\n\n)\n"}]}
1429
- {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.logout Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.logout`: Supported Method Parameters\n\nPWN::Plugins::HackerOne.logout(\n\nh1_obj: 'required h1_obj returned from #login method'\n\n)\n"}]}
1449
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.login Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.login`: Supported Method Parameters\n\nh1_obj = PWN::Plugins::HackerOne.login(\n\nusername: 'optional - username (default PWN::Env[:plugins][:hackerone][:username])',\napi_key: 'optional - api token (default PWN::Env; will prompt if still nil)',\ntoken: 'optional - alias for api_key'\n\n) Backward-compatible session object. Auth is HTTP Basic on every subsequent call; login simply resolves + validates credentials against me/reports.\n"}]}
1450
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.logout Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.logout`: Supported Method Parameters\n\nPWN::Plugins::HackerOne.logout(\n\nh1_obj: 'required h1_obj returned from #login method'\n\n) Clears the session hash in-place (no server-side session to destroy).\n"}]}
1451
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.resolve_api_key Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.resolve_api_key`: "}]}
1452
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.resolve_username Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.resolve_username`: "}]}
1453
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.submit_report_intent Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.submit_report_intent`: Supported Method Parameters\n\nresult = PWN::Plugins::HackerOne.submit_report_intent(\n\nid: 'required - report intent id',\nbody: 'optional - JSON:API body Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1454
+ {"messages":[{"role":"user","content":"PWN::Plugins::HackerOne.update_report_intent Usage"},{"role":"assistant","content":"`PWN::Plugins::HackerOne.update_report_intent`: Supported Method Parameters\n\nintent = PWN::Plugins::HackerOne.update_report_intent(\n\nid: 'required - report intent id',\nbody: 'required - full JSON:API body Hash',\nusername: 'optional', api_key: 'optional', h1_obj: 'optional'\n\n)\n"}]}
1430
1455
  {"messages":[{"role":"user","content":"PWN::Plugins::Hunter.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::Hunter.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
1431
1456
  {"messages":[{"role":"user","content":"PWN::Plugins::Hunter.help Usage"},{"role":"assistant","content":"`PWN::Plugins::Hunter.help`: "}]}
1432
1457
  {"messages":[{"role":"user","content":"PWN::Plugins::Hunter.hunter_rest_call Usage"},{"role":"assistant","content":"`PWN::Plugins::Hunter.hunter_rest_call`: Supported Method Parameters\n\nhunter_rest_call(\n\nhttp_method: 'optional HTTP method (defaults to GET)\nrest_call: 'required rest call to make per the schema',\nparams: 'optional params passed in the URI or HTTP Headers',\nhttp_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST'\n\n)\n"}]}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.652
4
+ version: 0.5.656
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "<"
17
17
  - !ruby/object:Gem::Version
18
- version: 8.1.3
18
+ version: 8.1.3.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "<"
24
24
  - !ruby/object:Gem::Version
25
- version: 8.1.3
25
+ version: 8.1.3.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: anemone
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -463,14 +463,14 @@ dependencies:
463
463
  requirements:
464
464
  - - '='
465
465
  - !ruby/object:Gem::Version
466
- version: 0.7.2
466
+ version: 0.8.0
467
467
  type: :development
468
468
  prerelease: false
469
469
  version_requirements: !ruby/object:Gem::Requirement
470
470
  requirements:
471
471
  - - '='
472
472
  - !ruby/object:Gem::Version
473
- version: 0.7.2
473
+ version: 0.8.0
474
474
  - !ruby/object:Gem::Dependency
475
475
  name: luhn
476
476
  requirement: !ruby/object:Gem::Requirement
@@ -505,28 +505,28 @@ dependencies:
505
505
  requirements:
506
506
  - - '='
507
507
  - !ruby/object:Gem::Version
508
- version: 1.0.0
508
+ version: 1.1.0
509
509
  type: :runtime
510
510
  prerelease: false
511
511
  version_requirements: !ruby/object:Gem::Requirement
512
512
  requirements:
513
513
  - - '='
514
514
  - !ruby/object:Gem::Version
515
- version: 1.0.0
515
+ version: 1.1.0
516
516
  - !ruby/object:Gem::Dependency
517
517
  name: meshtastic
518
518
  requirement: !ruby/object:Gem::Requirement
519
519
  requirements:
520
520
  - - '='
521
521
  - !ruby/object:Gem::Version
522
- version: 0.0.172
522
+ version: 0.0.173
523
523
  type: :development
524
524
  prerelease: false
525
525
  version_requirements: !ruby/object:Gem::Requirement
526
526
  requirements:
527
527
  - - '='
528
528
  - !ruby/object:Gem::Version
529
- version: 0.0.172
529
+ version: 0.0.173
530
530
  - !ruby/object:Gem::Dependency
531
531
  name: metasm
532
532
  requirement: !ruby/object:Gem::Requirement
@@ -897,14 +897,14 @@ dependencies:
897
897
  requirements:
898
898
  - - '='
899
899
  - !ruby/object:Gem::Version
900
- version: 7.0.5
900
+ version: 7.1.0
901
901
  type: :development
902
902
  prerelease: false
903
903
  version_requirements: !ruby/object:Gem::Requirement
904
904
  requirements:
905
905
  - - '='
906
906
  - !ruby/object:Gem::Version
907
- version: 7.0.5
907
+ version: 7.1.0
908
908
  - !ruby/object:Gem::Dependency
909
909
  name: rqrcode
910
910
  requirement: !ruby/object:Gem::Requirement
@@ -953,14 +953,14 @@ dependencies:
953
953
  requirements:
954
954
  - - '='
955
955
  - !ruby/object:Gem::Version
956
- version: 1.88.2
956
+ version: 1.89.0
957
957
  type: :runtime
958
958
  prerelease: false
959
959
  version_requirements: !ruby/object:Gem::Requirement
960
960
  requirements:
961
961
  - - '='
962
962
  - !ruby/object:Gem::Version
963
- version: 1.88.2
963
+ version: 1.89.0
964
964
  - !ruby/object:Gem::Dependency
965
965
  name: rubocop-rake
966
966
  requirement: !ruby/object:Gem::Requirement