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 +4 -4
- data/documentation/Installation.md +7 -4
- data/etc/default_skills/pwn/plugins/detect_os/SKILL.md +3 -1
- data/etc/default_skills/pwn/setup/SKILL.md +3 -2
- data/lib/pwn/plugins/credential_attack.rb +1 -1
- data/lib/pwn/plugins/detect_os.rb +224 -15
- data/lib/pwn/plugins/preflight_checker.rb +5 -1
- data/lib/pwn/plugins/recon.rb +1 -1
- data/lib/pwn/setup.rb +167 -14
- data/lib/pwn/version.rb +1 -1
- data/spec/documentation/installation_md_spec.rb +1 -1
- data/spec/lib/pwn/plugins/detect_os_spec.rb +54 -4
- data/spec/lib/pwn/setup_spec.rb +54 -0
- data/third_party/pwn_rdoc.jsonl +18 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ab0eff0005f523bb5972be43cf49ab4c15ab9eaecda0968c9206621e2997e47
|
|
4
|
+
data.tar.gz: 2a166f6ff6b5620e820de709d5dc1ae356550e23afffbc52de138d3ab6499d7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 **
|
|
107
|
-
`PWN::
|
|
108
|
-
`
|
|
109
|
-
|
|
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
|
-
|
|
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.
|
|
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?(:
|
|
53
|
+
`PWN::Setup.respond_to?(:packages_for)` after the
|
|
53
54
|
module is loaded. Read the source for parameter names.
|
|
@@ -4,10 +4,28 @@ require 'os'
|
|
|
4
4
|
|
|
5
5
|
module PWN
|
|
6
6
|
module Plugins
|
|
7
|
-
#
|
|
7
|
+
# Detect host OS family, distro flavor, and version string.
|
|
8
8
|
module DetectOS
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
162
|
+
# Return the OS family as a symbol (:linux :osx :windows :freebsd :openbsd :netbsd :cygwin).
|
|
61
163
|
#{self}.type
|
|
62
164
|
|
|
63
|
-
#
|
|
165
|
+
# Return the CPU architecture string from the host (e.g. x86_64, arm64).
|
|
64
166
|
#{self}.arch
|
|
65
167
|
|
|
66
|
-
#
|
|
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
|
data/lib/pwn/plugins/recon.rb
CHANGED
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
|
-
}.
|
|
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: '
|
|
390
|
-
|
|
391
|
-
elsif bin?(name: '
|
|
392
|
-
|
|
393
|
-
elsif bin?(name: '
|
|
394
|
-
|
|
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(
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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
|
data/spec/lib/pwn/setup_spec.rb
CHANGED
|
@@ -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
|
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -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.
|
|
1937
|
-
{"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.
|
|
1938
|
-
{"messages":[{"role":"user","content":"PWN::Plugins::DetectOS.
|
|
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.
|
|
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`: "}]}
|