pwn 0.5.692 → 0.5.695

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: f50cbd0dec53be2396642dd847d22880d66618404c3ed282a8b9c6a7043e75d1
4
- data.tar.gz: 39e643030ccaddaf1c2fa8cb19910d0fbb5bcb0637044fb9af72e739542829bf
3
+ metadata.gz: 445532063d09a6c2edd54ac2872c295932bdbdc45d025e87fe109a5e8641ce0f
4
+ data.tar.gz: b8b9aab81ae7c16ffcda16cdea943b85eaf69c93b6de929e2eb9fc288cb8f000
5
5
  SHA512:
6
- metadata.gz: 612374e1e3f402185038a2427cc046a4c23b75878c785e0fc124c5fb6fd12e6f9d3e3b28339f743b31a275f2d948510ccbc36b4660edb35c67ad3406af24df1b
7
- data.tar.gz: d1f31dcd701b7a9bd7c825d8cf91945a3f9ef57fb98e48a5dbe31d56a3762f2c1e8a635c3972337786acb84a8de7b8acc0760e27b788994ce3cb789679baa785
6
+ metadata.gz: 85071aaac40e5ed6348a52a7858be4dd1b781db8b706324b054a67e254c365bab4d10d4aba182ee42a387e15574b8f6b7fc67d2b31b1559dd17b5d7d6dfccae5
7
+ data.tar.gz: a677194de81bb47090ba62cebac6f293d68fc4e4876693faae868cfe96434478610fece0478e93eb980af4d688819ea1310cb6048bb7096b27a0bdbe516bf1da
data/Gemfile CHANGED
@@ -25,7 +25,7 @@ gem 'bundler-audit', '>=0.9.3'
25
25
  gem 'bunny', '3.2.0'
26
26
  gem 'colorize', '1.1.0'
27
27
  gem 'credit_card_validations', '9.0.0'
28
- gem 'curses', '1.6.0'
28
+ gem 'curses', '1.7.0'
29
29
  gem 'diffy', '3.4.4'
30
30
  gem 'eventmachine', '1.2.7'
31
31
  gem 'executable-hooks', '1.7.1'
@@ -50,7 +50,7 @@ gem 'libusb', '0.8.0'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
52
  gem 'mcp', '1.3.0'
53
- gem 'meshtastic', '0.0.174'
53
+ gem 'meshtastic', '0.0.175'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.25.0'
56
56
  gem 'msfrpc-client', '1.1.2'
@@ -81,7 +81,7 @@ gem 'rmagick', '7.1.1'
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.89.0'
84
+ gem 'rubocop', '1.90.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'
@@ -12,5 +12,5 @@
12
12
  - https://platform.claude.com/v1/oauth/token
13
13
  - https://platform.claude.com/oauth/code/callback
14
14
  - https://generativelanguage.googleapis.com/v1beta
15
- - http://127.0.0.1:1/
15
+ - http://127.0.0.1:ephemeral/
16
16
  - https://jira.company.com/rest/api/latest
@@ -59,7 +59,6 @@ Enable in Google Cloud: Gmail, Calendar, Drive, Docs, Sheets APIs.
59
59
  gw = PWN::Plugins::GoogleWorkspace
60
60
  gw.authenticated?
61
61
  gw.obtain_oauth_auth_url(services: 'all')
62
- gw.exchange_oauth_code(code: 'http://127.0.0.1:1/?code=...')
63
62
  gw.gmail_search(query: 'is:unread', max: 10)
64
63
  ```
65
64
 
@@ -70,10 +69,14 @@ then `pwn_eval`.
70
69
 
71
70
  1. Store the Desktop OAuth client fields in the oauth hash (pwn-vault).
72
71
  2. `obtain_oauth_auth_url(services: 'email,calendar')` — or `all`.
73
- 3. Browser fail on `http://127.0.0.1:1/` is expected. Paste the redirect.
74
- 4. `exchange_oauth_code(code: pasted)`. Tokens persist via pwn-vault.
72
+ 3. Authorize the printed URL. A loopback listener captures the redirect.
73
+ 4. Access and refresh tokens are written immediately into
74
+ `PWN::Env[:plugins][:google_workspace][:oauth]` and encrypted
75
+ `~/.pwn/pwn.yaml` (same persist path as `PWN::AI::Grok`).
75
76
  5. Later calls refresh automatically. `revoke` clears tokens.
76
77
 
78
+ `exchange_oauth_code` remains a paste fallback (`wait: false` then paste).
79
+
77
80
  ## Quick reference
78
81
 
79
82
  ```ruby
@@ -133,7 +136,7 @@ gw.sheets_append(id: 'SHEET_ID', range: 'Sheet1!A:C', values: [['a', 'b', 'c']])
133
136
 
134
137
  ## Pitfalls
135
138
 
136
- - Browser fail on `http://127.0.0.1:1/` is the paste step.
139
+ - Browser fail is not required. `obtain_oauth_auth_url` waits and persists.
137
140
  - Placeholder vault strings are not credentials.
138
141
  - A brief request is not a send.
139
142
 
@@ -152,8 +152,8 @@ module PWN
152
152
  {
153
153
  memory: (b[:memory] || (local ? 6 : 25)).to_i,
154
154
  metrics: (b[:metrics] || (local ? 3 : 8)).to_i,
155
- mistakes: (b[:mistakes] || (local ? 3 : 6)).to_i,
156
- learning: (b[:learning] || (local ? 2 : 5)).to_i,
155
+ mistakes: (b[:mistakes] || (local ? 3 : 6)).to_i,
156
+ learning: (b[:learning] || (local ? 2 : 5)).to_i,
157
157
  # Always inject prior user/assistant pairs from this session.
158
158
  recent_turns: (b[:recent_turns] || (local ? 3 : 6)).to_i,
159
159
  policy: (b[:policy] || 1).to_i,
data/lib/pwn/config.rb CHANGED
@@ -215,7 +215,7 @@ module PWN
215
215
  refresh_token: 'optional - durable Google refresh token (enables silent re-auth)',
216
216
  bearer_token: 'optional - Google access token (short-lived; auto-refreshed)',
217
217
  expires_at: 'optional - unix epoch seconds when bearer_token expires',
218
- redirect_uri: 'optional - defaults to http://127.0.0.1:1/',
218
+ redirect_uri: 'optional - loopback listener (default http://127.0.0.1:ephemeral/)',
219
219
  scope: 'optional - override space-delimited Google scopes',
220
220
  services: 'optional - email,calendar,drive,docs,sheets or all'
221
221
  }
@@ -7,6 +7,8 @@ require 'fileutils'
7
7
  require 'json'
8
8
  require 'rest-client'
9
9
  require 'securerandom'
10
+ require 'socket'
11
+ require 'timeout'
10
12
  require 'time'
11
13
  require 'tmpdir'
12
14
  require 'uri'
@@ -21,9 +23,10 @@ module PWN
21
23
  # Credentials live in PWN::Env[:plugins][:google_workspace][:oauth]
22
24
  # (seeded by PWN::Config / pwn-vault). Desktop-app client_id +
23
25
  # client_secret come from Google Cloud Console. After
24
- # obtain_oauth_auth_url + exchange_oauth_code, refresh_token is
25
- # persisted into the encrypted ~/.pwn/pwn.yaml when a decryptor is
26
- # present.
26
+ # obtain_oauth_auth_url. When wait is true (default), a loopback
27
+ # listener captures the redirect as soon as the operator authorizes
28
+ # the URL; refresh/bearer are written into PWN::Env and the encrypted
29
+ # ~/.pwn/pwn.yaml the same way PWN::AI::Grok persists OAuth tokens.
27
30
  module GoogleWorkspace
28
31
  AUTH_URI = 'https://accounts.google.com/o/oauth2/v2/auth'
29
32
  TOKEN_URI = 'https://oauth2.googleapis.com/token'
@@ -128,10 +131,18 @@ module PWN
128
131
  client_id = c[:client_id]
129
132
  raise 'client_id is required (Google Cloud Desktop OAuth client)' unless real_config_value?(value: client_id)
130
133
 
134
+ wait = opts.key?(:wait) ? opts[:wait] : true
135
+ timeout_s = (opts[:timeout] || 300).to_i
131
136
  redirect = real_config_value?(value: c[:redirect_uri]) ? c[:redirect_uri] : REDIRECT_URI
132
137
  wanted = scopes(opts.merge(services: c[:services], scope: c[:scope]))
133
138
  raise 'no Google scopes selected' if wanted.empty?
134
139
 
140
+ server = nil
141
+ if wait && !opts[:probe].respond_to?(:call)
142
+ server = open_oauth_listener(redirect_uri: redirect)
143
+ redirect = listener_redirect(server: server, fallback: redirect)
144
+ end
145
+
135
146
  pkce = pkce_pair
136
147
  save_pending!(verifier: pkce[:verifier], redirect_uri: redirect, client_id: client_id)
137
148
 
@@ -147,7 +158,96 @@ module PWN
147
158
  code_challenge_method: 'S256'
148
159
  }
149
160
  url = "#{AUTH_URI}?#{URI.encode_www_form(params)}"
150
- { auth_url: url, scope: wanted, redirect_uri: redirect }
161
+ return { auth_url: url, scope: wanted, redirect_uri: redirect } unless wait
162
+
163
+ puts "\n[*] Google Workspace OAuth — authorize this URL, then tokens persist automatically:"
164
+ puts " #{url}"
165
+ puts " Waiting for authorization on #{redirect} (timeout #{timeout_s}s)..."
166
+ code = await_oauth_redirect(
167
+ redirect_uri: redirect,
168
+ timeout: timeout_s,
169
+ probe: opts[:probe],
170
+ server: server
171
+ )
172
+ exchange_oauth_code(
173
+ opts.merge(
174
+ code: code,
175
+ redirect_uri: redirect,
176
+ code_verifier: pkce[:verifier],
177
+ client_id: client_id,
178
+ client_secret: c[:client_secret]
179
+ )
180
+ )
181
+ ensure
182
+ begin
183
+ server&.close
184
+ rescue StandardError
185
+ nil
186
+ end
187
+ end
188
+
189
+ private_class_method def self.open_oauth_listener(opts = {})
190
+ redirect = opts[:redirect_uri].to_s
191
+ uri = URI.parse(redirect.empty? ? REDIRECT_URI : redirect)
192
+ host = uri.host.to_s
193
+ host = '127.0.0.1' if host.empty?
194
+ port = uri.port.to_i
195
+ port = 0 if port <= 1
196
+ TCPServer.new(host, port)
197
+ end
198
+
199
+ private_class_method def self.listener_redirect(opts = {})
200
+ server = opts[:server]
201
+ fallback = opts[:fallback].to_s
202
+ return fallback unless server
203
+
204
+ addr = server.addr
205
+ host = addr[3].to_s
206
+ host = '127.0.0.1' if host.empty? || host == '0.0.0.0'
207
+ "http://#{host}:#{addr[1]}/"
208
+ end
209
+
210
+ private_class_method def self.await_oauth_redirect(opts = {})
211
+ redirect = opts[:redirect_uri].to_s
212
+ timeout_s = (opts[:timeout] || 300).to_i
213
+ probe = opts[:probe]
214
+ return probe.call(redirect) if probe.respond_to?(:call)
215
+
216
+ server = opts[:server]
217
+ owned = false
218
+ unless server
219
+ server = open_oauth_listener(redirect_uri: redirect)
220
+ owned = true
221
+ end
222
+ code = nil
223
+ Timeout.timeout(timeout_s) do
224
+ client = server.accept
225
+ request_line = client.gets.to_s
226
+ loop do
227
+ line = client.gets
228
+ break if line.nil? || line.strip.empty?
229
+ end
230
+ path = request_line.split[1].to_s
231
+ path = '/' if path.empty?
232
+ uri = URI.parse(path.start_with?('http') ? path : "http://127.0.0.1#{path}")
233
+ code = URI.decode_www_form(uri.query.to_s).to_h['code'].to_s
234
+ body = "PWN Google Workspace authorized. You can close this tab.\n"
235
+ client.print "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: #{body.bytesize}\r\nConnection: close\r\n\r\n#{body}"
236
+ client.close
237
+ end
238
+ raise 'Google OAuth redirect contained no code' if code.to_s.empty?
239
+
240
+ code
241
+ rescue Timeout::Error
242
+ raise 'Google OAuth timed out waiting for authorization'
243
+ ensure
244
+ if owned
245
+ begin
246
+ server&.close
247
+ rescue StandardError
248
+ nil
249
+ end
250
+ end
151
251
  end
152
252
 
153
253
  public_class_method def self.exchange_oauth_code(opts = {})
@@ -239,7 +339,7 @@ module PWN
239
339
  exp = c[:expires_at].to_i
240
340
  stale = !real_config_value?(value: token) || (exp.positive? && Time.now.to_i >= (exp - 120))
241
341
  return refresh_oauth_bearer_token(opts.merge(refresh_token: c[:refresh_token])) if stale && real_config_value?(value: c[:refresh_token])
242
- raise 'not authenticated — run obtain_oauth_auth_url then exchange_oauth_code' unless real_config_value?(value: token)
342
+ raise 'not authenticated — run obtain_oauth_auth_url (tokens persist on authorize)' unless real_config_value?(value: token)
243
343
 
244
344
  token
245
345
  end
@@ -779,8 +879,10 @@ module PWN
779
879
  puts <<~USAGE
780
880
  USAGE:
781
881
  # OAuth (Desktop client from Google Cloud Console)
882
+ # authorize the printed URL; refresh/bearer persist to Env + pwn.yaml
782
883
  #{self}.obtain_oauth_auth_url(services: 'email,calendar')
783
- #{self}.exchange_oauth_code(code: 'http://127.0.0.1:1/?code=...')
884
+ #{self}.obtain_oauth_auth_url(wait: false) # URL only; no listener
885
+ #{self}.exchange_oauth_code(code: 'optional paste fallback')
784
886
  #{self}.authenticated?
785
887
  #{self}.revoke
786
888
 
@@ -41,7 +41,7 @@ module PWN
41
41
 
42
42
  # Streaming FLEX demodulator fed by Base.run_native / Base.run_iq.
43
43
  # Per-sample PLL symbol clock, 4-level slicer, full state machine.
44
- # rubocop:disable Metrics/ClassLength
44
+ # rubocop:disable-next Metrics/ClassLength
45
45
  class Demod
46
46
  def initialize(rate: 48_000)
47
47
  @rate = rate.to_f
@@ -222,7 +222,6 @@ module PWN
222
222
  end
223
223
  end
224
224
  end
225
- # rubocop:enable Metrics/ClassLength
226
225
 
227
226
  # Supported Method Parameters::
228
227
  # code, polarity = PWN::SDR::Decoder::Flex.sync_check(buf: Integer)
@@ -488,11 +488,11 @@ module PWN
488
488
 
489
489
  # UHF paging bands (hospital, on-site, restaurant/coaster pagers, some public safety)
490
490
  # UHF low — occasional private systems
491
- { start_freq: '400.000.000', target_freq: '430.000.000' },
491
+ { start_freq: '400.000.000', target_freq: '430.000.000' },
492
492
  # UHF high — very common for local POCSAG (incl. 450–470 & 467.xxx for on-site)
493
- { start_freq: '440.000.000', target_freq: '470.000.000' },
493
+ { start_freq: '440.000.000', target_freq: '470.000.000' },
494
494
  # Classic UHF paging allocation (454/459 MHz pairs — many reallocated)
495
- { start_freq: '454.000.000', target_freq: '460.000.000' },
495
+ { start_freq: '454.000.000', target_freq: '460.000.000' },
496
496
 
497
497
  # 900 MHz exclusive paging band (mostly FLEX, but some legacy POCSAG remains)
498
498
  # Primary nationwide commercial paging band
@@ -523,11 +523,11 @@ module PWN
523
523
 
524
524
  # UHF paging bands (hospital, on-site, restaurant/coaster pagers, some public safety)
525
525
  # UHF low — occasional private systems
526
- { start_freq: '400.000.000', target_freq: '430.000.000' },
526
+ { start_freq: '400.000.000', target_freq: '430.000.000' },
527
527
  # UHF high — very common for local POCSAG (incl. 450–470 & 467.xxx for on-site)
528
- { start_freq: '440.000.000', target_freq: '470.000.000' },
528
+ { start_freq: '440.000.000', target_freq: '470.000.000' },
529
529
  # Classic UHF paging allocation (454/459 MHz pairs — many reallocated)
530
- { start_freq: '454.000.000', target_freq: '460.000.000' }
530
+ { start_freq: '454.000.000', target_freq: '460.000.000' }
531
531
  ],
532
532
  demodulator_mode: :FM,
533
533
  bandwidth: '12.500',
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.692'
4
+ VERSION = '0.5.695'
5
5
  end
@@ -17,7 +17,7 @@ require 'fileutils'
17
17
  # regression pinpoints exactly which requirement broke.
18
18
  # ─────────────────────────────────────────────────────────────────────────────
19
19
 
20
- # rubocop:disable Metrics/BlockLength
20
+ # rubocop:disable-next Metrics/BlockLength
21
21
  RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
22
22
  let(:reward) { PWN::AI::Agent::Reward }
23
23
  let(:curriculum) { PWN::AI::Agent::Curriculum }
@@ -266,8 +266,8 @@ RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
266
266
  skip 'PWN::MemoryIndex unavailable' unless defined?(PWN::MemoryIndex)
267
267
  now = Time.now.utc.iso8601
268
268
  mem = {
269
- a: { value: 'use nmap -sV for service detection', category: 'lesson', timestamp: now, importance: 0.5 },
270
- b: { value: 'prefer nmap -sV to detect services', category: 'lesson', timestamp: now, importance: 0.5 },
269
+ a: { value: 'use nmap -sV for service detection', category: 'lesson', timestamp: now, importance: 0.5 },
270
+ b: { value: 'prefer nmap -sV to detect services', category: 'lesson', timestamp: now, importance: 0.5 },
271
271
  c: { value: 'nmap -sV is best for service versions', category: 'lesson', timestamp: now, importance: 0.5 },
272
272
  d: { value: 'unrelated: rotate api keys quarterly', category: 'lesson', timestamp: now, importance: 0.5 }
273
273
  }
@@ -1556,4 +1556,3 @@ RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
1556
1556
  end
1557
1557
  end
1558
1558
  end
1559
- # rubocop:enable Metrics/BlockLength
@@ -81,4 +81,85 @@ describe PWN::Plugins::GoogleWorkspace do
81
81
  src = File.read(described_class.method(:bearer_token).source_location.first)
82
82
  expect(src).not_to match(/hermes/i)
83
83
  end
84
+
85
+ it 'writes refresh and bearer into Env and pwn.yaml when oauth_auth_url is authorized' do
86
+ Dir.mktmpdir('pwn-gw-oauth-') do |dir|
87
+ env_path = File.join(dir, 'pwn.yaml')
88
+ dec_path = "#{env_path}.decryptor"
89
+ key = '0' * 32
90
+ iv = '1' * 16
91
+ File.write(env_path, "plugins:\n google_workspace:\n oauth:\n client_id: cid\n client_secret: sec\n")
92
+ File.write(dec_path, YAML.dump(key: key, iv: iv))
93
+ allow(PWN::Plugins::Vault).to receive(:decrypt)
94
+ allow(PWN::Plugins::Vault).to receive(:encrypt)
95
+ allow(RestClient).to receive(:post).and_return(
96
+ instance_double(RestClient::Response, body: {
97
+ access_token: 'live-bearer',
98
+ refresh_token: 'live-refresh',
99
+ expires_in: 3600,
100
+ scope: 'https://www.googleapis.com/auth/gmail.modify'
101
+ }.to_json)
102
+ )
103
+
104
+ PWN::Env[:plugins] ||= {}
105
+ PWN::Env[:plugins][:google_workspace] ||= {}
106
+ PWN::Env[:plugins][:google_workspace][:oauth] ||= {}
107
+ slot = PWN::Env[:plugins][:google_workspace][:oauth]
108
+ prev = slot.dup
109
+ slot[:client_id] = 'cid'
110
+ slot[:client_secret] = 'sec'
111
+ slot[:bearer_token] = nil
112
+ slot[:refresh_token] = nil
113
+ PWN::Env[:driver_opts] ||= {}
114
+ PWN::Env[:driver_opts][:pwn_env_path] = env_path
115
+ PWN::Env[:driver_opts][:pwn_dec_path] = dec_path
116
+
117
+ result = described_class.obtain_oauth_auth_url(
118
+ client_id: 'cid',
119
+ client_secret: 'sec',
120
+ services: 'email',
121
+ wait: true,
122
+ probe: ->(_redirect) { 'AUTHCODE' }
123
+ )
124
+
125
+ expect(result[:bearer_token]).to eq('live-bearer')
126
+ expect(result[:refresh_token]).to eq('live-refresh')
127
+ expect(slot[:bearer_token]).to eq('live-bearer')
128
+ expect(slot[:refresh_token]).to eq('live-refresh')
129
+ vault = YAML.load_file(env_path, symbolize_names: true)
130
+ expect(vault.dig(:plugins, :google_workspace, :oauth, :bearer_token)).to eq('live-bearer')
131
+ expect(vault.dig(:plugins, :google_workspace, :oauth, :refresh_token)).to eq('live-refresh')
132
+ ensure
133
+ if prev
134
+ slot.clear
135
+ slot.merge!(prev)
136
+ end
137
+ PWN::Env[:driver_opts].delete(:pwn_env_path) if PWN::Env[:driver_opts].is_a?(Hash)
138
+ PWN::Env[:driver_opts].delete(:pwn_dec_path) if PWN::Env[:driver_opts].is_a?(Hash)
139
+ end
140
+ end
141
+
142
+ it 'captures a loopback authorize hit without a paste step' do
143
+ require 'net/http'
144
+ server = described_class.send(:open_oauth_listener, redirect_uri: 'http://127.0.0.1:0/')
145
+ redir = described_class.send(:listener_redirect, server: server, fallback: 'http://127.0.0.1/')
146
+ hit = Thread.new do
147
+ sleep 0.05
148
+ Net::HTTP.get(URI("#{redir}?code=FROMBROWSER"))
149
+ end
150
+ code = described_class.send(
151
+ :await_oauth_redirect,
152
+ redirect_uri: redir,
153
+ timeout: 5,
154
+ server: server
155
+ )
156
+ expect(code).to eq('FROMBROWSER')
157
+ expect(hit.value).to include('authorized')
158
+ ensure
159
+ begin
160
+ server&.close
161
+ rescue StandardError
162
+ nil
163
+ end
164
+ end
84
165
  end
@@ -1846,6 +1846,7 @@
1846
1846
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.api Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.api`: "}]}
1847
1847
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.authenticated? Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.authenticated?`: "}]}
1848
1848
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.authors`: "}]}
1849
+ {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.await_oauth_redirect Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.await_oauth_redirect`: "}]}
1849
1850
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.bearer_token Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.bearer_token`: "}]}
1850
1851
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.cal_when Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.cal_when`: "}]}
1851
1852
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.calendar_create Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.calendar_create`: "}]}
@@ -1876,8 +1877,10 @@
1876
1877
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.gmail_search Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.gmail_search`: "}]}
1877
1878
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.gmail_send Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.gmail_send`: "}]}
1878
1879
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.help Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.help`: "}]}
1880
+ {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.listener_redirect Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.listener_redirect`: "}]}
1879
1881
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.load_pending Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.load_pending`: "}]}
1880
1882
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.obtain_oauth_auth_url Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.obtain_oauth_auth_url`: "}]}
1883
+ {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.open_oauth_listener Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.open_oauth_listener`: "}]}
1881
1884
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.parse_oauth_code Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.parse_oauth_code`: "}]}
1882
1885
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.persist_oauth_to_vault Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.persist_oauth_to_vault`: "}]}
1883
1886
  {"messages":[{"role":"user","content":"PWN::Plugins::GoogleWorkspace.pkce_pair Usage"},{"role":"assistant","content":"`PWN::Plugins::GoogleWorkspace.pkce_pair`: "}]}
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.692
4
+ version: 0.5.695
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -211,14 +211,14 @@ dependencies:
211
211
  requirements:
212
212
  - - '='
213
213
  - !ruby/object:Gem::Version
214
- version: 1.6.0
214
+ version: 1.7.0
215
215
  type: :development
216
216
  prerelease: false
217
217
  version_requirements: !ruby/object:Gem::Requirement
218
218
  requirements:
219
219
  - - '='
220
220
  - !ruby/object:Gem::Version
221
- version: 1.6.0
221
+ version: 1.7.0
222
222
  - !ruby/object:Gem::Dependency
223
223
  name: diffy
224
224
  requirement: !ruby/object:Gem::Requirement
@@ -519,14 +519,14 @@ dependencies:
519
519
  requirements:
520
520
  - - '='
521
521
  - !ruby/object:Gem::Version
522
- version: 0.0.174
522
+ version: 0.0.175
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.174
529
+ version: 0.0.175
530
530
  - !ruby/object:Gem::Dependency
531
531
  name: metasm
532
532
  requirement: !ruby/object:Gem::Requirement
@@ -953,14 +953,14 @@ dependencies:
953
953
  requirements:
954
954
  - - '='
955
955
  - !ruby/object:Gem::Version
956
- version: 1.89.0
956
+ version: 1.90.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.89.0
963
+ version: 1.90.0
964
964
  - !ruby/object:Gem::Dependency
965
965
  name: rubocop-rake
966
966
  requirement: !ruby/object:Gem::Requirement