pwn 0.5.719 → 0.5.721
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/etc/default_skills/pwn/ai/agent/engagement/SKILL.md +53 -0
- data/etc/default_skills/pwn/ai/agent/learning/SKILL.md +4 -0
- data/etc/default_skills/pwn/ai/agent/loop/SKILL.md +5 -2
- data/etc/default_skills/pwn/ai/agent/metrics/SKILL.md +1 -0
- data/etc/default_skills/pwn/ai/agent/registry/SKILL.md +3 -0
- data/etc/default_skills/pwn/ai/agent/swarm/SKILL.md +1 -0
- data/etc/default_skills/pwn/ai/agent/tool_guard/SKILL.md +3 -0
- data/etc/default_skills/pwn/ai/agent/tools/debug_lane/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/tools/engagement/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/turn_finalizer/SKILL.md +4 -0
- data/etc/default_skills/pwn/plugins/artifact_registry/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/bin_workspace/SKILL.md +49 -0
- data/etc/default_skills/pwn/plugins/binary_parser/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/capability/SKILL.md +48 -0
- data/etc/default_skills/pwn/plugins/detonate/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/emulator/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/exploit_dev/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/findings/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/gdb/SKILL.md +2 -0
- data/etc/default_skills/pwn/plugins/packet/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/preflight_checker/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/process_tube/SKILL.md +2 -0
- data/etc/default_skills/pwn/plugins/repl/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/vault/SKILL.md +4 -0
- data/etc/default_skills/pwn/reports/engagement/SKILL.md +47 -0
- data/lib/pwn/ai/agent/dispatch.rb +69 -1
- data/lib/pwn/ai/agent/engagement.rb +167 -0
- data/lib/pwn/ai/agent/learning.rb +105 -5
- data/lib/pwn/ai/agent/loop.rb +83 -25
- data/lib/pwn/ai/agent/metrics.rb +35 -1
- data/lib/pwn/ai/agent/registry.rb +28 -0
- data/lib/pwn/ai/agent/reward.rb +80 -15
- data/lib/pwn/ai/agent/swarm.rb +36 -0
- data/lib/pwn/ai/agent/tool_guard.rb +93 -9
- data/lib/pwn/ai/agent/tools/debug_lane.rb +160 -0
- data/lib/pwn/ai/agent/tools/engagement.rb +40 -0
- data/lib/pwn/ai/agent/turn_finalizer.rb +84 -0
- data/lib/pwn/ai/agent.rb +1 -0
- data/lib/pwn/config.rb +18 -2
- data/lib/pwn/plugins/artifact_registry.rb +32 -2
- data/lib/pwn/plugins/bin_workspace.rb +81 -0
- data/lib/pwn/plugins/binary_parser.rb +27 -0
- data/lib/pwn/plugins/capability.rb +68 -0
- data/lib/pwn/plugins/detonate.rb +62 -0
- data/lib/pwn/plugins/emulator.rb +64 -0
- data/lib/pwn/plugins/exploit_dev.rb +33 -0
- data/lib/pwn/plugins/findings.rb +56 -1
- data/lib/pwn/plugins/gdb.rb +42 -0
- data/lib/pwn/plugins/ghidra.rb +20 -2
- data/lib/pwn/plugins/packet.rb +26 -0
- data/lib/pwn/plugins/preflight_checker.rb +23 -0
- data/lib/pwn/plugins/process_tube.rb +31 -1
- data/lib/pwn/plugins/repl.rb +25 -2
- data/lib/pwn/plugins/vault.rb +116 -0
- data/lib/pwn/plugins.rb +4 -0
- data/lib/pwn/reports/engagement.rb +63 -0
- data/lib/pwn/reports.rb +1 -0
- data/lib/pwn/sessions.rb +4 -0
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/dispatch_spec.rb +9 -0
- data/spec/lib/pwn/ai/agent/engagement_spec.rb +27 -0
- data/spec/lib/pwn/ai/agent/learning_spec.rb +23 -0
- data/spec/lib/pwn/ai/agent/loop_spec.rb +44 -0
- data/spec/lib/pwn/ai/agent/reward_spec.rb +17 -1
- data/spec/lib/pwn/ai/agent/swarm_spec.rb +11 -0
- data/spec/lib/pwn/ai/agent/tool_guard_spec.rb +9 -0
- data/spec/lib/pwn/ai/agent/tools/debug_lane_spec.rb +16 -0
- data/spec/lib/pwn/ai/agent/tools/engagement_spec.rb +16 -0
- data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +18 -0
- data/spec/lib/pwn/plugins/bin_workspace_spec.rb +26 -0
- data/spec/lib/pwn/plugins/capability_spec.rb +19 -0
- data/spec/lib/pwn/plugins/detonate_spec.rb +24 -0
- data/spec/lib/pwn/plugins/emulator_spec.rb +18 -0
- data/spec/lib/pwn/plugins/findings_spec.rb +16 -3
- data/spec/lib/pwn/plugins/gdb_spec.rb +10 -0
- data/spec/lib/pwn/plugins/packet_spec.rb +6 -0
- data/spec/lib/pwn/plugins/repl_spec.rb +1 -1
- data/spec/lib/pwn/plugins/vault_spec.rb +11 -0
- data/spec/lib/pwn/reports/engagement_spec.rb +30 -0
- data/third_party/pwn_rdoc.jsonl +73 -0
- metadata +25 -1
|
@@ -411,7 +411,7 @@ describe 'PWN::AI::Agent::Reward vs TUI plan' do
|
|
|
411
411
|
|
|
412
412
|
it 'floors verified PASS analytical answers at 0.6' do
|
|
413
413
|
src = File.read(PWN::AI::Agent::Reward.method(:judge).source_location.first)
|
|
414
|
-
expect(src).to include('[score, 0.
|
|
414
|
+
expect(src).to include('[score, 0.6].max')
|
|
415
415
|
expect(src).to include('\bPASS\b')
|
|
416
416
|
end
|
|
417
417
|
|
|
@@ -422,4 +422,20 @@ describe 'PWN::AI::Agent::Reward vs TUI plan' do
|
|
|
422
422
|
m = PWN::AI::Agent::Reward.send(:judge_model)
|
|
423
423
|
expect(m).to eq('local-judge')
|
|
424
424
|
end
|
|
425
|
+
|
|
426
|
+
it 'records verifier PASS as success even when the judge scores 0.0' do
|
|
427
|
+
allow(PWN::AI::Agent::Reward).to receive(:llm_judge).and_return(
|
|
428
|
+
{ score: 0.0, source: 'heuristic', verdict: :wrong, rationale: 'overlap=0.01', confidence: 0.3 }
|
|
429
|
+
)
|
|
430
|
+
v = PWN::AI::Agent::Reward.judge(
|
|
431
|
+
request: 'write /tmp/x',
|
|
432
|
+
final: 'PASS file exists',
|
|
433
|
+
trace: ['{"success":true}'],
|
|
434
|
+
commit: false,
|
|
435
|
+
verifier_verdict: :pass
|
|
436
|
+
)
|
|
437
|
+
expect(v[:success]).to eq(true)
|
|
438
|
+
expect(v[:verifier_verdict]).to eq(:pass)
|
|
439
|
+
expect(v[:judge_score]).to be < 0.6
|
|
440
|
+
end
|
|
425
441
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
4
5
|
|
|
5
6
|
describe PWN::AI::Agent::Swarm do
|
|
6
7
|
it 'should display information for authors' do
|
|
@@ -43,4 +44,14 @@ describe PWN::AI::Agent::Swarm do
|
|
|
43
44
|
expect(rows.length).to eq(4)
|
|
44
45
|
expect(rows.map { |r| r[:port] }.uniq.length).to eq(4)
|
|
45
46
|
end
|
|
47
|
+
|
|
48
|
+
it 'allows only one live claim per unit' do
|
|
49
|
+
Dir.mktmpdir do |dir|
|
|
50
|
+
allow(Dir).to receive(:home).and_return(dir)
|
|
51
|
+
a = described_class.claim(unit: '10.0.0.5:recon', agent_id: 'a', ttl: 60, engagement_id: 'lab')
|
|
52
|
+
b = described_class.claim(unit: '10.0.0.5:recon', agent_id: 'b', ttl: 60, engagement_id: 'lab')
|
|
53
|
+
expect(a[:ok]).to eq(true)
|
|
54
|
+
expect(b[:ok]).to eq(false)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
46
57
|
end
|
|
@@ -36,6 +36,15 @@ describe PWN::AI::Agent::ToolGuard do
|
|
|
36
36
|
it 'rejects a real command' do
|
|
37
37
|
expect(described_class.placeholder?(text: 'uname -r')).to be false
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
it 'does not treat ellipsis inside a heredoc body as a placeholder command' do
|
|
41
|
+
body = "cat <<'EOF'\nThe token ... lives in the document body.\nEOF\n"
|
|
42
|
+
expect(described_class.placeholder?(text: body)).to be false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'still denies a command that is only a placeholder token' do
|
|
46
|
+
expect(described_class.placeholder?(text: ' ... ')).to be true
|
|
47
|
+
end
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
describe '.coerce_args' do
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe 'PWN::AI::Agent::Tools debug_lane' do
|
|
6
|
+
before(:all) do
|
|
7
|
+
PWN::AI::Agent::Registry.discover(force: true)
|
|
8
|
+
load '/opt/pwn/lib/pwn/ai/agent/tools/debug_lane.rb'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'registers debug_session binary_diff crash_triage emulate detonate budget_status pty_expect' do
|
|
12
|
+
%w[debug_session binary_diff crash_triage capability_request emulate detonate budget_status pty_expect].each do |name|
|
|
13
|
+
expect(PWN::AI::Agent::Registry.lookup(name: name)).not_to be_nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe 'PWN::AI::Agent::Tools engagement' do
|
|
6
|
+
before(:all) do
|
|
7
|
+
PWN::AI::Agent::Registry.discover(force: true)
|
|
8
|
+
load '/opt/pwn/lib/pwn/ai/agent/tools/engagement.rb'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'registers engagement_open close and status' do
|
|
12
|
+
expect(PWN::AI::Agent::Registry.lookup(name: 'engagement_open')).not_to be_nil
|
|
13
|
+
expect(PWN::AI::Agent::Registry.lookup(name: 'engagement_close')).not_to be_nil
|
|
14
|
+
expect(PWN::AI::Agent::Registry.lookup(name: 'engagement_status')).not_to be_nil
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -103,4 +103,22 @@ describe PWN::AI::Agent::TurnFinalizer do
|
|
|
103
103
|
described_class.leave_user_path! while described_class.user_path?
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
|
+
|
|
107
|
+
it 'finalizes write-then-readback and names unmet without readback' do
|
|
108
|
+
Dir.mktmpdir do |dir|
|
|
109
|
+
path = File.join(dir, 'out.pdf')
|
|
110
|
+
File.write(path, 'pdf')
|
|
111
|
+
msgs = [
|
|
112
|
+
{ role: 'tool', content: { success: true, result: { path: path }, effect: 'write' }.to_json },
|
|
113
|
+
{ role: 'tool', content: { success: true, result: { stdout: path }, effect: 'read' }.to_json }
|
|
114
|
+
]
|
|
115
|
+
row = described_class.arbitrate(request: "store #{path}", messages: msgs, paths: [path])
|
|
116
|
+
expect(row[:complete]).to eq(true)
|
|
117
|
+
expect(row[:unmet]).to eq([])
|
|
118
|
+
write_only = [{ role: 'tool', content: { success: true, result: { path: path }, effect: 'write' }.to_json }]
|
|
119
|
+
nag = described_class.arbitrate(request: "store #{path}", messages: write_only, paths: [path])
|
|
120
|
+
expect(nag[:complete]).to eq(false)
|
|
121
|
+
expect(nag[:unmet].map { |u| u[:criterion] }).to include('readback_missing')
|
|
122
|
+
end
|
|
123
|
+
end
|
|
106
124
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
describe PWN::Plugins::BinWorkspace do
|
|
7
|
+
it 'should display information for authors' do
|
|
8
|
+
expect(described_class).to respond_to :authors
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should display information for existing help method' do
|
|
12
|
+
expect(described_class).to respond_to :help
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'persists annotations by sha256 across opens' do
|
|
16
|
+
Dir.mktmpdir do |dir|
|
|
17
|
+
allow(Dir).to receive(:home).and_return(dir)
|
|
18
|
+
stub_const('PWN::Plugins::BinWorkspace::ROOT', File.join(dir, '.pwn', 'binws'))
|
|
19
|
+
bin = File.join(dir, 'a.bin')
|
|
20
|
+
File.binwrite(bin, 'ELF')
|
|
21
|
+
described_class.annotate(path: bin, addr: '0x401000', kind: 'rename', name: 'win')
|
|
22
|
+
notes = described_class.notes(path: bin)
|
|
23
|
+
expect(notes[:'0x401000'][:name]).to eq('win')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PWN::Plugins::Capability do
|
|
6
|
+
it 'should display information for authors' do
|
|
7
|
+
expect(described_class).to respond_to :authors
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should display information for existing help method' do
|
|
11
|
+
expect(described_class).to respond_to :help
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'does not apply setcap without operator_ack' do
|
|
15
|
+
row = described_class.request(cap: 'cap_net_raw', reason: 'packet')
|
|
16
|
+
expect(row[:needs_ack]).to eq(true)
|
|
17
|
+
expect(row[:ok]).to eq(false)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
describe PWN::Plugins::Detonate do
|
|
7
|
+
it 'should display information for authors' do
|
|
8
|
+
expect(described_class).to respond_to :authors
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should display information for existing help method' do
|
|
12
|
+
expect(described_class).to respond_to :help
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'refuses when isolation preconditions are unmet' do
|
|
16
|
+
Dir.mktmpdir do |dir|
|
|
17
|
+
sample = File.join(dir, 's.bin')
|
|
18
|
+
File.write(sample, 'MZ')
|
|
19
|
+
allow(PWN::Plugins::PreflightChecker).to receive(:bin?).with(name: 'podman').and_return(false)
|
|
20
|
+
row = described_class.detonate(path: sample)
|
|
21
|
+
expect(row[:error].to_s).to match(/isolation/)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PWN::Plugins::Emulator do
|
|
6
|
+
it 'should display information for authors' do
|
|
7
|
+
expect(described_class).to respond_to :authors
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should display information for existing help method' do
|
|
11
|
+
expect(described_class).to respond_to :help
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'returns stub plaintext for a decode function' do
|
|
15
|
+
row = described_class.emulate(binary: '/bin/true', addr: '0x401000', backend: 'stub', plaintext: 'secret')
|
|
16
|
+
expect(row[:ret]).to eq('secret')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -7,7 +7,7 @@ describe PWN::Plugins::Findings do
|
|
|
7
7
|
it 'records and reports a finding' do
|
|
8
8
|
Dir.mktmpdir do |dir|
|
|
9
9
|
stub_const('PWN::Plugins::Findings::FILE', File.join(dir, 'findings.jsonl'))
|
|
10
|
-
row = described_class.record(title: 'xss', severity: 'high', host: '127.0.0.1', poc: '/tmp/poc.rb')
|
|
10
|
+
row = described_class.record(title: 'xss', severity: 'high', host: '127.0.0.1', poc: '/tmp/poc.rb', evidence: 'PoC at /tmp/poc.rb reproduces XSS in search with a reflected payload.')
|
|
11
11
|
expect(row[:id]).not_to be_empty
|
|
12
12
|
expect(described_class.report.first[:title]).to eq('xss')
|
|
13
13
|
end
|
|
@@ -19,7 +19,7 @@ describe PWN::Plugins::Findings do
|
|
|
19
19
|
expect { described_class.record(title: 'xss') }.to raise_error(/poc/i)
|
|
20
20
|
poc = File.join(dir, 'poc.rb')
|
|
21
21
|
File.write(poc, 'puts 1')
|
|
22
|
-
row = described_class.record(title: 'xss', poc_artifacts: [poc], severity: 'high')
|
|
22
|
+
row = described_class.record(title: 'xss', poc_artifacts: [poc], severity: 'high', evidence: 'PoC file reproduces reflected XSS in search; see artifact path.')
|
|
23
23
|
expect(row[:poc_artifacts]).to include(poc)
|
|
24
24
|
out = described_class.render(dir_path: dir, report_name: 'findings')
|
|
25
25
|
expect(File.file?(out[:markdown])).to be true
|
|
@@ -33,10 +33,23 @@ describe PWN::Plugins::Findings do
|
|
|
33
33
|
stub_const('PWN::Plugins::Findings::FILE', File.join(dir, 'findings.jsonl'))
|
|
34
34
|
poc = File.join(dir, 'poc.rb')
|
|
35
35
|
File.write(poc, 'puts 1')
|
|
36
|
-
parent = described_class.record(title: 'xss', poc_artifacts: [poc], severity: 'medium')
|
|
36
|
+
parent = described_class.record(title: 'xss', poc_artifacts: [poc], severity: 'medium', evidence: 'PoC file reproduces reflected XSS in search; see artifact path.')
|
|
37
37
|
child = described_class.chain(parent_id: parent[:id], title: 'account-takeover', poc_artifacts: [poc], severity: 'high')
|
|
38
38
|
expect(child[:chain_parent_id]).to eq(parent[:id])
|
|
39
39
|
expect(child[:composite_severity]).to eq('high')
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
|
+
|
|
43
|
+
it 'flags tampered evidence files' do
|
|
44
|
+
Dir.mktmpdir do |dir|
|
|
45
|
+
allow(Dir).to receive(:home).and_return(dir)
|
|
46
|
+
stub_const('PWN::Plugins::Findings::FILE', File.join(dir, 'findings.jsonl'))
|
|
47
|
+
poc = File.join(dir, 'poc.rb')
|
|
48
|
+
File.write(poc, 'puts 1')
|
|
49
|
+
described_class.record(title: 'xss', poc_artifacts: [poc], engagement_id: 'lab', evidence: 'PoC file reproduces reflected XSS in search; see artifact path.')
|
|
50
|
+
expect(described_class.evidence_verify(engagement_id: 'lab')[:ok]).to eq(true)
|
|
51
|
+
File.write(poc, 'tampered')
|
|
52
|
+
expect(described_class.evidence_verify(engagement_id: 'lab')[:ok]).to eq(false)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
42
55
|
end
|
|
@@ -25,4 +25,14 @@ describe PWN::Plugins::GDB do
|
|
|
25
25
|
expect(joined).to include('break *0x401000')
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
it 'parses crash_info from gdb output' do
|
|
30
|
+
allow(Open3).to receive(:capture3).and_return(
|
|
31
|
+
["Program received signal SIGSEGV\nrip 0x401000\n#0 0x401000 in foo ()\n", '', instance_double(Process::Status, exitstatus: 0)]
|
|
32
|
+
)
|
|
33
|
+
allow(PWN::Plugins::PreflightChecker).to receive(:require_bin!)
|
|
34
|
+
row = described_class.crash_info(binary: '/bin/true')
|
|
35
|
+
expect(row[:signal]).to include('SEGV')
|
|
36
|
+
expect(row[:exploitability_heuristic]).to eq('likely-exploitable')
|
|
37
|
+
end
|
|
28
38
|
end
|
|
@@ -12,4 +12,10 @@ describe PWN::Plugins::Packet do
|
|
|
12
12
|
help_response = PWN::Plugins::Packet
|
|
13
13
|
expect(help_response).to respond_to :help
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
it 'flags connect-scan results as degraded' do
|
|
17
|
+
row = described_class.tcp_connect_scan(hosts: ['127.0.0.1'], ports: [1], timeout: 0.05)
|
|
18
|
+
expect(row[:degraded]).to eq(true)
|
|
19
|
+
expect(row[:results]).to be_an(Array)
|
|
20
|
+
end
|
|
15
21
|
end
|
|
@@ -82,7 +82,7 @@ describe PWN::Plugins::REPL do
|
|
|
82
82
|
hits = described_class.pwn_ai_complete(target: '/sk', line: '/sk')
|
|
83
83
|
expect(hits).to include('/skills')
|
|
84
84
|
hits = described_class.pwn_ai_complete(target: '/', line: '/')
|
|
85
|
-
%w[/cron /skills /sessions /memory /debug /trace /back /help /model].each do |cmd|
|
|
85
|
+
%w[/cron /skills /sessions /memory /debug /trace /back /help /model /learning].each do |cmd|
|
|
86
86
|
expect(hits).to include(cmd)
|
|
87
87
|
end
|
|
88
88
|
hits = described_class.pwn_ai_complete(target: 'li', line: '/cron li')
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
4
5
|
|
|
5
6
|
describe PWN::Plugins::Vault do
|
|
6
7
|
it 'should display information for authors' do
|
|
@@ -12,4 +13,14 @@ describe PWN::Plugins::Vault do
|
|
|
12
13
|
help_response = PWN::Plugins::Vault
|
|
13
14
|
expect(help_response).to respond_to :help
|
|
14
15
|
end
|
|
16
|
+
|
|
17
|
+
it 'expands vault tokens and redacts stored secrets' do
|
|
18
|
+
Dir.mktmpdir do |dir|
|
|
19
|
+
allow(Dir).to receive(:home).and_return(dir)
|
|
20
|
+
described_class.store(label: 'api', secret: 's3cret-value')
|
|
21
|
+
expect(described_class.fetch(label: 'api')).to eq('s3cret-value')
|
|
22
|
+
expect(described_class.expand(text: 'tok={{vault:api}}')).to include('s3cret-value')
|
|
23
|
+
expect(described_class.redact(text: 'tok=s3cret-value')).to include('{{vault:api}}')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
15
26
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
describe PWN::Reports::Engagement do
|
|
7
|
+
it 'should display information for authors' do
|
|
8
|
+
expect(described_class).to respond_to :authors
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should display information for existing help method' do
|
|
12
|
+
expect(described_class).to respond_to :help
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'writes html for two findings with poc paths' do
|
|
16
|
+
Dir.mktmpdir do |dir|
|
|
17
|
+
poc = File.join(dir, 'poc.rb')
|
|
18
|
+
File.write(poc, 'puts 1')
|
|
19
|
+
out = described_class.generate(
|
|
20
|
+
engagement: 'lab',
|
|
21
|
+
dir_path: dir,
|
|
22
|
+
findings: [
|
|
23
|
+
{ title: 'a', severity: 'high', poc: poc },
|
|
24
|
+
{ title: 'b', severity: 'low', poc: poc }
|
|
25
|
+
]
|
|
26
|
+
)
|
|
27
|
+
expect(File.file?(out[:html])).to eq(true)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|