pwn 0.5.710 → 0.5.711

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: bb2089e549bccd1c21a643fd22719f13cb3a7519c1580f9cbd3e425c66f2be91
4
- data.tar.gz: d27e48a928750b279f8315e8a578071bed21403728ae7f8c5474015f75ff557e
3
+ metadata.gz: 2ab0eff0005f523bb5972be43cf49ab4c15ab9eaecda0968c9206621e2997e47
4
+ data.tar.gz: 2a166f6ff6b5620e820de709d5dc1ae356550e23afffbc52de138d3ab6499d7f
5
5
  SHA512:
6
- metadata.gz: 37c74e1e2ab80b6a872f0d533ec402b520bffd70dc1198114574b3bbe90383c9dbebe6ac9b844e79023eed1cdeefc6cb8df4cca964b186cff1e25bc718384b12
7
- data.tar.gz: 703c871173f4a754b9a515b45c4fd6aa4d3e2b1590f397168a53869d7eed5a77243a69e635f3894ce61be401b4c260395b0be7b435d6dc0613067b7b9047345c
6
+ metadata.gz: c74d677beb74e9cd348faebbc1b02dff1b48956b0225487ef48af1be080f94137472f892dce6698a2d55c0febbbb1637a477e4ec090098ffe64cc5e26030b33b
7
+ data.tar.gz: 6bf02d8127f74b609d3cc8ae1c03586b96f29e44c921e00893dc550f516817c6b4d3456ad516262c1d919e93a36cceffedc7891039460801de2c937dfbbaf2d1
@@ -103,10 +103,13 @@ pwn setup --list-profiles
103
103
  `--deps` / `--profile` will:
104
104
 
105
105
  1. Resolve the profile → set of native gems + external binaries.
106
- 2. Map those to OS packages for **your** package manager (data lives in
107
- `PWN::Setup::NATIVE_GEMS` / `::TOOLCHAIN` - versioned with the gem, so
108
- `gem install --verbose pwn`, git checkout, Docker, Packer and Vagrant all read the
109
- same table).
106
+ 2. Map those to OS packages for **this** distro and version
107
+ (`PWN::Plugins::DetectOS.distro` / `.version`, e.g. Kali `2026.3` vs Ubuntu
108
+ `24.04`) via `PWN::Setup.packages_for`. Family fallbacks still live in
109
+ `PWN::Setup::NATIVE_GEMS` / `::TOOLCHAIN`. Tools with no OS package on this
110
+ distro (Frida, one_gadget, ROPgadget, checksec, ropper, kube-hunter, or
111
+ Kali-only names on Ubuntu) install via `pip` / `gem` or are skipped so
112
+ `apt-get` does not abort the whole profile.
110
113
  3. Show the exact commands, prompt (unless `--yes`), run them, then
111
114
  `gem pristine` / `gem install` any native extension that still fails to
112
115
  load, and re-run the doctor.
@@ -12,7 +12,7 @@ metadata:
12
12
 
13
13
  # PWN::Plugins::DetectOS
14
14
 
15
- This plugin converts images to readable text
15
+ Detect host OS family, distro flavor, and version string.
16
16
 
17
17
  ## When to use
18
18
 
@@ -36,6 +36,8 @@ PWN::Plugins::DetectOS.type(opts)
36
36
  - `type`
37
37
  - `arch`
38
38
  - `endian`
39
+ - `distro`
40
+ - `version`
39
41
  - `authors`
40
42
  - `help`
41
43
 
@@ -28,11 +28,12 @@ Class methods take `(opts = {})` and read `opts`.
28
28
 
29
29
  ```ruby
30
30
  PWN::Setup.help
31
- PWN::Setup.pkg_manager(opts)
31
+ PWN::Setup.packages_for(opts)
32
32
  ```
33
33
 
34
34
  ## Public methods
35
35
 
36
+ - `packages_for`
36
37
  - `pkg_manager`
37
38
  - `check`
38
39
  - `deps`
@@ -49,5 +50,5 @@ PWN::Setup.pkg_manager(opts)
49
50
 
50
51
  ## Verification
51
52
 
52
- `PWN::Setup.respond_to?(:pkg_manager)` after the
53
+ `PWN::Setup.respond_to?(:packages_for)` after the
53
54
  module is loaded. Read the source for parameter names.
@@ -10,7 +10,7 @@ module PWN
10
10
  WORDLISTS = File.join(Dir.home, '.pwn', 'wordlists')
11
11
 
12
12
  public_class_method def self.required_bins
13
- []
13
+ %w[hydra john hashcat medusa]
14
14
  end
15
15
 
16
16
  public_class_method def self.wordlist_dir(opts = {})
@@ -4,10 +4,28 @@ require 'os'
4
4
 
5
5
  module PWN
6
6
  module Plugins
7
- # This plugin converts images to readable text
7
+ # Detect host OS family, distro flavor, and version string.
8
8
  module DetectOS
9
- # Supported Method Parameters::
10
- # PWN::Plugins::DetectOS.type
9
+ ID_ALIASES = {
10
+ 'chromiumos' => :chromeos,
11
+ 'chromeos' => :chromeos,
12
+ 'pop_os' => :pop,
13
+ 'pop' => :pop,
14
+ 'ol' => :oracle,
15
+ 'opensuse-leap' => :opensuse,
16
+ 'opensuse-tumbleweed' => :opensuse,
17
+ 'sles' => :suse,
18
+ 'darwin' => :macos,
19
+ 'osx' => :macos,
20
+ 'macos' => :macos,
21
+ 'iphoneos' => :ios,
22
+ 'ipados' => :ios,
23
+ 'ios' => :ios,
24
+ 'win32' => :windows,
25
+ 'mingw32' => :windows,
26
+ 'mingw' => :windows,
27
+ 'mswin' => :windows
28
+ }.freeze
11
29
 
12
30
  public_class_method def self.type
13
31
  os = :cygwin if OS.cygwin?
@@ -23,18 +41,12 @@ module PWN
23
41
  raise e
24
42
  end
25
43
 
26
- # Supported Method Parameters::
27
- # PWN::Plugins::DetectOS.arch
28
-
29
44
  public_class_method def self.arch
30
45
  OS.host_cpu
31
46
  rescue StandardError => e
32
47
  raise e
33
48
  end
34
49
 
35
- # Supported Method Parameters::
36
- # PWN::Plugins::DetectOS.endian
37
-
38
50
  public_class_method def self.endian
39
51
  if [1].pack('I') == [1].pack('N')
40
52
  :big
@@ -45,7 +57,99 @@ module PWN
45
57
  raise e
46
58
  end
47
59
 
48
- # Author(s):: 0day Inc. <support@0dayinc.com>
60
+ # Supported Method Parameters::
61
+ # PWN::Plugins::DetectOS.distro(
62
+ # os_release: 'optional - /etc/os-release body (defaults to reading the file)',
63
+ # lsb_release: 'optional - /etc/lsb-release body',
64
+ # build_prop: 'optional - Android /system/build.prop body',
65
+ # type: 'optional - OS family from #type (e.g. :linux :osx :windows :freebsd)',
66
+ # platform: 'optional - RUBY_PLATFORM override',
67
+ # uname: 'optional - uname -s string',
68
+ # sw_vers: 'optional - macOS/iOS product version string',
69
+ # win_ver: 'optional - Windows ver command output'
70
+ # )
71
+
72
+ public_class_method def self.distro(opts = {})
73
+ return normalize_id(id: opts[:distro]) if opts[:distro]
74
+
75
+ platform = (opts[:platform] || RUBY_PLATFORM).to_s.downcase
76
+ uname = (opts[:uname] || live_uname_s).to_s.downcase
77
+ os_type = opts[:type] || type
78
+ osr = parse_kv(content: opts[:os_release] || read_if_present(path: '/etc/os-release') || read_if_present(path: '/usr/lib/os-release'))
79
+ lsb = parse_kv(content: opts[:lsb_release] || read_if_present(path: '/etc/lsb-release'))
80
+ prop = parse_kv(content: opts[:build_prop] || read_if_present(path: '/system/build.prop'))
81
+
82
+ return :ios if ios_host?(platform: platform, uname: uname)
83
+ return :android if android_host?(osr: osr, prop: prop, uname: uname)
84
+ return :chromeos if chromeos_host?(osr: osr, lsb: lsb)
85
+ return :macos if os_type == :osx || platform.include?('darwin')
86
+ return :windows if os_type == :windows || platform.match?(/mswin|mingw|cygwin/)
87
+ return :freebsd if os_type == :freebsd || uname.include?('freebsd')
88
+ return :openbsd if os_type == :openbsd || uname.include?('openbsd')
89
+ return :netbsd if os_type == :netbsd || uname.include?('netbsd')
90
+ return :dragonfly if uname.include?('dragonfly')
91
+ return :cygwin if os_type == :cygwin
92
+
93
+ id = osr['ID'].to_s
94
+ return normalize_id(id: id) unless id.empty?
95
+
96
+ like = osr['ID_LIKE'].to_s.downcase
97
+ return :debian if like.include?('debian')
98
+ return :fedora if like.include?('fedora') || like.include?('rhel')
99
+ return :arch if like.include?('arch')
100
+ return :suse if like.include?('suse')
101
+
102
+ os_type || :unknown
103
+ end
104
+
105
+ # Supported Method Parameters::
106
+ # PWN::Plugins::DetectOS.version(
107
+ # os_release: 'optional - /etc/os-release body',
108
+ # lsb_release: 'optional - /etc/lsb-release body',
109
+ # build_prop: 'optional - Android build.prop body',
110
+ # sw_vers: 'optional - macOS/iOS product version',
111
+ # win_ver: 'optional - Windows ver output',
112
+ # uname_r: 'optional - uname -r string',
113
+ # type: 'optional - OS family from #type'
114
+ # )
115
+
116
+ public_class_method def self.version(opts = {})
117
+ return opts[:version].to_s if opts[:version]
118
+
119
+ injected = %i[os_release lsb_release build_prop sw_vers win_ver uname_r].any? { |k| opts.key?(k) }
120
+ os_type = opts[:type] || type
121
+ osr = parse_kv(content: opts[:os_release] || (injected ? nil : read_if_present(path: '/etc/os-release') || read_if_present(path: '/usr/lib/os-release')))
122
+ lsb = parse_kv(content: opts[:lsb_release] || (injected ? nil : read_if_present(path: '/etc/lsb-release')))
123
+ prop = parse_kv(content: opts[:build_prop] || (injected ? nil : read_if_present(path: '/system/build.prop')))
124
+
125
+ from_osr = osr['VERSION_ID'].to_s
126
+ return from_osr unless from_osr.empty?
127
+
128
+ chrome = lsb['CHROMEOS_RELEASE_VERSION'].to_s
129
+ return chrome unless chrome.empty?
130
+
131
+ android = prop['ro.build.version.release'].to_s
132
+ return android unless android.empty?
133
+
134
+ if os_type == :osx || opts[:sw_vers]
135
+ mac = (opts[:sw_vers] || live_sw_vers).to_s.strip
136
+ return mac unless mac.empty?
137
+ end
138
+
139
+ if os_type == :windows || opts[:win_ver]
140
+ win = (opts[:win_ver] || live_win_ver).to_s
141
+ m = win.match(/\[Version\s+([^\]]+)\]/i) || win.match(/(\d+\.\d+(?:\.\d+)*)/)
142
+ return m[1] if m
143
+ end
144
+
145
+ rel = (opts[:uname_r] || live_uname_r).to_s.strip
146
+ return rel if %i[freebsd openbsd netbsd].include?(os_type) && !rel.empty?
147
+
148
+ debian = read_if_present(path: '/etc/debian_version').to_s.strip
149
+ return debian unless debian.empty?
150
+
151
+ osr['VERSION'].to_s
152
+ end
49
153
 
50
154
  public_class_method def self.authors
51
155
  "AUTHOR(S):
@@ -53,24 +157,129 @@ module PWN
53
157
  "
54
158
  end
55
159
 
56
- # Display Usage for this Module
57
-
58
160
  public_class_method def self.help
59
161
  puts "USAGE:
60
- # Run type and return its result
162
+ # Return the OS family as a symbol (:linux :osx :windows :freebsd :openbsd :netbsd :cygwin).
61
163
  #{self}.type
62
164
 
63
- # Run arch and return its result
165
+ # Return the CPU architecture string from the host (e.g. x86_64, arm64).
64
166
  #{self}.arch
65
167
 
66
- # Run endian and return its result
168
+ # Return CPU endianness as :little or :big.
67
169
  #{self}.endian
68
170
 
171
+ # Return the distro flavor as a lowercase symbol (:kali :ubuntu :macos :windows :freebsd …).
172
+ #{self}.distro(
173
+ os_release: 'optional - /etc/os-release body (defaults to reading the file)',
174
+ lsb_release: 'optional - /etc/lsb-release body',
175
+ build_prop: 'optional - Android /system/build.prop body',
176
+ type: 'optional - OS family from #type (e.g. :linux :osx :windows :freebsd)',
177
+ platform: 'optional - RUBY_PLATFORM override (detect iOS/Android/Windows)',
178
+ uname: 'optional - uname -s string',
179
+ sw_vers: 'optional - macOS/iOS product version string',
180
+ win_ver: 'optional - Windows ver command output',
181
+ distro: 'optional - force a distro id instead of detecting'
182
+ )
183
+
184
+ # Return the distro version as a string (e.g. 2026.3, 24.04, 15.1, 14.1-RELEASE).
185
+ #{self}.version(
186
+ os_release: 'optional - /etc/os-release body',
187
+ lsb_release: 'optional - /etc/lsb-release body',
188
+ build_prop: 'optional - Android build.prop body',
189
+ sw_vers: 'optional - macOS/iOS product version',
190
+ win_ver: 'optional - Windows ver output',
191
+ uname_r: 'optional - uname -r string',
192
+ type: 'optional - OS family from #type',
193
+ version: 'optional - force a version string instead of detecting'
194
+ )
195
+
69
196
  # Print the AUTHOR(S) string for this module.
70
197
  #{self}.authors
71
198
  "
72
199
  constants.sort
73
200
  end
201
+
202
+ private_class_method def self.normalize_id(opts = {})
203
+ raw = opts[:id].to_s.downcase.strip.gsub(/["']/, '')
204
+ return :unknown if raw.empty?
205
+
206
+ ID_ALIASES[raw] || raw.tr('-', '_').to_sym
207
+ end
208
+
209
+ private_class_method def self.parse_kv(opts = {})
210
+ content = opts[:content].to_s
211
+ return {} if content.empty?
212
+
213
+ content.each_line.with_object({}) do |line, acc|
214
+ next if line.strip.empty? || line.strip.start_with?('#')
215
+
216
+ key, val = line.split('=', 2)
217
+ next unless val
218
+
219
+ acc[key.strip] = val.strip.gsub(/\A["']|["']\z/, '')
220
+ end
221
+ end
222
+
223
+ private_class_method def self.read_if_present(opts = {})
224
+ path = opts[:path].to_s
225
+ return unless File.readable?(path)
226
+
227
+ File.read(path)
228
+ rescue StandardError
229
+ nil
230
+ end
231
+
232
+ private_class_method def self.ios_host?(opts = {})
233
+ blob = "#{opts[:platform]} #{opts[:uname]}"
234
+ blob.match?(/iphone|ipad|ios/)
235
+ end
236
+
237
+ private_class_method def self.android_host?(opts = {})
238
+ osr = opts[:osr] || {}
239
+ prop = opts[:prop] || {}
240
+ uname = opts[:uname].to_s
241
+ osr['ID'].to_s.downcase == 'android' ||
242
+ !prop['ro.build.version.release'].to_s.empty? ||
243
+ uname.include?('android') ||
244
+ File.exist?('/system/build.prop')
245
+ end
246
+
247
+ private_class_method def self.chromeos_host?(opts = {})
248
+ osr = opts[:osr] || {}
249
+ lsb = opts[:lsb] || {}
250
+ id = osr['ID'].to_s.downcase
251
+ id == 'chromeos' || id == 'chromiumos' ||
252
+ !lsb['CHROMEOS_RELEASE_NAME'].to_s.empty? ||
253
+ !lsb['CHROMEOS_RELEASE_VERSION'].to_s.empty?
254
+ end
255
+
256
+ private_class_method def self.live_uname_s(opts = {})
257
+ opts[:skip]
258
+ `uname -s 2>/dev/null`.to_s
259
+ rescue StandardError
260
+ ''
261
+ end
262
+
263
+ private_class_method def self.live_uname_r(opts = {})
264
+ opts[:skip]
265
+ `uname -r 2>/dev/null`.to_s
266
+ rescue StandardError
267
+ ''
268
+ end
269
+
270
+ private_class_method def self.live_sw_vers(opts = {})
271
+ opts[:skip]
272
+ `sw_vers -productVersion 2>/dev/null`.to_s
273
+ rescue StandardError
274
+ ''
275
+ end
276
+
277
+ private_class_method def self.live_win_ver(opts = {})
278
+ opts[:skip]
279
+ `ver 2>NUL`.to_s
280
+ rescue StandardError
281
+ ''
282
+ end
74
283
  end
75
284
  end
76
285
  end
@@ -23,7 +23,11 @@ module PWN
23
23
  'PWN::Plugins::BurpSuite' => { bins: %w[burpsuite] },
24
24
  'PWN::Plugins::Zaproxy' => { bins: %w[zaproxy] },
25
25
  'PWN::Plugins::Packet' => { bins: [], caps: %w[CAP_NET_RAW] },
26
- 'PWN::Plugins::K8s' => { bins: %w[trivy], services: [{ name: 'docker', path: '/var/run/docker.sock' }] }
26
+ 'PWN::Plugins::K8s' => { bins: %w[trivy], services: [{ name: 'docker', path: '/var/run/docker.sock' }] },
27
+ 'PWN::Plugins::Semgrep' => { bins: %w[semgrep] },
28
+ 'PWN::Plugins::ExploitDB' => { bins: %w[searchsploit] },
29
+ 'PWN::Plugins::Recon' => { bins: %w[subfinder httpx] },
30
+ 'PWN::Plugins::CredentialAttack' => { bins: %w[hydra john hashcat] }
27
31
  }.freeze
28
32
 
29
33
  public_class_method def self.required_bins
@@ -13,7 +13,7 @@ module PWN
13
13
  UA = 'pwn-recon/1.0'
14
14
 
15
15
  public_class_method def self.required_bins
16
- []
16
+ %w[subfinder httpx masscan amass]
17
17
  end
18
18
 
19
19
  public_class_method def self.subdomains(opts = {})
data/lib/pwn/setup.rb CHANGED
@@ -160,7 +160,7 @@ module PWN
160
160
  },
161
161
  'sqlmap' => {
162
162
  apt: %w[sqlmap], dnf: %w[sqlmap], pacman: %w[sqlmap], brew: %w[sqlmap], port: %w[],
163
- plugins: %w[PWN::Plugins::Fuzz]
163
+ plugins: %w[PWN::Plugins::Sqlmap PWN::Plugins::Fuzz]
164
164
  },
165
165
  'tor' => {
166
166
  apt: %w[tor], dnf: %w[tor], pacman: %w[tor], brew: %w[tor], port: %w[tor],
@@ -246,15 +246,18 @@ module PWN
246
246
  plugins: %w[PWN::Plugins::Radare2]
247
247
  },
248
248
  'checksec' => {
249
- apt: %w[], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
249
+ apt: %w[], dnf: %w[], pacman: %w[checksec], brew: %w[], port: %w[],
250
+ pip: 'checksec.py',
250
251
  plugins: %w[PWN::Plugins::GDB PWN::Plugins::ExploitDev]
251
252
  },
252
253
  'ROPgadget' => {
253
254
  apt: %w[], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
255
+ pip: 'ROPGadget',
254
256
  plugins: %w[PWN::Plugins::ExploitDev]
255
257
  },
256
258
  'one_gadget' => {
257
259
  apt: %w[], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
260
+ gem: 'one_gadget',
258
261
  plugins: %w[PWN::Plugins::ExploitDev]
259
262
  },
260
263
  'pwndbg' => {
@@ -263,6 +266,7 @@ module PWN
263
266
  },
264
267
  'ropper' => {
265
268
  apt: %w[], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
269
+ pip: 'ropper',
266
270
  plugins: %w[PWN::Plugins::ExploitDev]
267
271
  },
268
272
  'rizin' => {
@@ -280,6 +284,7 @@ module PWN
280
284
  },
281
285
  'frida' => {
282
286
  apt: %w[], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
287
+ pip: 'frida-tools',
283
288
  plugins: %w[PWN::Plugins::Frida]
284
289
  },
285
290
  'apktool' => {
@@ -297,8 +302,78 @@ module PWN
297
302
  'hydra' => {
298
303
  apt: %w[hydra], dnf: %w[hydra], pacman: %w[hydra], brew: %w[thc-hydra], port: %w[],
299
304
  plugins: %w[PWN::Plugins::CredentialAttack]
305
+ },
306
+ 'afl-fuzz' => {
307
+ apt: %w[afl++], dnf: %w[afl++], pacman: %w[afl++], brew: %w[afl++], port: %w[],
308
+ plugins: %w[PWN::Plugins::AFLplusplus]
309
+ },
310
+ 'searchsploit' => {
311
+ apt: %w[exploitdb], dnf: %w[], pacman: %w[exploitdb], brew: %w[], port: %w[],
312
+ plugins: %w[PWN::Plugins::ExploitDB]
313
+ },
314
+ 'trivy' => {
315
+ apt: %w[trivy], dnf: %w[trivy], pacman: %w[trivy], brew: %w[trivy], port: %w[],
316
+ plugins: %w[PWN::Plugins::K8s]
317
+ },
318
+ 'kube-hunter' => {
319
+ apt: %w[], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
320
+ pip: 'kube-hunter',
321
+ plugins: %w[PWN::Plugins::K8s]
322
+ },
323
+ 'semgrep' => {
324
+ apt: %w[semgrep], dnf: %w[], pacman: %w[], brew: %w[semgrep], port: %w[],
325
+ pip: 'semgrep',
326
+ plugins: %w[PWN::Plugins::Semgrep]
327
+ },
328
+ 'vol' => {
329
+ apt: %w[volatility3], dnf: %w[], pacman: %w[volatility3], brew: %w[], port: %w[],
330
+ pip: 'volatility3',
331
+ plugins: %w[PWN::Plugins::Volatility]
332
+ },
333
+ 'subfinder' => {
334
+ apt: %w[subfinder], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
335
+ plugins: %w[PWN::Plugins::Recon]
336
+ },
337
+ 'httpx' => {
338
+ apt: %w[httpx-toolkit], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
339
+ plugins: %w[PWN::Plugins::Recon]
340
+ },
341
+ 'masscan' => {
342
+ apt: %w[masscan], dnf: %w[masscan], pacman: %w[masscan], brew: %w[masscan], port: %w[],
343
+ plugins: %w[PWN::Plugins::Recon]
344
+ },
345
+ 'naabu' => {
346
+ apt: %w[naabu], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
347
+ plugins: %w[PWN::Plugins::Recon]
348
+ },
349
+ 'amass' => {
350
+ apt: %w[amass], dnf: %w[], pacman: %w[], brew: %w[amass], port: %w[],
351
+ plugins: %w[PWN::Plugins::Recon]
352
+ },
353
+ 'john' => {
354
+ apt: %w[john], dnf: %w[john], pacman: %w[john], brew: %w[john], port: %w[john],
355
+ plugins: %w[PWN::Plugins::CredentialAttack]
356
+ },
357
+ 'hashcat' => {
358
+ apt: %w[hashcat], dnf: %w[hashcat], pacman: %w[hashcat], brew: %w[hashcat], port: %w[],
359
+ plugins: %w[PWN::Plugins::CredentialAttack]
360
+ },
361
+ 'medusa' => {
362
+ apt: %w[medusa], dnf: %w[medusa], pacman: %w[medusa], brew: %w[], port: %w[],
363
+ plugins: %w[PWN::Plugins::CredentialAttack]
364
+ },
365
+ 'hashid' => {
366
+ apt: %w[hashid], dnf: %w[], pacman: %w[], brew: %w[], port: %w[],
367
+ pip: 'hashid',
368
+ plugins: %w[PWN::Plugins::CredentialAttack]
300
369
  }
301
- }.freeze
370
+ }.tap do |t|
371
+ %w[burpsuite zaproxy msfconsole nuclei searchsploit httpx subfinder naabu amass apktool jadx rizin pwndbg trivy semgrep vol hashid].each do |bin|
372
+ next unless t[bin]
373
+
374
+ t[bin] = t[bin].merge(ubuntu: [], debian: [])
375
+ end
376
+ end.freeze
302
377
 
303
378
  # Capability profiles — how you get the ergonomics of `pwn-full`
304
379
  # without shipping a second gem. `pwn setup --profile <name>`.
@@ -368,6 +443,27 @@ module PWN
368
443
  OK = "\e[32mok\e[0m"
369
444
  MISS = "\e[31mMISSING\e[0m"
370
445
 
446
+ # Supported Method Parameters::
447
+ # PWN::Setup.packages_for(
448
+ # bin: 'required - TOOLCHAIN key (e.g. nmap or burpsuite)',
449
+ # distro: 'optional - DetectOS.distro symbol (defaults to live detect)',
450
+ # version: 'optional - DetectOS.version string (defaults to live detect)',
451
+ # pm_key: 'optional - package-manager family (:apt :dnf :pacman :brew :port :pkg …)'
452
+ # )
453
+
454
+ public_class_method def self.packages_for(opts = {})
455
+ bin = opts[:bin].to_s
456
+ meta = TOOLCHAIN[bin] || {}
457
+ distro = (opts[:distro] || PWN::Plugins::DetectOS.distro).to_s.to_sym
458
+ version = (opts[:version] || PWN::Plugins::DetectOS.version).to_s
459
+ pm_key = opts[:pm_key] || pkg_manager[:key]
460
+ ver_key = :"#{distro}/#{version}"
461
+ return Array(meta[ver_key]) if meta.key?(ver_key)
462
+ return Array(meta[distro]) if meta.key?(distro)
463
+
464
+ Array(meta[pm_key])
465
+ end
466
+
371
467
  # Supported Method Parameters::
372
468
  # PWN::Setup.pkg_manager
373
469
 
@@ -385,13 +481,39 @@ module PWN
385
481
  end
386
482
  sudo = root || !bin?(name: 'sudo') ? '' : 'sudo '
387
483
 
484
+ distro = begin
485
+ PWN::Plugins::DetectOS.distro
486
+ rescue StandardError
487
+ nil
488
+ end
489
+
388
490
  @pkg_manager =
389
- if bin?(name: 'apt-get') then { key: :apt, install: "#{sudo}apt-get install -y", sudo: !sudo.empty? }
390
- elsif bin?(name: 'dnf') then { key: :dnf, install: "#{sudo}dnf install -y", sudo: !sudo.empty? }
391
- elsif bin?(name: 'pacman') then { key: :pacman, install: "#{sudo}pacman -S --noconfirm", sudo: !sudo.empty? }
392
- elsif bin?(name: 'brew') then { key: :brew, install: 'brew install', sudo: false }
393
- elsif bin?(name: 'port') then { key: :port, install: "#{sudo}port -N install", sudo: !sudo.empty? }
394
- else { key: :unknown, install: nil, sudo: false }
491
+ if distro == :freebsd && bin?(name: 'pkg')
492
+ { key: :pkg, install: "#{sudo}pkg install -y", sudo: !sudo.empty? }
493
+ elsif distro == :openbsd && bin?(name: 'pkg_add')
494
+ { key: :pkg_add, install: "#{sudo}pkg_add", sudo: !sudo.empty? }
495
+ elsif distro == :netbsd && bin?(name: 'pkgin')
496
+ { key: :pkgin, install: "#{sudo}pkgin -y install", sudo: !sudo.empty? }
497
+ elsif distro == :alpine && bin?(name: 'apk')
498
+ { key: :apk, install: "#{sudo}apk add", sudo: !sudo.empty? }
499
+ elsif distro == :android && bin?(name: 'pkg')
500
+ { key: :pkg, install: 'pkg install -y', sudo: false }
501
+ elsif distro == :windows && bin?(name: 'winget')
502
+ { key: :winget, install: 'winget install -e --accept-package-agreements --accept-source-agreements', sudo: false }
503
+ elsif distro == :windows && bin?(name: 'choco')
504
+ { key: :choco, install: 'choco install -y', sudo: false }
505
+ elsif bin?(name: 'apt-get')
506
+ { key: :apt, install: "#{sudo}apt-get install -y", sudo: !sudo.empty? }
507
+ elsif bin?(name: 'dnf')
508
+ { key: :dnf, install: "#{sudo}dnf install -y", sudo: !sudo.empty? }
509
+ elsif bin?(name: 'pacman')
510
+ { key: :pacman, install: "#{sudo}pacman -S --noconfirm", sudo: !sudo.empty? }
511
+ elsif bin?(name: 'brew')
512
+ { key: :brew, install: 'brew install', sudo: false }
513
+ elsif bin?(name: 'port')
514
+ { key: :port, install: "#{sudo}port -N install", sudo: !sudo.empty? }
515
+ else
516
+ { key: :unknown, install: nil, sudo: false }
395
517
  end
396
518
  end
397
519
 
@@ -506,12 +628,14 @@ module PWN
506
628
  profile = (opts[:profile] || :full).to_sym
507
629
  yes = opts[:yes] ? true : false
508
630
  dry_run = opts[:dry_run] ? true : false
631
+ distro = opts[:distro] || PWN::Plugins::DetectOS.distro
632
+ version = opts[:version] || PWN::Plugins::DetectOS.version
509
633
  io = opts[:io] || $stdout
510
634
 
511
635
  raise "Unknown profile '#{profile}'. Known: #{PROFILES.keys.join(', ')}" unless PROFILES.key?(profile)
512
636
 
513
637
  pm = pkg_manager
514
- raise 'No supported package manager found (apt / dnf / pacman / brew / port).' if pm[:key] == :unknown
638
+ raise 'No supported package manager found (apt / dnf / pacman / brew / port / pkg / winget / choco).' if pm[:key] == :unknown
515
639
 
516
640
  prof = PROFILES[profile]
517
641
  gems = Array(prof[:gems])
@@ -527,10 +651,11 @@ module PWN
527
651
  bins = Array(prof[:bins])
528
652
  os_pkgs = []
529
653
  gems.each { |g| os_pkgs.concat(Array(NATIVE_GEMS.dig(g, pm[:key]))) }
530
- bins.each { |b| os_pkgs.concat(Array(TOOLCHAIN.dig(b, pm[:key]))) }
654
+ bins.each { |b| os_pkgs.concat(packages_for(bin: b, distro: distro, version: version, pm_key: pm[:key])) }
531
655
  os_pkgs = os_pkgs.reject(&:empty?).uniq
532
656
 
533
657
  io.puts "Profile : #{profile} — #{prof[:desc]}"
658
+ io.puts "Distro : #{distro} #{version}"
534
659
  io.puts "Pkg mgr : #{pm[:key]}"
535
660
  io.puts "OS pkgs : #{os_pkgs.empty? ? '(none)' : os_pkgs.join(' ')}"
536
661
  io.puts "Ruby exts : #{gems.empty? ? '(none)' : gems.join(' ')}"
@@ -543,6 +668,7 @@ module PWN
543
668
  cmds << "gem pristine #{broken.map { |g| Shellwords.escape(g) }.join(' ')}" unless broken.empty?
544
669
  cmds << "gem install #{broken.map { |g| Shellwords.escape(g) }.join(' ')}" unless broken.empty?
545
670
  end
671
+ cmds.concat(alt_install_cmds(bins: bins, pm_key: pm[:key], distro: distro, version: version))
546
672
 
547
673
  if cmds.empty?
548
674
  io.puts "Nothing to do — profile '#{profile}' is already satisfied."
@@ -786,6 +912,23 @@ module PWN
786
912
  false
787
913
  end
788
914
 
915
+ private_class_method def self.alt_install_cmds(opts = {})
916
+ bins = Array(opts[:bins])
917
+ pm_key = opts[:pm_key]
918
+ distro = opts[:distro]
919
+ version = opts[:version]
920
+ bins.filter_map do |b|
921
+ meta = TOOLCHAIN[b] || {}
922
+ next if packages_for(bin: b, pm_key: pm_key, distro: distro, version: version).any?
923
+
924
+ if meta[:pip].to_s != ''
925
+ "python3 -m pip install --user #{Shellwords.escape(meta[:pip])}"
926
+ elsif meta[:gem].to_s != ''
927
+ "gem install #{Shellwords.escape(meta[:gem])}"
928
+ end
929
+ end
930
+ end
931
+
789
932
  private_class_method def self.bin?(opts = {})
790
933
  !which(name: opts[:name]).empty?
791
934
  end
@@ -935,21 +1078,31 @@ module PWN
935
1078
 
936
1079
  public_class_method def self.help
937
1080
  puts "USAGE:
938
- # Run pkg manager and return its result
1081
+ # Detect this host's package manager (apt, dnf, pacman, brew, pkg, winget, …).
939
1082
  #{self}.pkg_manager
940
1083
 
1084
+ # Resolve OS package names for a TOOLCHAIN binary on this distro/version.
1085
+ #{self}.packages_for(
1086
+ bin: 'required - TOOLCHAIN key (e.g. nmap or burpsuite)',
1087
+ distro: 'optional - DetectOS.distro symbol (defaults to live detect)',
1088
+ version: 'optional - DetectOS.version string (defaults to live detect)',
1089
+ pm_key: 'optional - package-manager family (:apt :dnf :pacman :brew :port :pkg)'
1090
+ )
1091
+
941
1092
  # Run check and return its result
942
1093
  #{self}.check(
943
1094
  io: 'optional - IO to write the report to (default $stdout)',
944
1095
  profile: 'optional - profile value consumed by #check'
945
1096
  )
946
1097
 
947
- # Run deps and return its result
1098
+ # Install OS packages + native gems for a capability profile.
948
1099
  #{self}.deps(
949
1100
  profile: 'optional - one of PROFILES.keys (default :full)',
950
1101
  yes: 'optional - non-interactive; assume yes to prompts (default false)',
951
1102
  dry_run: 'optional - print commands only, do not execute (default false)',
952
- io: 'optional - IO to write to (default $stdout)'
1103
+ io: 'optional - IO to write to (default $stdout)',
1104
+ distro: 'optional - DetectOS.distro override (e.g. :kali :ubuntu :macos)',
1105
+ version: 'optional - DetectOS.version override (e.g. 2026.3 or 24.04)'
953
1106
  )
954
1107
 
955
1108
  # Opt-in installer for SHIFT+ENTER multi-line support in the pwn-ai /
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.710'
4
+ VERSION = '0.5.711'
5
5
  end
@@ -146,7 +146,7 @@ RSpec.describe 'documentation/Installation.md' do
146
146
  pm = PWN::Setup.pkg_manager[:key]
147
147
  # every :net bin's package appears in the plan
148
148
  PWN::Setup::PROFILES[:net][:bins].each do |b|
149
- Array(PWN::Setup::TOOLCHAIN.dig(b, pm)).each do |pkg|
149
+ Array(PWN::Setup.packages_for(bin: b, pm_key: pm)).each do |pkg|
150
150
  expect(plan).to include(pkg), "profile :net missing own package '#{pkg}'"
151
151
  end
152
152
  end
@@ -4,12 +4,62 @@ require 'spec_helper'
4
4
 
5
5
  describe PWN::Plugins::DetectOS do
6
6
  it 'should display information for authors' do
7
- authors_response = PWN::Plugins::DetectOS
8
- expect(authors_response).to respond_to :authors
7
+ expect(described_class).to respond_to :authors
9
8
  end
10
9
 
11
10
  it 'should display information for existing help method' do
12
- help_response = PWN::Plugins::DetectOS
13
- expect(help_response).to respond_to :help
11
+ expect(described_class).to respond_to :help
12
+ end
13
+
14
+ describe '.distro' do
15
+ it 'returns a lowercase symbol on this host' do
16
+ expect(described_class.distro).to be_a(Symbol)
17
+ end
18
+
19
+ it 'reads Kali from os-release including rolling versions like 2026.3' do
20
+ body = <<~OS
21
+ ID=kali
22
+ VERSION_ID="2026.3"
23
+ ID_LIKE=debian
24
+ OS
25
+ expect(described_class.distro(os_release: body, type: :linux)).to eq(:kali)
26
+ expect(described_class.version(os_release: body, type: :linux)).to eq('2026.3')
27
+ end
28
+
29
+ it 'reads Ubuntu and Debian' do
30
+ expect(described_class.distro(os_release: "ID=ubuntu\nVERSION_ID=\"24.04\"\n", type: :linux)).to eq(:ubuntu)
31
+ expect(described_class.version(os_release: "ID=ubuntu\nVERSION_ID=\"24.04\"\n", type: :linux)).to eq('24.04')
32
+ expect(described_class.distro(os_release: "ID=debian\nVERSION_ID=\"12\"\n", type: :linux)).to eq(:debian)
33
+ end
34
+
35
+ it 'reads Fedora, Arch, Alpine' do
36
+ expect(described_class.distro(os_release: "ID=fedora\nVERSION_ID=41\n", type: :linux)).to eq(:fedora)
37
+ expect(described_class.distro(os_release: "ID=arch\n", type: :linux)).to eq(:arch)
38
+ expect(described_class.distro(os_release: "ID=alpine\nVERSION_ID=3.20.0\n", type: :linux)).to eq(:alpine)
39
+ end
40
+
41
+ it 'detects ChromeOS from lsb-release' do
42
+ lsb = "CHROMEOS_RELEASE_NAME=Chrome OS\nCHROMEOS_RELEASE_VERSION=15662.64.0\n"
43
+ expect(described_class.distro(lsb_release: lsb, os_release: "ID=chromeos\n", type: :linux)).to eq(:chromeos)
44
+ expect(described_class.version(lsb_release: lsb, os_release: "ID=chromeos\n", type: :linux)).to eq('15662.64.0')
45
+ end
46
+
47
+ it 'detects Android from build.prop' do
48
+ prop = "ro.build.version.release=14\nro.product.brand=google\n"
49
+ expect(described_class.distro(build_prop: prop, type: :linux)).to eq(:android)
50
+ expect(described_class.version(build_prop: prop, type: :linux)).to eq('14')
51
+ end
52
+
53
+ it 'detects macOS, iOS, Windows, and BSDs' do
54
+ expect(described_class.distro(type: :osx, sw_vers: '15.1')).to eq(:macos)
55
+ expect(described_class.version(type: :osx, sw_vers: '15.1')).to eq('15.1')
56
+ expect(described_class.distro(type: :linux, platform: 'arm64-darwin-ios')).to eq(:ios)
57
+ expect(described_class.distro(type: :windows, win_ver: 'Microsoft Windows [Version 10.0.26100.1742]')).to eq(:windows)
58
+ expect(described_class.version(type: :windows, win_ver: 'Microsoft Windows [Version 10.0.26100.1742]')).to eq('10.0.26100.1742')
59
+ expect(described_class.distro(type: :freebsd, uname: 'FreeBSD')).to eq(:freebsd)
60
+ expect(described_class.version(type: :freebsd, uname_r: '14.1-RELEASE')).to eq('14.1-RELEASE')
61
+ expect(described_class.distro(type: :openbsd, uname: 'OpenBSD')).to eq(:openbsd)
62
+ expect(described_class.distro(type: :netbsd, uname: 'NetBSD')).to eq(:netbsd)
63
+ end
14
64
  end
15
65
  end
@@ -101,4 +101,58 @@ describe PWN::Setup do
101
101
  expect(body.scan('PWN::Setup.ensure_cron').length).to eq(1)
102
102
  expect(body).to include('opts[:migrate] && running')
103
103
  end
104
+
105
+ it 'TOOLCHAIN covers every plugin required_bins name' do
106
+ plugin_bins = Dir[File.expand_path('../../../lib/pwn/plugins/*.rb', __dir__)].flat_map do |path|
107
+ src = File.read(path)
108
+ m = src.match(/public_class_method def self\.required_bins\n\s+%w\[([^\]]*)\]/)
109
+ next [] unless m
110
+
111
+ m[1].split
112
+ end.uniq
113
+ missing = plugin_bins - PWN::Setup::TOOLCHAIN.keys
114
+ expect(missing).to eq([]), "pwn setup TOOLCHAIN missing plugin bins: #{missing.join(', ')}"
115
+ end
116
+
117
+ it 'every TOOLCHAIN row has an OS package or pip/gem installer' do
118
+ PWN::Setup::TOOLCHAIN.each do |bin, meta|
119
+ has_pkg = %i[apt dnf pacman brew port].any? { |k| Array(meta[k]).any? }
120
+ has_alt = meta[:pip].to_s != '' || meta[:gem].to_s != ''
121
+ expect(has_pkg || has_alt).to be(true), "#{bin} has no apt/dnf/pacman/brew/port/pip/gem installer"
122
+ end
123
+ end
124
+
125
+ it 'deps --profile full dry-run emits pip/gem for unpackaged toolchain bins' do
126
+ io = StringIO.new
127
+ r = PWN::Setup.deps(profile: :full, dry_run: true, io: io)
128
+ cmds = Array(r[:ran]).map { |h| h[:cmd] }.join("\n")
129
+ expect(r[:skipped]).not_to be true
130
+ unpackaged = PWN::Setup::TOOLCHAIN.select do |_bin, meta|
131
+ %i[apt dnf pacman brew port].none? { |k| Array(meta[k]).any? }
132
+ end
133
+ unpackaged.each do |bin, meta|
134
+ token = meta[:pip] || meta[:gem] || bin
135
+ expect(cmds).to include(token.to_s), "full dry-run missing installer for #{bin} (#{token})"
136
+ end
137
+ end
138
+
139
+ it 'packages_for prefers distro overrides then package-manager family' do
140
+ expect(
141
+ PWN::Setup.packages_for(bin: 'nmap', distro: :ubuntu, version: '24.04', pm_key: :apt)
142
+ ).to eq(%w[nmap])
143
+ expect(
144
+ PWN::Setup.packages_for(bin: 'burpsuite', distro: :kali, version: '2026.3', pm_key: :apt)
145
+ ).to eq(%w[burpsuite])
146
+ expect(
147
+ PWN::Setup.packages_for(bin: 'burpsuite', distro: :ubuntu, version: '24.04', pm_key: :apt)
148
+ ).to eq([])
149
+ end
150
+
151
+ it 'deps skips kali-only apt packages on ubuntu' do
152
+ io = StringIO.new
153
+ r = PWN::Setup.deps(profile: :web, dry_run: true, io: io, distro: :ubuntu, version: '24.04')
154
+ cmds = Array(r[:ran]).map { |h| h[:cmd] }.join(' ')
155
+ expect(cmds).not_to include('burpsuite')
156
+ expect(cmds).not_to include('zaproxy')
157
+ end
104
158
  end
@@ -1933,11 +1933,23 @@
1933
1933
  {"messages":[{"role":"user","content":"PWN::Plugins::DefectDojo.tool_configuration_list Usage"},{"role":"assistant","content":"`PWN::Plugins::DefectDojo.tool_configuration_list`: Supported Method Parameters\n\ntool_configuration_list = PWN::Plugins::DefectDojo.tool_configuration_list(\n\ndd_obj: 'required dd_obj returned from #login method',\nid: 'optional - retrieve single test by id, otherwise return all'\n\n)\n"}]}
1934
1934
  {"messages":[{"role":"user","content":"PWN::Plugins::DefectDojo.tool_configuration_resource_uri_by_name Usage"},{"role":"assistant","content":"`PWN::Plugins::DefectDojo.tool_configuration_resource_uri_by_name`: Supported Method Parameters\n\ntool_configuration_resource_uri_by_name(\n\ndd_obj: 'required dd_obj returned from #login method',\ntool_config_name: 'required tool configuration name'\n\n)\n"}]}
1935
1935
  {"messages":[{"role":"user","content":"PWN::Plugins::DefectDojo.user_list Usage"},{"role":"assistant","content":"`PWN::Plugins::DefectDojo.user_list`: Supported Method Parameters\n\nuser_list = PWN::Plugins::DefectDojo.user_list(\n\ndd_obj: 'required dd_obj returned from #login method',\nid: 'optional - retrieve single user by id, otherwise return all'\n\n)\n"}]}
1936
- {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.arch Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.arch`: Supported Method Parameters\n\nPWN::Plugins::DetectOS.arch\n"}]}
1937
- {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
1938
- {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.endian Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.endian`: Supported Method Parameters\n\nPWN::Plugins::DetectOS.endian\n"}]}
1936
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.android_host? Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.android_host?`: "}]}
1937
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.arch Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.arch`: "}]}
1938
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.authors`: "}]}
1939
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.chromeos_host? Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.chromeos_host?`: "}]}
1940
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.distro Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.distro`: Supported Method Parameters\n\nPWN::Plugins::DetectOS.distro(\n\nos_release: 'optional - /etc/os-release body (defaults to reading the file)',\nlsb_release: 'optional - /etc/lsb-release body',\nbuild_prop: 'optional - Android /system/build.prop body',\ntype: 'optional - OS family from #type (e.g. :linux :osx :windows :freebsd)',\nplatform: 'optional - RUBY_PLATFORM override',\nuname: 'optional - uname -s string',\nsw_vers: 'optional - macOS/iOS product version string',\nwin_ver: 'optional - Windows ver command output'\n\n)\n"}]}
1941
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.endian Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.endian`: "}]}
1939
1942
  {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.help Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.help`: "}]}
1940
- {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.type Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.type`: Supported Method Parameters\n\nPWN::Plugins::DetectOS.type\n"}]}
1943
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.ios_host? Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.ios_host?`: "}]}
1944
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.live_sw_vers Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.live_sw_vers`: "}]}
1945
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.live_uname_r Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.live_uname_r`: "}]}
1946
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.live_uname_s Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.live_uname_s`: "}]}
1947
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.live_win_ver Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.live_win_ver`: "}]}
1948
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.normalize_id Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.normalize_id`: "}]}
1949
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.parse_kv Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.parse_kv`: "}]}
1950
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.read_if_present Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.read_if_present`: "}]}
1951
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.type Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.type`: "}]}
1952
+ {"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.version Usage"},{"role":"assistant","content":"`PWN::Plugins::DetectOS.version`: Supported Method Parameters\n\nPWN::Plugins::DetectOS.version(\n\nos_release: 'optional - /etc/os-release body',\nlsb_release: 'optional - /etc/lsb-release body',\nbuild_prop: 'optional - Android build.prop body',\nsw_vers: 'optional - macOS/iOS product version',\nwin_ver: 'optional - Windows ver output',\nuname_r: 'optional - uname -r string',\ntype: 'optional - OS family from #type'\n\n)\n"}]}
1941
1953
  {"messages":[{"role":"user","content":"PWN::Plugins::EIN.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::EIN.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
1942
1954
  {"messages":[{"role":"user","content":"PWN::Plugins::EIN.generate Usage"},{"role":"assistant","content":"`PWN::Plugins::EIN.generate`: Supported Method Parameters\n\nPWN::Plugins::EIN.generate(\n\ncount: 'required - number of EIN numbers to generate'\n\n)\n"}]}
1943
1955
  {"messages":[{"role":"user","content":"PWN::Plugins::EIN.help Usage"},{"role":"assistant","content":"`PWN::Plugins::EIN.help`: "}]}
@@ -3171,6 +3183,7 @@
3171
3183
  {"messages":[{"role":"user","content":"PWN::Sessions.to_response_history Usage"},{"role":"assistant","content":"`PWN::Sessions.to_response_history`: Supported Method Parameters\n\nhistory_for_ai = PWN::Sessions.to_response_history(session_id:) (converts transcript to the response_history format used by PWN::AI::* .chat)\n"}]}
3172
3184
  {"messages":[{"role":"user","content":"PWN::Setup.ai_active_engine Usage"},{"role":"assistant","content":"`PWN::Setup.ai_active_engine`: "}]}
3173
3185
  {"messages":[{"role":"user","content":"PWN::Setup.ai_key_configured? Usage"},{"role":"assistant","content":"`PWN::Setup.ai_key_configured?`: "}]}
3186
+ {"messages":[{"role":"user","content":"PWN::Setup.alt_install_cmds Usage"},{"role":"assistant","content":"`PWN::Setup.alt_install_cmds`: "}]}
3174
3187
  {"messages":[{"role":"user","content":"PWN::Setup.authors Usage"},{"role":"assistant","content":"`PWN::Setup.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
3175
3188
  {"messages":[{"role":"user","content":"PWN::Setup.bin? Usage"},{"role":"assistant","content":"`PWN::Setup.bin?`: "}]}
3176
3189
  {"messages":[{"role":"user","content":"PWN::Setup.check Usage"},{"role":"assistant","content":"`PWN::Setup.check`: Supported Method Parameters\n\nPWN::Setup.check(\n\nio: 'optional - IO to write the report to (default $stdout)'\n\n)\n"}]}
@@ -3181,6 +3194,7 @@
3181
3194
  {"messages":[{"role":"user","content":"PWN::Setup.help Usage"},{"role":"assistant","content":"`PWN::Setup.help`: "}]}
3182
3195
  {"messages":[{"role":"user","content":"PWN::Setup.list_profiles Usage"},{"role":"assistant","content":"`PWN::Setup.list_profiles`: Supported Method Parameters\n\nPWN::Setup.list_profiles(\n\nio: 'optional - IO to write to (default $stdout)'\n\n)\n"}]}
3183
3196
  {"messages":[{"role":"user","content":"PWN::Setup.migrate Usage"},{"role":"assistant","content":"`PWN::Setup.migrate`: Supported Method Parameters\n\nPWN::Setup.migrate(\n\nfix: 'optional - also autofix incompatible ~/.pwn files (default false)',\ndry_run: 'optional - print what WOULD happen (default false)',\nyes: 'optional - alias for fix:true (CI-friendly)',\nio: 'optional - IO to write to (default $stdout)'\n\n)\n\nDelegate to PWN::Migrate.run — verify every ~/.pwn state file is compatible with THIS pwn release and (optionally) autofix it. A timestamped backup of ~/.pwn is taken first. See ‘PWN::Migrate.help`.\n"}]}
3197
+ {"messages":[{"role":"user","content":"PWN::Setup.packages_for Usage"},{"role":"assistant","content":"`PWN::Setup.packages_for`: Supported Method Parameters\n\nPWN::Setup.packages_for(\n\nbin: 'required - TOOLCHAIN key (e.g. nmap or burpsuite)',\ndistro: 'optional - DetectOS.distro symbol (defaults to live detect)',\nversion: 'optional - DetectOS.version string (defaults to live detect)',\npm_key: 'optional - package-manager family (:apt :dnf :pacman :brew :port :pkg …)'\n\n)\n"}]}
3184
3198
  {"messages":[{"role":"user","content":"PWN::Setup.pkg_manager Usage"},{"role":"assistant","content":"`PWN::Setup.pkg_manager`: Supported Method Parameters\n\nPWN::Setup.pkg_manager\n"}]}
3185
3199
  {"messages":[{"role":"user","content":"PWN::Setup.terminal Usage"},{"role":"assistant","content":"`PWN::Setup.terminal`: Supported Method Parameters\n\nPWN::Setup.terminal(\n\nyes: 'optional - assume yes; non-interactive (default false)',\ndry_run: 'optional - print what WOULD happen (default false)',\nio: 'optional - IO to write to (default $stdout)'\n\n)\n\nOpt-in installer for SHIFT+ENTER multi-line support in the pwn-ai / pwn-asm REPL. Never runs implicitly — the user invokes it with ‘pwn setup –terminal` after PWNMultiLineInput#ensure_vte_shift_enter tells them their emulator can’t distinguish Shift+Enter from Enter.\n\nWhat it does:\n\n1. Prints the two `~/.tmux.conf` lines that persist the runtime\n `tmux set -s` calls PWNMultiLineInput already makes.\n2. If running under Terminator (a VTE host), copies\n third_party/terminator/pwn_shift_enter.py to\n ~/.config/terminator/plugins/ and enables the PWNShiftEnter\n plugin in ~/.config/terminator/config — WITH confirmation and\n a .pwnbak backup. This is the only VTE emulator we can fix at\n the GTK layer without patching libvte itself.\n\nEverything here is host-config mutation, which is why it lives in ‘pwn setup` and NOT in the REPL’s runtime path. See GNOME/vte issues #2601 / #2607 for why VTE can’t be fixed with escape sequences alone.\n"}]}
3186
3200
  {"messages":[{"role":"user","content":"PWN::Setup.which Usage"},{"role":"assistant","content":"`PWN::Setup.which`: "}]}
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.710
4
+ version: 0.5.711
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.