pwn 0.5.733 → 0.5.734
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/.rubocop_todo.yml +5 -0
- data/Gemfile +1 -1
- data/documentation/Agent-Tool-Registry.md +1 -1
- data/documentation/Configuration.md +26 -6
- data/documentation/General-PWN-Usage.md +1 -1
- data/documentation/Recon-Findings-API.md +4 -0
- data/documentation/pwn-REPL.md +21 -1
- data/etc/default_skills/pwn/ai/agent/swarm/SKILL.md +2 -0
- data/etc/default_skills/pwn/plugins/findings/SKILL.md +4 -0
- data/etc/default_skills/pwn/plugins/github/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/repl/SKILL.md +27 -23
- data/etc/default_skills/pwn/plugins/repl/ai/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/repl/asm/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/repl/irc/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/repl/mesh/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/repl/vault/SKILL.md +47 -0
- data/lib/pwn/ai/agent/loop.rb +8 -1
- data/lib/pwn/ai/agent/swarm.rb +64 -0
- data/lib/pwn/ai/agent/tools/finding_record.rb +19 -2
- data/lib/pwn/ai/agent/tools/swarm.rb +19 -0
- data/lib/pwn/config.rb +15 -2
- data/lib/pwn/plugins/findings.rb +171 -0
- data/lib/pwn/plugins/github.rb +44 -0
- data/lib/pwn/plugins/repl/ai.rb +1056 -0
- data/lib/pwn/plugins/repl/asm.rb +55 -0
- data/lib/pwn/plugins/repl/irc.rb +153 -0
- data/lib/pwn/plugins/repl/mesh.rb +1779 -0
- data/lib/pwn/plugins/repl/vault.rb +97 -0
- data/lib/pwn/plugins/repl.rb +94 -1419
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/loop_issue_work_spec.rb +31 -0
- data/spec/lib/pwn/ai/agent/swarm_roster_spec.rb +29 -0
- data/spec/lib/pwn/ai/agent/tools/finding_record_spec.rb +5 -0
- data/spec/lib/pwn/config_spec.rb +14 -1
- data/spec/lib/pwn/plugins/findings_structured_spec.rb +33 -0
- data/spec/lib/pwn/plugins/github_spec.rb +21 -0
- data/spec/lib/pwn/plugins/repl/ai_spec.rb +21 -0
- data/spec/lib/pwn/plugins/repl/asm_spec.rb +21 -0
- data/spec/lib/pwn/plugins/repl/irc_spec.rb +21 -0
- data/spec/lib/pwn/plugins/repl/mesh_spec.rb +21 -0
- data/spec/lib/pwn/plugins/repl/vault_spec.rb +21 -0
- data/spec/lib/pwn/plugins/repl_mesh_channel_route_spec.rb +247 -0
- data/spec/lib/pwn/plugins/repl_mesh_console_spec.rb +130 -0
- data/spec/lib/pwn/plugins/repl_mesh_dispatch_spec.rb +113 -0
- data/spec/lib/pwn/plugins/repl_mesh_dm_spec.rb +72 -0
- data/spec/lib/pwn/plugins/repl_mesh_rx_paint_spec.rb +71 -0
- data/spec/lib/pwn/plugins/repl_pwn_vault_spec.rb +5 -9
- data/spec/lib/pwn/plugins/repl_spec.rb +599 -3
- data/third_party/pwn_rdoc.jsonl +62 -27
- metadata +25 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35f85751c76226260916fb5b417180f8c10a1fe53eb8913295895aec83da8eaa
|
|
4
|
+
data.tar.gz: 7808ea07ce71dd2576b168acf9cb1c429c0f9f4dc098570c13ffa81d416e6ffc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 933f35d3a0d28a1dfdb87eb826874e88b150738b333bfb2518a9366f61cd72b24c5fb7e64fe30e73a92086320d6d3df8bfb88b3d8047f9565eab9dd995780087
|
|
7
|
+
data.tar.gz: 5384de39a95c61302cd37f6a6bcdfbcd54f1f9b047a7588c990afef5b89a8e572007dab3abab3e35547d8dc35c6a43d4f2ec72997122e70ea86ea0a3cb21fc7d
|
data/.rubocop_todo.yml
CHANGED
|
@@ -18,6 +18,11 @@ Layout/TrailingWhitespace:
|
|
|
18
18
|
Lint/NestedMethodDefinition:
|
|
19
19
|
Exclude:
|
|
20
20
|
- 'lib/pwn/plugins/repl.rb'
|
|
21
|
+
- 'lib/pwn/plugins/repl/ai.rb'
|
|
22
|
+
- 'lib/pwn/plugins/repl/asm.rb'
|
|
23
|
+
- 'lib/pwn/plugins/repl/irc.rb'
|
|
24
|
+
- 'lib/pwn/plugins/repl/mesh.rb'
|
|
25
|
+
- 'lib/pwn/plugins/repl/vault.rb'
|
|
21
26
|
|
|
22
27
|
# Offense count: 8
|
|
23
28
|
# This cop supports safe autocorrection (--autocorrect).
|
data/Gemfile
CHANGED
|
@@ -24,7 +24,7 @@ toolsets; the JSON-Schema for each tool is what the model actually sees.
|
|
|
24
24
|
| `policy` | **`policy_stats`** · **`policy_evaluate`** · **`policy_recommend`** | `PWN::AI::Agent::Policy` → `~/.pwn/policy.json` + `~/.pwn/policy_traj.jsonl` |
|
|
25
25
|
| `extrospection` | `extro_snapshot` · `extro_drift` · `extro_observe` · `extro_observations` · `extro_intel` · **`extro_watch`** · **`extro_verify`** · **`extro_rf_tune`** · **`extro_osint`** · **`extro_serial`** · **`extro_telecomm`** · **`extro_packet`** · **`extro_vision`** · **`extro_voice`** · `extro_correlate` · `extro_stats` · `extro_reset` · `extro_auto_toggle` | `PWN::AI::Agent::Extrospection` (+ Serial/Packet/OCR/Voice/BareSIP/TransparentBrowser/GQRX) → `~/.pwn/extrospection.json` |
|
|
26
26
|
| `cron` | `cron_list` · `cron_create` · `cron_run` · `cron_enable` · `cron_disable` · `cron_remove` | `PWN::Cron` → `~/.pwn/cron/jobs.yml` |
|
|
27
|
-
| `swarm` | `agent_list` · `agent_spawn` · `agent_ask` · `agent_debate` · `agent_broadcast` · `swarm_bus` · `swarm_list` | `PWN::AI::Agent::Swarm` → `~/.pwn/agents.yml` + `~/.pwn/swarm/` |
|
|
27
|
+
| `swarm` | `agent_list` · `agent_spawn` · `agent_ask` · `agent_debate` · `agent_broadcast` · `swarm_bus` · `swarm_list` · `agent_roster` | `PWN::AI::Agent::Swarm` → `~/.pwn/agents.yml` + `~/.pwn/swarm/` |
|
|
28
28
|
| `manifest` | `host_os_type` | YAML tool declarations in `lib/pwn/ai/tools/*.yaml` via `PWN::AI::Agent::Manifest` |
|
|
29
29
|
|
|
30
30
|
The `learning` toolset is deliberately large: **Mistakes** (negative feedback),
|
|
@@ -203,18 +203,29 @@ plugins:
|
|
|
203
203
|
token: ... # Jira Personal Access Token for PWN::Plugins::JiraDataCenter. Redacted.
|
|
204
204
|
meshtastic:
|
|
205
205
|
admin_key: ... # Public key authorised to send admin messages to mesh nodes via `pwn-mesh`.
|
|
206
|
+
transport: auto # auto | serial | bluetooth | tcp | mqtt — `auto` probes serial → bluetooth → tcp → mqtt; `/transport` pins one.
|
|
207
|
+
dispatch_to_pwn_ai: false # `/toggle-dispatch-to-pwn-ai` — encrypted + ai_whitelist + `@ai` prefix → pwn-ai via Env ai.active; DMs if the channel is on ai_whitelist.
|
|
208
|
+
ai_whitelist: [] # Channel names pwn-ai may answer on (e.g. LongFast). Empty = none.
|
|
206
209
|
serial:
|
|
207
|
-
port: /dev/
|
|
210
|
+
port: /dev/ttyACM0 # Serial device path (`Meshtastic::Serial.connect` block_dev).
|
|
208
211
|
baud: 115200 # Serial baud rate.
|
|
209
|
-
bits: 8 # Serial data bits.
|
|
210
|
-
stop: 1 # Serial stop bits.
|
|
212
|
+
bits: 8 # Serial data bits (`data_bits`).
|
|
213
|
+
stop: 1 # Serial stop bits (`stop_bits`).
|
|
211
214
|
parity: none # Serial parity (none | even | odd).
|
|
215
|
+
bluetooth:
|
|
216
|
+
address: AA:BB:CC:DD:EE:FF # BLE MAC/address for `Meshtastic::Bluetooth.connect`.
|
|
217
|
+
tcp:
|
|
218
|
+
host: 127.0.0.1 # IP/hostname for `Meshtastic::TCP.connect`.
|
|
219
|
+
port: 4403 # TCP port for a Meshtastic node (default 4403).
|
|
212
220
|
mqtt:
|
|
213
221
|
host: mqtt.meshtastic.org # MQTT broker hostname for Meshtastic-over-MQTT.
|
|
214
222
|
port: 1883 # MQTT broker port (8883 for TLS).
|
|
215
223
|
tls: false # Use TLS to the MQTT broker.
|
|
216
224
|
user: meshdev # MQTT username (public Meshtastic broker default shown).
|
|
217
225
|
pass: large4cats # MQTT password (public Meshtastic broker default shown). Redacted.
|
|
226
|
+
client_id: # Optional MQTT client id (`Meshtastic::MQTT.connect`).
|
|
227
|
+
keep_alive: 60 # Optional MQTT keep-alive seconds.
|
|
228
|
+
ack_timeout: 5 # Optional MQTT ack timeout seconds.
|
|
218
229
|
channel:
|
|
219
230
|
active: LongFast # Which named channel block below `pwn-mesh` uses for TX/RX.
|
|
220
231
|
LongFast: # Channel definition - name is arbitrary, referenced by `active:` above.
|
|
@@ -367,16 +378,25 @@ PWN::Config.refresh_env
|
|
|
367
378
|
| `plugins.jira_data_center.base_uri` | String | - | `PWN::Plugins::JiraDataCenter` | Jira DC REST base (e.g. `https://jira.company.com/rest/api/latest`). |
|
|
368
379
|
| `plugins.jira_data_center.token` | String | - | `PWN::Plugins::JiraDataCenter` | Jira Personal Access Token. |
|
|
369
380
|
| `plugins.meshtastic.admin_key` | String | - | `PWN::Plugins::REPL` (`pwn-mesh`) | Public key authorised to send admin messages to mesh nodes. |
|
|
370
|
-
| `plugins.meshtastic.
|
|
381
|
+
| `plugins.meshtastic.transport` | String | `auto` | `pwn-mesh` | `auto` probes serial → bluetooth → tcp → mqtt. Pin with `/transport <kind>`. |
|
|
382
|
+
| `plugins.meshtastic.dispatch_to_pwn_ai` | Boolean | `false` | `pwn-mesh` `/toggle-dispatch-to-pwn-ai` | When true, pwn-ai may answer TEXT_MESSAGE_APP on a securely encrypted channel that is listed in `ai_whitelist` and whose text begins with `@ai`. DMs are allowed when they reside on a whitelisted channel. |
|
|
383
|
+
| `plugins.meshtastic.ai_whitelist` | Array | `[]` | `pwn-mesh` dispatch | Channel names pwn-ai is allowed to answer on. Empty means none. |
|
|
384
|
+
| `plugins.meshtastic.serial.port` | String | `/dev/ttyACM0` | `pwn-mesh` → `Meshtastic::Serial.connect` `block_dev` | Serial device path. |
|
|
371
385
|
| `plugins.meshtastic.serial.baud` | Integer | `115200` | `pwn-mesh` (serial) | Serial baud rate. |
|
|
372
|
-
| `plugins.meshtastic.serial.bits` | Integer | `8` | `pwn-mesh` (serial) | Serial data bits. |
|
|
373
|
-
| `plugins.meshtastic.serial.stop` | Integer | `1` | `pwn-mesh` (serial) | Serial stop bits. |
|
|
386
|
+
| `plugins.meshtastic.serial.bits` | Integer | `8` | `pwn-mesh` (serial) `data_bits` | Serial data bits. |
|
|
387
|
+
| `plugins.meshtastic.serial.stop` | Integer | `1` | `pwn-mesh` (serial) `stop_bits` | Serial stop bits. |
|
|
374
388
|
| `plugins.meshtastic.serial.parity` | Symbol | `:none` | `pwn-mesh` (serial) | Serial parity. |
|
|
389
|
+
| `plugins.meshtastic.bluetooth.address` | String | - | `pwn-mesh` → `Meshtastic::Bluetooth.connect` | BLE MAC/address of the node. |
|
|
390
|
+
| `plugins.meshtastic.tcp.host` | String | `127.0.0.1` | `pwn-mesh` → `Meshtastic::TCP.connect` | TCP host of the node. |
|
|
391
|
+
| `plugins.meshtastic.tcp.port` | Integer | `4403` | `pwn-mesh` (tcp) | TCP port of the node. |
|
|
375
392
|
| `plugins.meshtastic.mqtt.host` | String | `mqtt.meshtastic.org` | `pwn-mesh` → `Meshtastic::MQTT.connect` | MQTT broker hostname. |
|
|
376
393
|
| `plugins.meshtastic.mqtt.port` | Integer | `1883` | `pwn-mesh` | MQTT broker port. |
|
|
377
394
|
| `plugins.meshtastic.mqtt.tls` | Boolean | `false` | `pwn-mesh` | Use TLS to the MQTT broker. |
|
|
378
395
|
| `plugins.meshtastic.mqtt.user` | String | `meshdev` | `pwn-mesh` | MQTT username. |
|
|
379
396
|
| `plugins.meshtastic.mqtt.pass` | String | `large4cats` | `pwn-mesh` | MQTT password. |
|
|
397
|
+
| `plugins.meshtastic.mqtt.client_id` | String | - | `pwn-mesh` | Optional MQTT client id. |
|
|
398
|
+
| `plugins.meshtastic.mqtt.keep_alive` | Integer | `60` | `pwn-mesh` | Optional MQTT keep-alive seconds. |
|
|
399
|
+
| `plugins.meshtastic.mqtt.ack_timeout` | Integer | `5` | `pwn-mesh` | Optional MQTT ack timeout seconds. |
|
|
380
400
|
| `plugins.meshtastic.channel.active` | String | `LongFast` | `pwn-mesh` | Which named channel block below is used for TX/RX. |
|
|
381
401
|
| `plugins.meshtastic.channel.<NAME>.psk` | String (b64) | `AQ==` | `pwn-mesh` | Channel pre-shared key. |
|
|
382
402
|
| `plugins.meshtastic.channel.<NAME>.region` | String | - | `pwn-mesh` | LoRa region tag (e.g. `US/UT`). |
|
|
@@ -39,7 +39,7 @@ history # what you've typed → copy into a driver
|
|
|
39
39
|
|---|---|
|
|
40
40
|
| `pwn-ai` | Enter the agent TUI (SHIFT+ENTER = newline, ENTER = submit) |
|
|
41
41
|
| `pwn-asm` | Multi-line assembly ↔ opcode workbench |
|
|
42
|
-
| `pwn-mesh` | Meshtastic serial / MQTT client (Ruby ≥ 4.0) |
|
|
42
|
+
| `pwn-mesh` | Meshtastic serial / bluetooth / TCP / MQTT client (Ruby ≥ 4.0) |
|
|
43
43
|
| `pwn-vault` | Decrypt → edit `~/.pwn/pwn.yaml` in `$EDITOR` → re-encrypt |
|
|
44
44
|
| `pwn-ai-memory` | Inspect / edit `~/.pwn/memory.json` |
|
|
45
45
|
| `pwn-ai-sessions` | List / view / delete transcripts |
|
|
@@ -24,6 +24,10 @@ Use `affected_asset: asset[:id]` and `evidence_paths: asset[:evidence_paths]` in
|
|
|
24
24
|
|
|
25
25
|
Legacy `Findings.record`, query/report, chain, render, and SARIF output remain callable; legacy record is not the strict API. Agent record and chain operations are strict. Existing query/export tool operations remain available. Accepted structured rows mark `verification_status: not_executed`; evidence hashes prove captured bytes, not exploit execution. Severity is derived from the validated score. No automatic escalation for linked findings.
|
|
26
26
|
|
|
27
|
+
`Findings.verify` / `finding_record op=verify` attests a working PoC from HTTP request/response files or a script execution log. The impact marker must appear in that evidence or the row stays `failed`. `Findings.retest` replays the same path after a fix (`still_open` vs `fixed`). `Findings.chain_impact` may raise combined severity only when a combined-impact file names every finding id. Issue work is unfinished while recorded findings remain `not_executed` (`issue_work_unverified`).
|
|
28
|
+
|
|
29
|
+
`PWN::AI::Agent::Swarm.ensure_specialists` / `agent_roster` writes recon, authz, injection, xss, and business_logic personas. SARIF export is `Findings.render`; `PWN::Plugins::Github.open_fix_pr` opens a remediation PR (tests stub the GitHub API).
|
|
30
|
+
|
|
27
31
|
Markdown/HTML/JSON report payloads compose connected explicit references into attack-chain sections, preserving all finding fields. Combined severity is maximum recorded constituent severity with a rationale explicitly disclaiming escalation and combined exploitability. This is not a computed CVSS chain score.
|
|
28
32
|
|
|
29
33
|
## Verification boundaries
|
data/documentation/pwn-REPL.md
CHANGED
|
@@ -21,7 +21,7 @@ commands.
|
|
|
21
21
|
| `pwn-ai` | `Agent::Loop` | Enter the AI agent TUI |
|
|
22
22
|
| `pwn-vault` | `PWN::Plugins::Vault` | Decrypt, edit `~/.pwn/pwn.yaml`, re-encrypt. Stays in the editor until `PWN::Config` accepts the file. |
|
|
23
23
|
| `pwn-asm` | `Plugins::Assembly` | Multiline asm ↔ opcodes workbench |
|
|
24
|
-
| `pwn-mesh` | `Meshtastic` gem | Meshtastic serial / MQTT client (Ruby ≥ 4.0;
|
|
24
|
+
| `pwn-mesh` | `Meshtastic` gem | Meshtastic serial / bluetooth / TCP / MQTT client (Ruby ≥ 4.0; `plugins.meshtastic.transport`). Inside the mesh TUI, leading-slash menus (`/channel`, `/transport`, `/device`, `/status`, `/help`, `/back`) configure the session locally and are not sent as mesh text. TAB completes those commands. |
|
|
25
25
|
| `pwn-ai-memory` | `PWN::Memory` | View/edit persistent memory |
|
|
26
26
|
| `pwn-ai-sessions` | `PWN::Sessions` | List/view/delete transcripts |
|
|
27
27
|
| `pwn-ai-cron` | `PWN::Cron` | List/run/toggle scheduled jobs |
|
|
@@ -34,6 +34,26 @@ commands.
|
|
|
34
34
|
| `toggle-pager` | Pry | Page long output on/off |
|
|
35
35
|
| `back` | - | Leave `pwn-ai` / `pwn-asm` / `pwn-mesh` sub-REPL |
|
|
36
36
|
|
|
37
|
+
## pwn-mesh slash commands
|
|
38
|
+
|
|
39
|
+
Typed at the mesh TX prompt (leading `/`). `/` or `/menu` opens a boxed curses menu (arrows/j/k, Enter, Esc). `/channel`, `/transport`, and `/device` without an argument open the same kind of list. Explicit `/channel LongFast` still jumps without a menu. Commands are local — they are not published as Meshtastic text. Changes update `PWN::Env[:plugins][:meshtastic]` and persist to `~/.pwn/pwn.yaml` when the vault decryptor is available; a live session reconnects so RX follows the new channel or radio.
|
|
40
|
+
|
|
41
|
+
| Command | Purpose |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `/help` | List mesh slash commands |
|
|
44
|
+
| `/status` | Show active transport, device, and channel |
|
|
45
|
+
| `/channel list` | List named `plugins.meshtastic.channel` blocks (`*` = active) |
|
|
46
|
+
| `/channel <name>` | Switch the active channel (e.g. `LongFast`) |
|
|
47
|
+
| `/transport list` | List `auto` / `serial` / `bluetooth` / `tcp` / `mqtt` |
|
|
48
|
+
| `/transport <kind>` | Pin a connection type (`auto` re-enables serial → bluetooth → tcp → mqtt probe) |
|
|
49
|
+
| `/device list` | List serial ports, BLE Meshtastic addresses, or the configured TCP/MQTT host |
|
|
50
|
+
| `/device <id>` | Switch radio for the active transport (`/dev/ttyACM0`, BLE MAC, `host:port`) |
|
|
51
|
+
| `/msg [!nodeid|channel] <text>` | Send a DM to `!nodeid`, or a broadcast on a named channel. Omit the target to reuse the last incoming DM sender or channel. A compose line without `/msg` goes to the active channel. Also `@!aabbccdd text` on a normal send. |
|
|
52
|
+
| `/toggle-dispatch-to-pwn-ai` | Off by default. When on, pwn-ai answers only if the source channel is encrypted, listed in `plugins.meshtastic.ai_whitelist`, and the text begins with `@ai`. DMs are allowed when they reside on a whitelisted channel. Uses `PWN::Env[:ai][:active]`. |
|
|
53
|
+
| `/back` | Leave pwn-mesh (same as `back` / CTRL+D) |
|
|
54
|
+
|
|
55
|
+
Typing `/` shows matching commands in the TX pane (ncurses hides Reline's dropdown). TAB still completes `/…` commands, channel names, transports, and device ids.
|
|
56
|
+
|
|
37
57
|
## Multi-line input
|
|
38
58
|
|
|
39
59
|
`pwn-ai` and `pwn-asm` use a custom `PWNMultiLineInput` reader. Plain
|
|
@@ -38,38 +38,42 @@ PWN::Plugins::REPL.ready_tty(opts)
|
|
|
38
38
|
- `refresh_ps1_proc`
|
|
39
39
|
- `add_commands`
|
|
40
40
|
- `add_hooks`
|
|
41
|
-
- `
|
|
41
|
+
- `leave_special_mode`
|
|
42
|
+
- `enable_autocomplete`
|
|
43
|
+
- `start`
|
|
44
|
+
- `authors`
|
|
45
|
+
- `help`
|
|
46
|
+
- `install_pwn_ai_completer!`
|
|
47
|
+
- `install_pwn_mesh_completer!`
|
|
48
|
+
- `leave_special_mode!`
|
|
49
|
+
- `mesh_menu_pick`
|
|
50
|
+
- `mesh_reset_input!`
|
|
51
|
+
- `persist_ai_selection`
|
|
52
|
+
- `persist_mesh_env`
|
|
53
|
+
- `pwn_ai_activation_session`
|
|
42
54
|
- `pwn_ai_complete`
|
|
43
55
|
- `pwn_ai_complete_command`
|
|
56
|
+
- `pwn_ai_complete_kind`
|
|
44
57
|
- `pwn_ai_complete_path`
|
|
45
58
|
- `pwn_ai_complete_ruby`
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
48
|
-
- `leave_special_mode`
|
|
49
|
-
- `pwn_ai_activation_session`
|
|
50
|
-
- `pwn_ai_profile_command`
|
|
51
|
-
- `pwn_ai_memory_command`
|
|
52
|
-
- `pwn_ai_dispatch_slash`
|
|
59
|
+
- `pwn_ai_dispatch_slash!`
|
|
60
|
+
- `pwn_ai_engine_model`
|
|
53
61
|
- `pwn_ai_engines`
|
|
54
|
-
- `pwn_ai_provider_class`
|
|
55
|
-
- `pwn_ai_model_ids`
|
|
56
62
|
- `pwn_ai_list_llms`
|
|
57
|
-
- `
|
|
58
|
-
- `
|
|
59
|
-
- `
|
|
63
|
+
- `pwn_ai_memory_command`
|
|
64
|
+
- `pwn_ai_model_ids`
|
|
65
|
+
- `pwn_ai_profile_command`
|
|
66
|
+
- `pwn_ai_provider_class`
|
|
60
67
|
- `pwn_ai_run_cron`
|
|
61
|
-
- `pwn_ai_run_sessions`
|
|
62
|
-
- `pwn_ai_run_memory`
|
|
63
68
|
- `pwn_ai_run_learning`
|
|
64
|
-
- `pwn_ai_run_skills`
|
|
65
69
|
- `pwn_ai_run_mcp`
|
|
66
|
-
- `
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
71
|
-
- `
|
|
72
|
-
- `
|
|
70
|
+
- `pwn_ai_run_memory`
|
|
71
|
+
- `pwn_ai_run_model`
|
|
72
|
+
- `pwn_ai_run_sessions`
|
|
73
|
+
- `pwn_ai_run_skills`
|
|
74
|
+
- `pwn_mesh_complete`
|
|
75
|
+
- `pwn_mesh_dispatch_slash!`
|
|
76
|
+
- `pwn_mesh_menu_rows`
|
|
73
77
|
- `ready_tty!`
|
|
74
78
|
- `restore_pwn_ai_completer!`
|
|
75
79
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-repl-ai
|
|
3
|
+
description: Drive PWN::Plugins::REPL::AI from pwn_eval.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
generated: true
|
|
9
|
+
module: PWN::Plugins::REPL::AI
|
|
10
|
+
source: pwn/plugins/repl/ai.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::REPL::AI
|
|
14
|
+
|
|
15
|
+
pwn-ai REPL mode.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
Call `PWN::Plugins::REPL::AI` from `pwn_eval` when the task needs this module.
|
|
20
|
+
Do not reimplement it in shell.
|
|
21
|
+
|
|
22
|
+
## Methodologies
|
|
23
|
+
|
|
24
|
+
Generated from `pwn/plugins/repl/ai.rb`. Prefer the public class methods below.
|
|
25
|
+
Class methods take `(opts = {})` and read `opts`.
|
|
26
|
+
|
|
27
|
+
## How to call
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
PWN::Plugins::REPL::AI.help
|
|
31
|
+
PWN::Plugins::REPL::AI.add_commands(opts)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Public methods
|
|
35
|
+
|
|
36
|
+
- `add_commands`
|
|
37
|
+
- `authors`
|
|
38
|
+
- `help`
|
|
39
|
+
|
|
40
|
+
## Source
|
|
41
|
+
|
|
42
|
+
`pwn/plugins/repl/ai.rb`
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
`PWN::Plugins::REPL::AI.respond_to?(:add_commands)` after the
|
|
47
|
+
module is loaded. Read the source for parameter names.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-repl-asm
|
|
3
|
+
description: Drive PWN::Plugins::REPL::ASM from pwn_eval.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
generated: true
|
|
9
|
+
module: PWN::Plugins::REPL::ASM
|
|
10
|
+
source: pwn/plugins/repl/asm.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::REPL::ASM
|
|
14
|
+
|
|
15
|
+
pwn-asm REPL mode.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
Call `PWN::Plugins::REPL::ASM` from `pwn_eval` when the task needs this module.
|
|
20
|
+
Do not reimplement it in shell.
|
|
21
|
+
|
|
22
|
+
## Methodologies
|
|
23
|
+
|
|
24
|
+
Generated from `pwn/plugins/repl/asm.rb`. Prefer the public class methods below.
|
|
25
|
+
Class methods take `(opts = {})` and read `opts`.
|
|
26
|
+
|
|
27
|
+
## How to call
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
PWN::Plugins::REPL::ASM.help
|
|
31
|
+
PWN::Plugins::REPL::ASM.add_commands(opts)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Public methods
|
|
35
|
+
|
|
36
|
+
- `add_commands`
|
|
37
|
+
- `authors`
|
|
38
|
+
- `help`
|
|
39
|
+
|
|
40
|
+
## Source
|
|
41
|
+
|
|
42
|
+
`pwn/plugins/repl/asm.rb`
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
`PWN::Plugins::REPL::ASM.respond_to?(:add_commands)` after the
|
|
47
|
+
module is loaded. Read the source for parameter names.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-repl-irc
|
|
3
|
+
description: Drive PWN::Plugins::REPL::IRC from pwn_eval.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
generated: true
|
|
9
|
+
module: PWN::Plugins::REPL::IRC
|
|
10
|
+
source: pwn/plugins/repl/irc.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::REPL::IRC
|
|
14
|
+
|
|
15
|
+
pwn-irc REPL mode.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
Call `PWN::Plugins::REPL::IRC` from `pwn_eval` when the task needs this module.
|
|
20
|
+
Do not reimplement it in shell.
|
|
21
|
+
|
|
22
|
+
## Methodologies
|
|
23
|
+
|
|
24
|
+
Generated from `pwn/plugins/repl/irc.rb`. Prefer the public class methods below.
|
|
25
|
+
Class methods take `(opts = {})` and read `opts`.
|
|
26
|
+
|
|
27
|
+
## How to call
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
PWN::Plugins::REPL::IRC.help
|
|
31
|
+
PWN::Plugins::REPL::IRC.add_commands(opts)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Public methods
|
|
35
|
+
|
|
36
|
+
- `add_commands`
|
|
37
|
+
- `authors`
|
|
38
|
+
- `help`
|
|
39
|
+
|
|
40
|
+
## Source
|
|
41
|
+
|
|
42
|
+
`pwn/plugins/repl/irc.rb`
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
`PWN::Plugins::REPL::IRC.respond_to?(:add_commands)` after the
|
|
47
|
+
module is loaded. Read the source for parameter names.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-repl-mesh
|
|
3
|
+
description: Drive PWN::Plugins::REPL::Mesh from pwn_eval.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
generated: true
|
|
9
|
+
module: PWN::Plugins::REPL::Mesh
|
|
10
|
+
source: pwn/plugins/repl/mesh.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::REPL::Mesh
|
|
14
|
+
|
|
15
|
+
pwn-mesh REPL mode.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
Call `PWN::Plugins::REPL::Mesh` from `pwn_eval` when the task needs this module.
|
|
20
|
+
Do not reimplement it in shell.
|
|
21
|
+
|
|
22
|
+
## Methodologies
|
|
23
|
+
|
|
24
|
+
Generated from `pwn/plugins/repl/mesh.rb`. Prefer the public class methods below.
|
|
25
|
+
Class methods take `(opts = {})` and read `opts`.
|
|
26
|
+
|
|
27
|
+
## How to call
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
PWN::Plugins::REPL::Mesh.help
|
|
31
|
+
PWN::Plugins::REPL::Mesh.add_commands(opts)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Public methods
|
|
35
|
+
|
|
36
|
+
- `add_commands`
|
|
37
|
+
- `authors`
|
|
38
|
+
- `help`
|
|
39
|
+
|
|
40
|
+
## Source
|
|
41
|
+
|
|
42
|
+
`pwn/plugins/repl/mesh.rb`
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
`PWN::Plugins::REPL::Mesh.respond_to?(:add_commands)` after the
|
|
47
|
+
module is loaded. Read the source for parameter names.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-repl-vault
|
|
3
|
+
description: Drive PWN::Plugins::REPL::Vault from pwn_eval.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
generated: true
|
|
9
|
+
module: PWN::Plugins::REPL::Vault
|
|
10
|
+
source: pwn/plugins/repl/vault.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::REPL::Vault
|
|
14
|
+
|
|
15
|
+
pwn-vault REPL mode.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
Call `PWN::Plugins::REPL::Vault` from `pwn_eval` when the task needs this module.
|
|
20
|
+
Do not reimplement it in shell.
|
|
21
|
+
|
|
22
|
+
## Methodologies
|
|
23
|
+
|
|
24
|
+
Generated from `pwn/plugins/repl/vault.rb`. Prefer the public class methods below.
|
|
25
|
+
Class methods take `(opts = {})` and read `opts`.
|
|
26
|
+
|
|
27
|
+
## How to call
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
PWN::Plugins::REPL::Vault.help
|
|
31
|
+
PWN::Plugins::REPL::Vault.add_commands(opts)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Public methods
|
|
35
|
+
|
|
36
|
+
- `add_commands`
|
|
37
|
+
- `authors`
|
|
38
|
+
- `help`
|
|
39
|
+
|
|
40
|
+
## Source
|
|
41
|
+
|
|
42
|
+
`pwn/plugins/repl/vault.rb`
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
`PWN::Plugins::REPL::Vault.respond_to?(:add_commands)` after the
|
|
47
|
+
module is loaded. Read the source for parameter names.
|
data/lib/pwn/ai/agent/loop.rb
CHANGED
|
@@ -705,6 +705,11 @@ module PWN
|
|
|
705
705
|
artifact[:unmet].each { |row| unmet << "#{row[:criterion]}:#{row[:detail]}" }
|
|
706
706
|
trace_files = session_files(messages: opts[:messages])
|
|
707
707
|
unmet << 'issue_work_proofs' if contract[:issue_work] && Array(contract[:proofs]).empty? && files.empty? && trace_files.empty?
|
|
708
|
+
if contract[:issue_work] && defined?(PWN::Plugins::Findings)
|
|
709
|
+
gaps = PWN::Plugins::Findings.issue_work_gaps(session_id: Thread.current[:pwn_session_id].to_s)
|
|
710
|
+
unmet << 'issue_work_unverified' if Array(gaps[:recorded]).any? && Array(gaps[:reproduced]).empty?
|
|
711
|
+
unmet << 'issue_work_unchained' if gaps[:unchained]
|
|
712
|
+
end
|
|
708
713
|
unmet << 'skills' if declared_skills_missing?(skills: contract[:skills], request: request)
|
|
709
714
|
blob = evidence_blob(messages: opts[:messages], files: files + trace_files)
|
|
710
715
|
asked_hosts = Array(contract[:hosts]).select { |host| request.downcase.include?(host.to_s.downcase) }
|
|
@@ -2787,7 +2792,8 @@ module PWN
|
|
|
2787
2792
|
bound = operator_bound_refusal(from: opts[:from] || opts[:account])
|
|
2788
2793
|
return bound if bound
|
|
2789
2794
|
|
|
2790
|
-
engine =
|
|
2795
|
+
engine = opts[:engine]
|
|
2796
|
+
engine = engine.to_s.empty? ? active_engine : engine.to_s.downcase.to_sym
|
|
2791
2797
|
local = local_engine?(engine: engine)
|
|
2792
2798
|
|
|
2793
2799
|
# Cheap intent/kind FIRST - before PromptBuilder / Registry / TaskSummarizer
|
|
@@ -3366,6 +3372,7 @@ module PWN
|
|
|
3366
3372
|
account: 'optional - operator account id to bind',
|
|
3367
3373
|
force_tools: 'optional - force tools value consumed by #run',
|
|
3368
3374
|
nested: 'optional - true for Swarm/child Loop.run (skip RN footer)',
|
|
3375
|
+
engine: 'optional - provider name; defaults to PWN::Env ai.active',
|
|
3369
3376
|
core_only: 'optional - restrict to CORE_TOOLS when true',
|
|
3370
3377
|
trace: 'optional - enable TracePoint debug for this run',
|
|
3371
3378
|
debug_tee: 'optional - IO to tee debug logs'
|
data/lib/pwn/ai/agent/swarm.rb
CHANGED
|
@@ -34,6 +34,33 @@ module PWN
|
|
|
34
34
|
DEFAULT_TAIL = 12
|
|
35
35
|
DEFAULT_TOOLSET = %w[terminal pwn memory skills sessions learning
|
|
36
36
|
metrics extrospection].freeze
|
|
37
|
+
SPECIALIST_ROLES = {
|
|
38
|
+
recon: {
|
|
39
|
+
role: 'Map the operator-named target. Record assets and working probes. Do not invent hosts.',
|
|
40
|
+
skills: %w[osint penetration-testing],
|
|
41
|
+
toolsets: %w[pwn terminal extrospection http]
|
|
42
|
+
},
|
|
43
|
+
authz: {
|
|
44
|
+
role: 'Test authorization and IDOR on discovered assets. Reproduce with request/response PoCs.',
|
|
45
|
+
skills: %w[web-application-penetration-testing],
|
|
46
|
+
toolsets: %w[pwn terminal http]
|
|
47
|
+
},
|
|
48
|
+
injection: {
|
|
49
|
+
role: 'Test injection on discovered inputs. Attach a working PoC, not a scanner signature.',
|
|
50
|
+
skills: %w[web-application-penetration-testing deep-exploitation],
|
|
51
|
+
toolsets: %w[pwn terminal http]
|
|
52
|
+
},
|
|
53
|
+
xss: {
|
|
54
|
+
role: 'Test XSS and CSRF in a real browser session. Evidence is a reproduced payload, not a pattern hit.',
|
|
55
|
+
skills: %w[web-application-penetration-testing],
|
|
56
|
+
toolsets: %w[pwn terminal http]
|
|
57
|
+
},
|
|
58
|
+
business_logic: {
|
|
59
|
+
role: 'Chain authorization, injection, and workflow flaws into one evidenced impact path.',
|
|
60
|
+
skills: %w[web-application-penetration-testing bug-bounty-hunting],
|
|
61
|
+
toolsets: %w[pwn terminal http]
|
|
62
|
+
}
|
|
63
|
+
}.freeze
|
|
37
64
|
|
|
38
65
|
# ------------------------------------------------------------------
|
|
39
66
|
# Persona registry (~/.pwn/agents.yml)
|
|
@@ -524,6 +551,33 @@ module PWN
|
|
|
524
551
|
{ name: name, skills: skills, toolsets: toolsets }
|
|
525
552
|
end
|
|
526
553
|
|
|
554
|
+
# Catalog of lead-spawned offensive specialists (recon through business logic).
|
|
555
|
+
|
|
556
|
+
public_class_method def self.specialist_roles(opts = {})
|
|
557
|
+
name = opts[:name].to_s
|
|
558
|
+
return SPECIALIST_ROLES.fetch(name.to_sym) unless name.empty?
|
|
559
|
+
|
|
560
|
+
SPECIALIST_ROLES
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# Write ephemeral specialist personas for a swarm without running Loop.
|
|
564
|
+
|
|
565
|
+
public_class_method def self.ensure_specialists(opts = {})
|
|
566
|
+
sid = opts[:swarm_id].to_s
|
|
567
|
+
raise ArgumentError, 'swarm_id is required' if sid.empty?
|
|
568
|
+
|
|
569
|
+
SPECIALIST_ROLES.map do |name, spec|
|
|
570
|
+
spawn(
|
|
571
|
+
name: name.to_s,
|
|
572
|
+
role: spec[:role],
|
|
573
|
+
skills: spec[:skills],
|
|
574
|
+
toolsets: spec[:toolsets],
|
|
575
|
+
swarm_id: sid,
|
|
576
|
+
ephemeral: true
|
|
577
|
+
)
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
|
|
527
581
|
public_class_method def self.child_inbox(opts = {})
|
|
528
582
|
sid = opts[:session_id].to_s
|
|
529
583
|
findings = if defined?(PWN::Plugins::Findings)
|
|
@@ -737,6 +791,16 @@ module PWN
|
|
|
737
791
|
orchestrator: 'optional - true to keep the swarm toolset'
|
|
738
792
|
)
|
|
739
793
|
|
|
794
|
+
# Catalog of lead-spawned specialists (recon, authz, injection, xss, business_logic).
|
|
795
|
+
#{self}.specialist_roles(
|
|
796
|
+
name: 'optional - one role name; omit to return the full catalog'
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
# Write ephemeral specialist personas for a swarm without running Loop.
|
|
800
|
+
#{self}.ensure_specialists(
|
|
801
|
+
swarm_id: 'required - swarm id from #create'
|
|
802
|
+
)
|
|
803
|
+
|
|
740
804
|
# World-object inbox for a child session (finding ids, artifact shas).
|
|
741
805
|
#{self}.child_inbox(
|
|
742
806
|
session_id: 'optional - child PWN::Sessions id',
|
|
@@ -28,9 +28,18 @@ PWN::AI::Agent::Registry.register(
|
|
|
28
28
|
poc: { type: 'string' },
|
|
29
29
|
poc_artifacts: { type: 'array', items: { type: 'string' } },
|
|
30
30
|
session_id: { type: 'string' },
|
|
31
|
-
op: { type: 'string' },
|
|
31
|
+
op: { type: 'string', enum: %w[query export chain record verify retest chain_impact gaps] },
|
|
32
32
|
parent_id: { type: 'string' },
|
|
33
|
-
engagement_id: { type: 'string' }
|
|
33
|
+
engagement_id: { type: 'string' },
|
|
34
|
+
kind: { type: 'string', enum: %w[http script] },
|
|
35
|
+
impact: { type: 'string' },
|
|
36
|
+
request_path: { type: 'string' },
|
|
37
|
+
response_path: { type: 'string' },
|
|
38
|
+
execution_log: { type: 'string' },
|
|
39
|
+
ids: { type: 'array', items: { type: 'string' } },
|
|
40
|
+
combined_impact_path: { type: 'string' },
|
|
41
|
+
escalate: { type: 'boolean' },
|
|
42
|
+
combined_severity: { type: 'string' }
|
|
34
43
|
},
|
|
35
44
|
required: %w[],
|
|
36
45
|
anyOf: [
|
|
@@ -52,6 +61,14 @@ PWN::AI::Agent::Registry.register(
|
|
|
52
61
|
PWN::Plugins::Findings.record_structured(args.merge(attack_chain_refs: (Array(args[:attack_chain_refs]) + [parent]).uniq))
|
|
53
62
|
when 'export'
|
|
54
63
|
PWN::Plugins::Findings.render(args)
|
|
64
|
+
when 'verify'
|
|
65
|
+
PWN::Plugins::Findings.verify(args)
|
|
66
|
+
when 'retest'
|
|
67
|
+
PWN::Plugins::Findings.retest(args)
|
|
68
|
+
when 'chain_impact'
|
|
69
|
+
PWN::Plugins::Findings.chain_impact(args)
|
|
70
|
+
when 'gaps'
|
|
71
|
+
PWN::Plugins::Findings.issue_work_gaps(args)
|
|
55
72
|
when 'record'
|
|
56
73
|
PWN::Plugins::Findings.record_structured(args)
|
|
57
74
|
else
|