pwn 0.5.723 → 0.5.725
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/Gemfile +3 -3
- data/bin/pwn-ai +6 -0
- data/bin/pwn-capd +5 -0
- data/documentation/AI-Integration.md +3 -2
- data/documentation/Agent-Tool-Registry.md +3 -1
- data/documentation/Capability-Broker-Sandbox.md +97 -0
- data/documentation/Policy-Benchmark.md +149 -4
- data/documentation/Proxy-Skills-Hygiene.md +102 -0
- data/documentation/Recon-Findings-API.md +31 -0
- data/documentation/Reinforcement-Learning.md +54 -2
- data/documentation/Swarm.md +38 -3
- data/etc/default_skills/pwn/ai/agent/engagement_memory/SKILL.md +46 -0
- data/etc/default_skills/pwn/ai/agent/manifest/SKILL.md +49 -0
- data/etc/default_skills/pwn/ai/agent/policy/SKILL.md +3 -1
- data/etc/default_skills/pwn/ai/agent/policy_evaluation/SKILL.md +49 -0
- data/etc/default_skills/pwn/ai/agent/profiles/SKILL.md +46 -0
- data/etc/default_skills/pwn/ai/agent/registry/SKILL.md +2 -0
- data/etc/default_skills/pwn/ai/agent/request_runtime/SKILL.md +46 -0
- data/etc/default_skills/pwn/ai/agent/reward/SKILL.md +1 -0
- data/etc/default_skills/pwn/ai/agent/skill_consolidation/SKILL.md +48 -0
- data/etc/default_skills/pwn/ai/agent/tool_guard/SKILL.md +3 -2
- data/etc/default_skills/pwn/ai/agent/tools/http_replay/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/tools/sandbox/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/tools/skills_consolidate/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/turn_finalizer/SKILL.md +3 -0
- data/etc/default_skills/pwn/ai/agent/verification/SKILL.md +48 -0
- data/etc/default_skills/pwn/ai/cli/SKILL.md +48 -0
- data/etc/default_skills/pwn/ai/context/SKILL.md +2 -0
- data/etc/default_skills/pwn/ai/context_ingestion/SKILL.md +56 -0
- data/etc/default_skills/pwn/ai/context_ingestion/references/urls.md +3 -0
- data/etc/default_skills/pwn/plugins/binary_analysis/SKILL.md +50 -0
- data/etc/default_skills/pwn/plugins/capability_broker/SKILL.md +47 -0
- data/etc/default_skills/pwn/plugins/findings/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/mitm_proxy/SKILL.md +56 -0
- data/etc/default_skills/pwn/plugins/mitm_proxy/references/urls.md +3 -0
- data/etc/default_skills/pwn/plugins/process_tube/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/radare2/SKILL.md +4 -0
- data/etc/default_skills/pwn/plugins/recon/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/repl/SKILL.md +5 -0
- data/etc/default_skills/pwn/plugins/rop/SKILL.md +49 -0
- data/etc/default_skills/pwn/plugins/sandbox/SKILL.md +50 -0
- data/etc/default_skills/pwn/plugins/vault/SKILL.md +4 -2
- data/etc/default_skills/pwn/redaction/SKILL.md +48 -0
- data/etc/default_skills/pwn/reports/SKILL.md +1 -0
- data/etc/default_skills/pwn/session_trace/SKILL.md +50 -0
- data/lib/pwn/ai/agent/dispatch.rb +143 -6
- data/lib/pwn/ai/agent/engagement_memory.rb +137 -0
- data/lib/pwn/ai/agent/learning.rb +2 -1
- data/lib/pwn/ai/agent/loop.rb +131 -57
- data/lib/pwn/ai/agent/manifest.rb +178 -0
- data/lib/pwn/ai/agent/mistakes.rb +38 -3
- data/lib/pwn/ai/agent/policy.rb +148 -35
- data/lib/pwn/ai/agent/policy_evaluation.rb +230 -0
- data/lib/pwn/ai/agent/profiles.rb +103 -0
- data/lib/pwn/ai/agent/registry.rb +45 -3
- data/lib/pwn/ai/agent/request_runtime.rb +94 -0
- data/lib/pwn/ai/agent/reward.rb +83 -24
- data/lib/pwn/ai/agent/skill_consolidation.rb +151 -0
- data/lib/pwn/ai/agent/swarm.rb +11 -25
- data/lib/pwn/ai/agent/tool_guard.rb +27 -2
- data/lib/pwn/ai/agent/tools/finding_record.rb +27 -23
- data/lib/pwn/ai/agent/tools/http_replay.rb +23 -0
- data/lib/pwn/ai/agent/tools/recon_run.rb +27 -0
- data/lib/pwn/ai/agent/tools/ruby_eval.rb +4 -8
- data/lib/pwn/ai/agent/tools/sandbox.rb +28 -0
- data/lib/pwn/ai/agent/tools/shell.rb +17 -27
- data/lib/pwn/ai/agent/tools/skills_consolidate.rb +34 -0
- data/lib/pwn/ai/agent/tools/swarm.rb +9 -3
- data/lib/pwn/ai/agent/turn_finalizer.rb +88 -19
- data/lib/pwn/ai/agent/verification.rb +202 -0
- data/lib/pwn/ai/agent.rb +10 -3
- data/lib/pwn/ai/cli.rb +91 -0
- data/lib/pwn/ai/context.rb +7 -0
- data/lib/pwn/ai/context_ingestion.rb +213 -0
- data/lib/pwn/ai/gemini.rb +6 -2
- data/lib/pwn/ai/grok.rb +12 -1
- data/lib/pwn/ai/ollama.rb +3 -1
- data/lib/pwn/ai/open_ai.rb +56 -8
- data/lib/pwn/ai/open_web_ui.rb +3 -1
- data/lib/pwn/ai/tools/README.md +74 -0
- data/lib/pwn/ai/tools/core.yaml +20 -0
- data/lib/pwn/ai/tools/host.yaml +10 -0
- data/lib/pwn/ai.rb +1 -0
- data/lib/pwn/config.rb +7 -0
- data/lib/pwn/migrate.rb +21 -1
- data/lib/pwn/plugins/binary_analysis.rb +104 -0
- data/lib/pwn/plugins/capability_broker/daemon.py +145 -0
- data/lib/pwn/plugins/capability_broker.rb +52 -0
- data/lib/pwn/plugins/findings.rb +108 -10
- data/lib/pwn/plugins/ghidra_headless.rb +52 -33
- data/lib/pwn/plugins/ghidra_scripts/PwnExport.java +62 -0
- data/lib/pwn/plugins/log.rb +30 -11
- data/lib/pwn/plugins/mitm_proxy.rb +282 -0
- data/lib/pwn/plugins/monkey_patch.rb +5 -1
- data/lib/pwn/plugins/packet.rb +32 -129
- data/lib/pwn/plugins/process_tube.rb +12 -0
- data/lib/pwn/plugins/pwn_logger.rb +2 -1
- data/lib/pwn/plugins/radare2.rb +121 -0
- data/lib/pwn/plugins/recon.rb +173 -0
- data/lib/pwn/plugins/repl.rb +174 -60
- data/lib/pwn/plugins/rop.rb +103 -0
- data/lib/pwn/plugins/sandbox/Dockerfile +5 -0
- data/lib/pwn/plugins/sandbox/driver.py +187 -0
- data/lib/pwn/plugins/sandbox.rb +68 -0
- data/lib/pwn/plugins/transparent_browser.rb +11 -0
- data/lib/pwn/plugins/vault.rb +88 -21
- data/lib/pwn/plugins.rb +5 -0
- data/lib/pwn/redaction.rb +70 -0
- data/lib/pwn/reports/html.rb +9 -0
- data/lib/pwn/reports/json.rb +2 -1
- data/lib/pwn/reports/markdown.rb +6 -0
- data/lib/pwn/reports.rb +42 -0
- data/lib/pwn/session_trace.rb +163 -0
- data/lib/pwn/sessions.rb +17 -32
- data/lib/pwn/setup.rb +4 -0
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn.rb +2 -0
- data/scripts/benchmark_policy.rb +115 -15
- data/spec/integration/dispatch_spec.rb +2 -2
- data/spec/integration/reinforced_feedback_loop_spec.rb +13 -6
- data/spec/lib/pwn/ai/agent/dispatch_policy_spec.rb +142 -0
- data/spec/lib/pwn/ai/agent/engagement_memory_spec.rb +74 -0
- data/spec/lib/pwn/ai/agent/learning_spec.rb +19 -0
- data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +48 -0
- data/spec/lib/pwn/ai/agent/loop_spec.rb +139 -6
- data/spec/lib/pwn/ai/agent/manifest_spec.rb +46 -0
- data/spec/lib/pwn/ai/agent/mistakes_precision_spec.rb +57 -0
- data/spec/lib/pwn/ai/agent/policy_evaluation_spec.rb +221 -0
- data/spec/lib/pwn/ai/agent/policy_spec.rb +145 -8
- data/spec/lib/pwn/ai/agent/profiles_spec.rb +49 -0
- data/spec/lib/pwn/ai/agent/registry_spec.rb +64 -3
- data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +55 -0
- data/spec/lib/pwn/ai/agent/reward_spec.rb +102 -2
- data/spec/lib/pwn/ai/agent/skill_consolidation_spec.rb +64 -0
- data/spec/lib/pwn/ai/agent/swarm_spec.rb +158 -0
- data/spec/lib/pwn/ai/agent/tool_guard_spec.rb +8 -0
- data/spec/lib/pwn/ai/agent/tools/finding_record_spec.rb +9 -0
- data/spec/lib/pwn/ai/agent/tools/http_replay_spec.rb +22 -0
- data/spec/lib/pwn/ai/agent/tools/recon_run_spec.rb +15 -0
- data/spec/lib/pwn/ai/agent/tools/sandbox_spec.rb +15 -0
- data/spec/lib/pwn/ai/agent/tools/shell_spec.rb +2 -2
- data/spec/lib/pwn/ai/agent/tools/skills_consolidate_spec.rb +22 -0
- data/spec/lib/pwn/ai/agent/tools/swarm_spec.rb +14 -0
- data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +55 -1
- data/spec/lib/pwn/ai/agent/verification_spec.rb +172 -0
- data/spec/lib/pwn/ai/cli_spec.rb +102 -0
- data/spec/lib/pwn/ai/context_ingestion_spec.rb +80 -0
- data/spec/lib/pwn/ai/gemini_spec.rb +19 -0
- data/spec/lib/pwn/ai/open_ai_oauth_transport_spec.rb +35 -0
- data/spec/lib/pwn/ai/open_ai_spec.rb +97 -0
- data/spec/lib/pwn/config_spec.rb +10 -0
- data/spec/lib/pwn/migrate_spec.rb +28 -0
- data/spec/lib/pwn/plugins/binary_analysis_spec.rb +41 -0
- data/spec/lib/pwn/plugins/capability_broker_spec.rb +15 -0
- data/spec/lib/pwn/plugins/capd_test.py +27 -0
- data/spec/lib/pwn/plugins/findings_spec.rb +2 -1
- data/spec/lib/pwn/plugins/findings_structured_spec.rb +72 -0
- data/spec/lib/pwn/plugins/log_spec.rb +3 -3
- data/spec/lib/pwn/plugins/mitm_proxy_spec.rb +78 -0
- data/spec/lib/pwn/plugins/monkey_patch_spec.rb +7 -0
- data/spec/lib/pwn/plugins/packet_spec.rb +8 -0
- data/spec/lib/pwn/plugins/process_tube_spec.rb +7 -0
- data/spec/lib/pwn/plugins/recon_pipeline_spec.rb +129 -0
- data/spec/lib/pwn/plugins/repl_spec.rb +121 -0
- data/spec/lib/pwn/plugins/rop_spec.rb +14 -0
- data/spec/lib/pwn/plugins/sandbox_spec.rb +21 -0
- data/spec/lib/pwn/plugins/sandbox_test.py +47 -0
- data/spec/lib/pwn/plugins/transparent_browser_spec.rb +28 -0
- data/spec/lib/pwn/plugins/vault_credentials_spec.rb +54 -0
- data/spec/lib/pwn/redaction_spec.rb +126 -0
- data/spec/lib/pwn/session_trace_spec.rb +78 -0
- data/spec/spec_helper.rb +2 -0
- data/third_party/pwn_rdoc.jsonl +154 -10
- metadata +91 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ada4e79d8d8864ce2ea48b7378b4d10e124e7925d050424d3ef10e269438d5f
|
|
4
|
+
data.tar.gz: 714c044b1ab48bf33ca175d73fee3997d0788ed9d34e6c124d59c7fdf630c01e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2472a6aa034cd281f4b2b67804558b7f40827abfca843857b7f713d1c492a9ea1e3c2bfd03282134bc2d2270e1db77cdc7c20876b0f4baf58d5b36c929ba536f
|
|
7
|
+
data.tar.gz: e7550328388a59e8d4047bceef0da28446850c2b0d40670930e104ea3ca7a89559aa65db510bd2d2e60f02053f85a98890a5bd96fee7d0435d1a53c845a5fe44
|
data/Gemfile
CHANGED
|
@@ -49,7 +49,7 @@ gem 'jwt', '3.2.0'
|
|
|
49
49
|
gem 'libusb', '0.8.0'
|
|
50
50
|
gem 'luhn', '3.0.0'
|
|
51
51
|
gem 'mail', '2.9.1'
|
|
52
|
-
gem 'mcp', '1.5.
|
|
52
|
+
gem 'mcp', '1.5.1'
|
|
53
53
|
gem 'meshtastic', '0.0.175'
|
|
54
54
|
gem 'metasm', '1.0.6'
|
|
55
55
|
gem 'mongo', '2.25.0'
|
|
@@ -89,8 +89,8 @@ gem 'ruby-nmap', '1.0.3'
|
|
|
89
89
|
gem 'ruby-saml', '1.18.1'
|
|
90
90
|
gem 'rvm', '1.11.3.9'
|
|
91
91
|
gem 'savon', '2.17.4'
|
|
92
|
-
gem 'selenium-devtools', '0.
|
|
93
|
-
gem 'selenium-webdriver', '4.
|
|
92
|
+
gem 'selenium-devtools', '0.153.0'
|
|
93
|
+
gem 'selenium-webdriver', '4.49.0'
|
|
94
94
|
gem 'slack-ruby-client', '3.2.0'
|
|
95
95
|
gem 'socksify', '1.8.1'
|
|
96
96
|
gem 'spreadsheet', '1.3.5'
|
data/bin/pwn-ai
ADDED
data/bin/pwn-capd
ADDED
|
@@ -113,8 +113,9 @@ puts resp[:content]
|
|
|
113
113
|
|
|
114
114
|
## Model diversity in Swarm
|
|
115
115
|
|
|
116
|
-
Because each persona in [`agents.yml`](Swarm.md) can override `engine
|
|
117
|
-
`agent_debate` can
|
|
116
|
+
Because each persona in [`agents.yml`](Swarm.md) can override `engine:` and
|
|
117
|
+
`model:`, an `agent_debate` can compare different models on the same provider
|
|
118
|
+
or several *different providers* against each other - real
|
|
118
119
|
antagonism, not one model role-playing three voices. The same mechanism backs
|
|
119
120
|
`ai.agent.escalation_persona`: when a local model is stuck, `Loop.run` asks a
|
|
120
121
|
frontier persona for a 3-line corrective hint and injects it as a synthetic
|
|
@@ -10,10 +10,11 @@ toolsets; the JSON-Schema for each tool is what the model actually sees.
|
|
|
10
10
|
|
|
11
11
|
| Toolset | Tools | Backed by |
|
|
12
12
|
|---|---|---|
|
|
13
|
+
| `http` | `http_proxy_start` · `http_proxy_stop` · `http_proxy_entries` · `http_proxy_rules` · `http_replay` | `PWN::Plugins::MitmProxy`: native HTTP HAR capture/replay; opaque CONNECT tunnels |
|
|
13
14
|
| `terminal` | `shell` | `Open3.capture3` on the host, after `PWN::AI::Agent::ToolGuard` |
|
|
14
15
|
| `pwn` | `pwn_eval` | `TOPLEVEL_BINDING.eval` in the live REPL process, after `ToolGuard` |
|
|
15
16
|
| `memory` | `memory_remember` · `memory_recall` · `memory_forget` · `memory_clear` · **`memory_lean`** | `PWN::Memory` → `~/.pwn/memory.json` |
|
|
16
|
-
| `skills` | **`skills_recall`** · `skill_list` · `skill_view` · `skill_create` · `skill_add_reference` · `skill_delete` · `skill_migrate_legacy` | `~/.pwn/skills/<name>/SKILL.md` (**[agentskills.io](https://agentskills.io) spec**; legacy flat `*.md` auto-migrated) |
|
|
17
|
+
| `skills` | `skills_consolidate` · **`skills_recall`** · `skill_list` · `skill_view` · `skill_create` · `skill_add_reference` · `skill_delete` · `skill_migrate_legacy` | `~/.pwn/skills/<name>/SKILL.md` (**[agentskills.io](https://agentskills.io) spec**; legacy flat `*.md` auto-migrated) |
|
|
17
18
|
| `sessions` | **`session_recall`** · `sessions_list` · `sessions_view` · `sessions_current` · `sessions_delete` · `sessions_stats` · **`sessions_lean`** | `PWN::Sessions` → `~/.pwn/sessions/` |
|
|
18
19
|
| `learning` | `learning_note_outcome` · `learning_reflect` · `learning_distill_skill` · `learning_stats` · `learning_outcomes` · `learning_consolidate` · `learning_reset` · `learning_auto_introspect_toggle` · **`learning_gc_stores`** · **`learning_purge_noise`** · **`mistakes_list`** · **`mistakes_record`** · **`mistakes_resolve`** · **`mistakes_reset`** · **`mistakes_lean`** · **`reward_judge`** · **`reward_prm`** · **`reward_sentinel`** · **`reward_preferences`** · **`reward_export_dpo`** · **`reward_warm_sentinel`** · **`reward_scrub_preferences`** · **`reward_preference_balance`** · **`curriculum_practice`** · **`curriculum_train`** · **`curriculum_hindsight`** · **`curriculum_offline_judge`** · **`curriculum_preference_balance`** | `PWN::AI::Agent::Learning` + `Mistakes` + `Reward` + `Curriculum` → `~/.pwn/learning.jsonl` + `~/.pwn/mistakes.json` + `~/.pwn/preferences.jsonl` + `~/.pwn/curriculum/` + `~/.pwn/finetune/` |
|
|
19
20
|
| `reward` | **`reward_generator_mix`** | `PWN::AI::Agent::Reward.generator_mix` → online preference source-mix controller (`preferences.jsonl`) |
|
|
@@ -23,6 +24,7 @@ toolsets; the JSON-Schema for each tool is what the model actually sees.
|
|
|
23
24
|
| `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` |
|
|
24
25
|
| `cron` | `cron_list` · `cron_create` · `cron_run` · `cron_enable` · `cron_disable` · `cron_remove` | `PWN::Cron` → `~/.pwn/cron/jobs.yml` |
|
|
25
26
|
| `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
|
+
| `manifest` | `host_os_type` | YAML tool declarations in `lib/pwn/ai/tools/*.yaml` via `PWN::AI::Agent::Manifest` |
|
|
26
28
|
|
|
27
29
|
The `learning` toolset is deliberately large: **Mistakes** (negative feedback),
|
|
28
30
|
**Reward** (outcome and process judges, sentinel, preference ledger) and
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Capability broker and disposable sandbox
|
|
2
|
+
|
|
3
|
+
## pwn-capd (P8)
|
|
4
|
+
|
|
5
|
+
The Python stdlib helper speaks one bounded JSON request per Unix connection.
|
|
6
|
+
Linux SO_PEERCRED authenticates the configured caller UID; the client also
|
|
7
|
+
checks the daemon UID. The socket is mode 0600. Only explicitly configured
|
|
8
|
+
interfaces are accepted. Operations: `status`, `raw_send` (base64 Ethernet
|
|
9
|
+
frame), `capture` (bounded in-memory pcap), `arp` / `nd` (typed IP kernel
|
|
10
|
+
neighbor-cache queries). There is no command endpoint, arbitrary privileged
|
|
11
|
+
output path, firewall mutation, or implicit sudo. ARP/ND currently query cache;
|
|
12
|
+
they do not actively solicit neighbors or mutate entries.
|
|
13
|
+
|
|
14
|
+
Operator installation (NOT executed by the application): install
|
|
15
|
+
`lib/pwn/plugins/capability_broker/daemon.py` (not the Ruby packaging driver)
|
|
16
|
+
root-owned under `/usr/local/libexec/pwn-capd`, mode 0755, in a root-owned
|
|
17
|
+
non-writable directory. Do **not** setcap the system Ruby/Python interpreter
|
|
18
|
+
or this script: Linux ignores script file capabilities, and granting a shared
|
|
19
|
+
interpreter network privileges exposes every script. Use a dedicated systemd
|
|
20
|
+
service with `User=<operator>`, `AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN`,
|
|
21
|
+
`CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN`, `NoNewPrivileges=yes`,
|
|
22
|
+
`ProtectSystem=strict`, `ProtectHome=yes`, `RuntimeDirectory=pwn-capd`,
|
|
23
|
+
`RestrictAddressFamilies=AF_UNIX AF_PACKET AF_NETLINK`, and the reviewed helper
|
|
24
|
+
as ExecStart. The socket owner and configured peer UID must match that operator.
|
|
25
|
+
Enable a preconfigured unit with `sudo systemctl start pwn-capd`.
|
|
26
|
+
For an audited one-shot sudo launch use:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
sudo /usr/local/libexec/pwn-capd --uid "$(id -u)" --interface lo
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Never authorize unrestricted sudo access to arbitrary helper arguments. Pin
|
|
33
|
+
UID, socket and interface in sudoers. Only add engagement interfaces after
|
|
34
|
+
operator review. CAP_NET_ADMIN is reserved for future neighbor management;
|
|
35
|
+
current operations need CAP_NET_RAW, while neighbor queries are read-only.
|
|
36
|
+
|
|
37
|
+
Ruby API (explicit require until central autoload is integrated):
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
require 'pwn/plugins/capability_broker'
|
|
41
|
+
PWN::Plugins::CapabilityBroker.request(operation: 'status')
|
|
42
|
+
PWN::Plugins::CapabilityBroker.request(operation: 'arp', iface: 'lo', address: '127.0.0.1')
|
|
43
|
+
PWN::Plugins::CapabilityBroker.request(operation: 'nd', iface: 'lo', address: '::1')
|
|
44
|
+
# Packet.send(pkt:, iface:, socket:) and Packet.capture(path:, count:, timeout:, iface:, socket:)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`PWN_CAPD_SOCKET` overrides `/run/pwn-capd/control.sock`. Capture artifacts are
|
|
48
|
+
written and read back by the unprivileged client, never by the daemon.
|
|
49
|
+
Absent broker returns degraded error and missing local CAP_NET_RAW accurately;
|
|
50
|
+
there is no fabricated capture path or privileged Ruby remediation.
|
|
51
|
+
|
|
52
|
+
## Sandbox (P13)
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
require 'pwn/plugins/sandbox'
|
|
56
|
+
PWN::Plugins::Sandbox.run(binary: '/path/safe-fixture', argv: [], stdin: '', timeout: 10, memory_mb: 256)
|
|
57
|
+
PWN::Plugins::Sandbox.fuzz(target: '/path/safe-fixture', corpus: '/path/seeds', minutes: 1, seed: 0)
|
|
58
|
+
s = PWN::Plugins::Sandbox.snapshot(binary: '/path/safe-fixture')
|
|
59
|
+
PWN::Plugins::Sandbox.rollback(snapshot: s[:snapshot], backend: 'bwrap')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Docker is default, image `pwn-sandbox:local`; it must be provisioned locally,
|
|
63
|
+
with `/usr/bin/python3`, strace, GDB, and target runtime libraries. No implicit
|
|
64
|
+
pull, installation, daemon startup or host-execution fallback occurs. Use the
|
|
65
|
+
included `lib/pwn/plugins/sandbox/Dockerfile` to build explicitly. Network none,
|
|
66
|
+
read-only root/artifacts, unprivileged UID, all caps dropped, no-new-privileges,
|
|
67
|
+
pid/cpu/memory/swap constraints, disposable bounded tmpfs, forced cleanup.
|
|
68
|
+
Instrumentation runs are separate replays with the same input, not observation
|
|
69
|
+
of the original execution. Signals, PC, backtrace, raw GDB output are returned;
|
|
70
|
+
exploitable classification is `unknown` if the GDB plugin is absent. No invented
|
|
71
|
+
classification. Current `-nx` configuration deliberately does not load arbitrary
|
|
72
|
+
user GDB startup scripts. Install a reviewed exploitable plugin into the image
|
|
73
|
+
and extend its explicit initialization if classification is required.
|
|
74
|
+
|
|
75
|
+
Explicit `backend: 'bwrap'` works without a Docker daemon where unprivileged
|
|
76
|
+
user namespaces are permitted. It exposes only read-only system runtime trees
|
|
77
|
+
and target artifact, creates fresh user/network/PID/mount namespaces, and hides
|
|
78
|
+
home. It is a **weaker resource tier**: per-process RLIMIT_AS, not an aggregate
|
|
79
|
+
cgroup budget or fork-bomb protection. Use Docker/microVM for hostile samples.
|
|
80
|
+
No automatic backend fallback. Snapshot/rollback means verified immutable input
|
|
81
|
+
copies and fresh disposable environments, **not live process/VM checkpoints**.
|
|
82
|
+
Fuzzing is bounded seeded bit-flip stdin mutation, not coverage-guided AFL.
|
|
83
|
+
|
|
84
|
+
Tools register via `require 'pwn/ai/agent/tools/sandbox'`: `sandbox_run` and
|
|
85
|
+
`sandbox_fuzz`; normal Registry discovery finds that file.
|
|
86
|
+
|
|
87
|
+
Verification:
|
|
88
|
+
```
|
|
89
|
+
bundle exec rspec spec/lib/pwn/plugins/{packet,capability_broker,sandbox}_spec.rb
|
|
90
|
+
python3 spec/lib/pwn/plugins/capd_test.py
|
|
91
|
+
python3 spec/lib/pwn/plugins/sandbox_test.py
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Safe real Docker smoke after explicit provisioning:
|
|
95
|
+
```
|
|
96
|
+
bundle exec ruby -Ilib -rpwn/plugins/sandbox -e 'p PWN::Plugins::Sandbox.run(binary: "/bin/true")'
|
|
97
|
+
```
|
|
@@ -18,8 +18,9 @@ bundle exec rubocop scripts/benchmark_policy.rb
|
|
|
18
18
|
The experiment uses Ruby and its standard libraries; it does not require the
|
|
19
19
|
full application boot sequence. Use a fresh Ruby process, not the live agent
|
|
20
20
|
console. JSON is printed to stdout and optionally written to `--output`.
|
|
21
|
-
`--self-check` runs independent scorer tests
|
|
22
|
-
it prints
|
|
21
|
+
`--self-check` runs independent scorer tests, both complete experimental arms,
|
|
22
|
+
and two fresh snapshot workers; it prints short pass messages instead of a
|
|
23
|
+
report. Use the options separately.
|
|
23
24
|
The benchmark exits nonzero if persistence changes during evaluation, splits
|
|
24
25
|
overlap, a negative control succeeds, or a positive control fails. Self-checks
|
|
25
26
|
also require actual training updates in the on arm and none in the off arm.
|
|
@@ -36,8 +37,9 @@ not `Reward.judge`, model prose, or a previously assigned reward. It calls
|
|
|
36
37
|
training. This is not a test of Reward's verification-record binding, the live
|
|
37
38
|
Loop, Metrics learning, memory retrieval, or adapter training.
|
|
38
39
|
|
|
39
|
-
The
|
|
40
|
-
|
|
40
|
+
The experimental driver remains in `scripts/`. The opt-in `PolicyEvaluation`
|
|
41
|
+
module launches only this fixed runner; it does not load the live agent in its
|
|
42
|
+
workers. Its module autoload does not enable evaluation or promotion.
|
|
41
43
|
|
|
42
44
|
## Protocol
|
|
43
45
|
|
|
@@ -60,6 +62,10 @@ CLI and production module tree; no module manifest or generated skill is needed.
|
|
|
60
62
|
answer keys to choose actions. Each call writes into a fresh task directory.
|
|
61
63
|
Exact artifact correctness supplies a binary training label to Policy; the
|
|
62
64
|
off arm executes the same work but Policy declines to update.
|
|
65
|
+
Each call has an explicit action ID. Only after the independent artifact
|
|
66
|
+
check, `finish` receives a `controlled_comparison` attribution receipt naming
|
|
67
|
+
that ID. This is a one-action isolated experiment, not a claim that the last
|
|
68
|
+
action in an arbitrary live trace caused the outcome.
|
|
63
69
|
4. **Held-out evaluation.** Only after training, materialize eight distinct
|
|
64
70
|
held-out inputs with literal answer keys: four numeric and four inventory
|
|
65
71
|
tasks. Assert no training input appears in evaluation. Task families and
|
|
@@ -117,6 +123,9 @@ No fixed gains are embedded in the report.
|
|
|
117
123
|
|
|
118
124
|
- **Completion:** tasks with at least one objectively successful attempt divided
|
|
119
125
|
by evaluated tasks. A failed attempt does not count as task completion.
|
|
126
|
+
- **Artifact check score:** each attempt earns 1.0 only for an exact correct
|
|
127
|
+
artifact with unchanged input, otherwise 0.0; phase score is its mean across
|
|
128
|
+
attempts. Report rows retain the actual artifact body as well as its hash.
|
|
120
129
|
- **False-success count/rate:** attempts claiming `ok: true` without achieving the
|
|
121
130
|
task; rate denominator is executed attempts, not tasks. This is false reporting
|
|
122
131
|
by the handler, not acceptance of that report by the independent checker.
|
|
@@ -149,3 +158,139 @@ would undermine the no-network/no-credentials guarantee. A future live-model
|
|
|
149
158
|
study should use a separately reviewed runner, identical model/tool budgets,
|
|
150
159
|
external objective verifiers, frozen held-out evaluation, and actual provider
|
|
151
160
|
usage records. Do not present this controller experiment as that study.
|
|
161
|
+
|
|
162
|
+
## Opt-in independent snapshot evaluation (R5)
|
|
163
|
+
|
|
164
|
+
The original commands and `off`/`on` report shape still work. To additionally
|
|
165
|
+
export snapshots and run repeated evaluations in **fresh subprocesses**, use:
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
ruby scripts/benchmark_policy.rb --heldout \
|
|
169
|
+
--snapshot-dir /tmp/pwn-policy-snapshots \
|
|
170
|
+
--output /tmp/pwn-policy-heldout.json
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The snapshot directory must be **new**, below `/tmp`, with no symlink parents.
|
|
174
|
+
`off.json` and `on.json` are genuine Policy JSON tables after the same real
|
|
175
|
+
training schedule. Exported `updated_at` metadata is normalized to `null` for
|
|
176
|
+
reproducible snapshot digests; no Q entries or rewards are fabricated. Export
|
|
177
|
+
happens before that arm's evaluation. Neither `--heldout` nor `--snapshot-dir`
|
|
178
|
+
promotes anything or discovers/reads a real home-directory policy.
|
|
179
|
+
|
|
180
|
+
The added `heldout` array contains protocol `pwn-policy-heldout-v2`, for suite
|
|
181
|
+
indices 0 and 1. Each worker runs three frozen arms: `off` (baseline snapshot,
|
|
182
|
+
policy disabled), `baseline` (baseline enabled), and `candidate` (candidate
|
|
183
|
+
enabled). Workers never call begin/observe/finish, warmup, or reset the caller's
|
|
184
|
+
policy. Resets and snapshot installation occur only inside temporary HOME.
|
|
185
|
+
Only fixed local handlers are registered; no provider, shell tool, credential,
|
|
186
|
+
user config, or network client is loaded. Environment variables, including Ruby
|
|
187
|
+
startup hooks, are removed before spawning Ruby. Workers have a 30-second
|
|
188
|
+
deadline; stalled children are killed and reaped. Snapshot inputs must be regular
|
|
189
|
+
non-symlink files, at most 4 MiB, with valid numeric `q`, `h`, `visits`, `returns`,
|
|
190
|
+
`n_updates`, and `td_abs_sum` fields. Parent directories cannot be symlinks.
|
|
191
|
+
|
|
192
|
+
Suite indices 0..7 are bounded deterministic variations, **not random trials**.
|
|
193
|
+
Numeric inputs and separate literal answer keys are scaled by `seed + 1`;
|
|
194
|
+
inventory IDs and separate answer keys are offset by `100 * seed`. This does not
|
|
195
|
+
call a candidate algorithm to construct its answer key. Even indices use flat
|
|
196
|
+
paths and compact JSON; odd indices use nested paths containing spaces, pretty
|
|
197
|
+
JSON, and read-only inputs. Every arm runs eight tasks, 16 negative controls and
|
|
198
|
+
eight positive controls. Training fixtures remain unchanged and disjoint.
|
|
199
|
+
These are two task families and two filesystem configurations, not unseen tools
|
|
200
|
+
or broad environment generalization. Both environment types are required for
|
|
201
|
+
promotion eligibility.
|
|
202
|
+
For externally supplied snapshots, `disjoint_inputs` describes the harness's
|
|
203
|
+
fixture sets, not proof of the snapshot's training history; that history is not
|
|
204
|
+
attested by this runner.
|
|
205
|
+
|
|
206
|
+
Explicit snapshots from another controlled experiment can be evaluated without
|
|
207
|
+
booting the application:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
require './lib/pwn/ai/agent/policy_evaluation'
|
|
211
|
+
evaluator = PWN::AI::Agent::PolicyEvaluation
|
|
212
|
+
reports = [0, 1].map do |seed|
|
|
213
|
+
evaluator.evaluate(baseline: '/tmp/baseline.json',
|
|
214
|
+
candidate: '/tmp/candidate.json', seed: seed)
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
With fixed source revisions and snapshot bytes, snapshot reports reproduce all
|
|
219
|
+
fields except `elapsed_seconds`. They contain no wall-clock timestamps, random
|
|
220
|
+
IDs or temporary paths. The original training report still contains the timing
|
|
221
|
+
and metadata variability described above.
|
|
222
|
+
|
|
223
|
+
## Explicit promotion and rollback
|
|
224
|
+
|
|
225
|
+
This is an **operator-invoked local eligibility gate**, not automatic online
|
|
226
|
+
policy promotion. `Policy.finish` and Loop do not call it. The existing online
|
|
227
|
+
learning behavior is not redirected or promoted by this module. No live policy
|
|
228
|
+
path is defaulted, and writes require `enabled: true` **and** `quiescent: true`.
|
|
229
|
+
The latter is an operator assertion: **stop all agent processes and policy
|
|
230
|
+
writers first**. The existing Policy writer does not share a transaction lock
|
|
231
|
+
with this module; concurrent live learning or concurrent promotions are not
|
|
232
|
+
supported. Restart writers only after the operation and readback complete.
|
|
233
|
+
|
|
234
|
+
Promotion requires 2..8 reports with distinct valid suite indices covering both
|
|
235
|
+
filesystem configurations. It then **reruns each suite in a fresh worker** using
|
|
236
|
+
the specified snapshot bytes. Every non-timing report field must match the fresh
|
|
237
|
+
execution, including source/harness digests, snapshot digests, input hashes,
|
|
238
|
+
artifact bodies and hashes, action choices, scores, controls, and frozen-policy
|
|
239
|
+
checks. Hashes alone are not signatures or evidence of trusted authorship;
|
|
240
|
+
re-execution is the authority. Model-written `passed: true`, edited scores,
|
|
241
|
+
invented artifact bodies, stale source revisions, or copied duplicate reports
|
|
242
|
+
cannot substitute for those executions. Supplied elapsed times are discarded;
|
|
243
|
+
only newly measured times enter the gate.
|
|
244
|
+
|
|
245
|
+
For **every** repeated suite, compared with both baseline-on and off:
|
|
246
|
+
|
|
247
|
+
- completion and mean artifact-check score must not decrease;
|
|
248
|
+
- no previously solved individual task may become unsolved (aggregate gains
|
|
249
|
+
cannot hide a task/family regression);
|
|
250
|
+
- false-success count **and rate**, repeated mistakes and tool calls must not rise;
|
|
251
|
+
- elapsed time must be at most `baseline_seconds * 1.25 + 0.02`, a fixed local
|
|
252
|
+
jitter allowance rather than evidence of a statistically established speedup.
|
|
253
|
+
|
|
254
|
+
Each suite must also improve completion, false-success count, repeated mistakes,
|
|
255
|
+
or calls relative to baseline-on. Better training returns, more updates, or a
|
|
256
|
+
timing-only change cannot qualify. The gate fails closed when verification fails.
|
|
257
|
+
Snapshots, source digests and live-baseline bytes must still match. An explicit
|
|
258
|
+
live target must already exist and equal the evaluated baseline byte-for-byte.
|
|
259
|
+
The previous policy is saved beside it as a digest-named rollback JSON before a
|
|
260
|
+
same-directory atomic replacement and exact readback. No trajectory file is
|
|
261
|
+
modified. Preserve the returned receipt for rollback.
|
|
262
|
+
|
|
263
|
+
A disposable demonstration using the opt-in benchmark output above:
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
require './lib/pwn/ai/agent/policy_evaluation'
|
|
267
|
+
evaluator = PWN::AI::Agent::PolicyEvaluation
|
|
268
|
+
baseline = '/tmp/pwn-policy-snapshots/off.json'
|
|
269
|
+
candidate = '/tmp/pwn-policy-snapshots/on.json'
|
|
270
|
+
reports = JSON.parse(File.read('/tmp/pwn-policy-heldout.json'), symbolize_names: true).fetch(:heldout)
|
|
271
|
+
live = '/tmp/pwn-policy-demo-live.json' # NOT the real online policy
|
|
272
|
+
File.open(live, File::WRONLY | File::CREAT | File::EXCL, 0o600) { |f| f.write(File.binread(baseline)) }
|
|
273
|
+
receipt = evaluator.promote(enabled: true, quiescent: true,
|
|
274
|
+
baseline: baseline, candidate: candidate,
|
|
275
|
+
reports: reports, live_path: live)
|
|
276
|
+
raise receipt.inspect unless receipt[:promoted]
|
|
277
|
+
restored = evaluator.rollback(enabled: true, quiescent: true,
|
|
278
|
+
live_path: live, receipt: receipt)
|
|
279
|
+
raise restored.inspect unless restored[:rolled_back]
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Rollback verifies the receipt's explicit target, backup path and prior digest,
|
|
283
|
+
validates the backup schema, and refuses if the live file no longer matches the
|
|
284
|
+
promoted candidate digest. Missing, altered or symlink backups/targets fail
|
|
285
|
+
closed. Both methods default to a disabled result; rejected operations return
|
|
286
|
+
`promoted: false` or `rolled_back: false` with a reason. `evaluate` raises on an
|
|
287
|
+
invalid snapshot, worker failure, or deadline. Force-killing a worker can leave
|
|
288
|
+
its temporary directory; this is not an OS sandbox for untrusted code.
|
|
289
|
+
|
|
290
|
+
**Limit:** this gate measures only the fixed benchmark action vocabulary and
|
|
291
|
+
public task families. A real online policy containing unrelated tools may show
|
|
292
|
+
no gain and be rejected; passing does not validate those unrelated routes or
|
|
293
|
+
establish live LLM gains. A production rollout still needs separately reviewed,
|
|
294
|
+
representative objective tasks and operator judgment. Do not interpret this
|
|
295
|
+
small public held-out set as a secret test or optimize repeatedly against it
|
|
296
|
+
and then claim independent generalization.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Native HTTP capture and skill hygiene
|
|
2
|
+
|
|
3
|
+
## Native HTTP proxy (P16)
|
|
4
|
+
|
|
5
|
+
`PWN::Plugins::MitmProxy` uses WEBrick's native proxy/CONNECT transport and
|
|
6
|
+
Net::HTTP for interception. It does not require mitmproxy, Burp, or ZAP.
|
|
7
|
+
Only backend `native` is accepted. HTTPS CONNECT tunnels work, but their
|
|
8
|
+
contents are **not decrypted**: captured entries explicitly have
|
|
9
|
+
`_capture: opaque_connect`. HTTP requests and responses are captured to HAR 1.2.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
require 'pwn/plugins/mitm_proxy'
|
|
13
|
+
proxy = PWN::Plugins::MitmProxy.start(har_path: '/tmp/engagement.har')
|
|
14
|
+
browser = PWN::Plugins::TransparentBrowser.open(
|
|
15
|
+
browser_type: :headless_chrome, capture_proxy: proxy
|
|
16
|
+
)
|
|
17
|
+
# Navigate the browser using its normal API. Chrome loopback bypass is disabled.
|
|
18
|
+
entries = PWN::Plugins::MitmProxy.entries(proxy: proxy)
|
|
19
|
+
entry = PWN::Plugins::MitmProxy.http_replay(
|
|
20
|
+
proxy: proxy, request_id: entries.first[:_request_id],
|
|
21
|
+
mutations: { method: 'POST', path: '/fixture', headers: { 'X-Test' => 'yes' }, body: 'sample' }
|
|
22
|
+
)
|
|
23
|
+
# Close the browser separately, then stop its caller-owned proxy:
|
|
24
|
+
PWN::Plugins::MitmProxy.stop(proxy: proxy)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Agent toolset `http`: `http_proxy_start`, `http_proxy_entries`,
|
|
28
|
+
`http_proxy_rules`, `http_replay`, `http_proxy_stop`. Lifecycle tools return
|
|
29
|
+
JSON descriptors; subsequent calls use `proxy_id: descriptor[:id]`.
|
|
30
|
+
The plugin also accepts `proxy:` descriptors. IDs are process-local, not
|
|
31
|
+
persistent replay handles after restart.
|
|
32
|
+
|
|
33
|
+
Replace the entire active rule list with `rules(proxy:, rules:)`. Rules are
|
|
34
|
+
literal substitutions (not regex):
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
[{ phase: 'request', field: 'header:x-test', match: 'old', replace: 'new' },
|
|
38
|
+
{ phase: 'response', field: 'body', match: 'old', replace: 'new' }]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Fields are `body`, request-only `url`, or `header:NAME`. Replay mutations are
|
|
42
|
+
`method`, `url`, `path`, `query`, `headers`, `body`; null header values remove
|
|
43
|
+
headers. Hop-by-hop/framing headers are regenerated. Binary HAR bodies use
|
|
44
|
+
base64 encoding. Replay does not mutate the original captured entry.
|
|
45
|
+
|
|
46
|
+
HAR files contain **unredacted traffic**, including any cookies and credentials
|
|
47
|
+
sent by the operator. They are created mode 0600; use engagement storage and
|
|
48
|
+
retention controls. Only loopback fixtures are used by the regression tests.
|
|
49
|
+
There is no built-in scope policy for proxy traffic; enforcement belongs in
|
|
50
|
+
engagement network isolation or the central scope layer. Binding is loopback
|
|
51
|
+
by default. Upstream HTTP timeout defaults to 30 seconds. Bodies and the
|
|
52
|
+
session capture collection are currently buffered in memory, not streamed.
|
|
53
|
+
|
|
54
|
+
## On-demand skills consolidation (P19)
|
|
55
|
+
|
|
56
|
+
`PWN::AI::Agent::SkillConsolidation.consolidate(content:, evidence: {}, now:,
|
|
57
|
+
stale_days: 90)` returns a pure proposal: `content`, `version`, `changed`,
|
|
58
|
+
`deduplicated`, `promoted`, and `review`.
|
|
59
|
+
|
|
60
|
+
- Deduplicates RL notes, ignoring leading date annotations and whitespace.
|
|
61
|
+
- Conflicting text for one feedback ID, explicit negation against a SOP line,
|
|
62
|
+
regressed proofs, and stale verification move to a **not active SOP** review
|
|
63
|
+
section. Unmanaged SOP prose is never automatically deleted or rewritten.
|
|
64
|
+
- Promotions require resolved status, three distinct explicitly successful
|
|
65
|
+
verification sessions, and recent non-future verification. Failure counts
|
|
66
|
+
and failure-session IDs do not count as successful evidence.
|
|
67
|
+
- Managed promoted procedures are rechecked for stale or regressed proofs.
|
|
68
|
+
- Stamps `<!-- pwn-skill-version: HASH12 updated: ISO8601 -->` after YAML
|
|
69
|
+
frontmatter; unchanged content remains byte-identical on subsequent passes.
|
|
70
|
+
|
|
71
|
+
`consolidate_file(path:)` previews by default. Applying requires
|
|
72
|
+
`dry_run: false, confirm: true, expected_sha256: preview[:source_sha256]`.
|
|
73
|
+
It serializes cooperating consolidation writers, checks the original content,
|
|
74
|
+
keeps a content-addressed backup, atomically replaces the file, and verifies
|
|
75
|
+
readback. Symlink files are rejected. No weekly job is installed.
|
|
76
|
+
|
|
77
|
+
Agent tool `skills_consolidate(name:, dry_run: true)` targets an already
|
|
78
|
+
installed skill; it does not accept model-provided verification evidence.
|
|
79
|
+
It reloads the skill index after an explicitly confirmed write.
|
|
80
|
+
|
|
81
|
+
## Precise correction evidence (P20) and integration hooks
|
|
82
|
+
|
|
83
|
+
Docker socket permissions, registry pull denial and template parsing remain
|
|
84
|
+
distinct classes/signatures. Generic path fixes cannot resolve non-path
|
|
85
|
+
failures; raw-socket fixes cannot resolve ordinary filesystem/daemon
|
|
86
|
+
permissions. Legacy incompatible fixes are excluded from KNOWN FIXES.
|
|
87
|
+
|
|
88
|
+
For promotion evidence, the successful verification hook should call:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
PWN::AI::Agent::Mistakes.note_hint_outcome(
|
|
92
|
+
signature: signature, helped: true, session_id: verified_session_id
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Failed hints clear that stability evidence. `skills_consolidate` consumes
|
|
97
|
+
`verified_sessions` and `last_verified` keyed by the existing RL signature.
|
|
98
|
+
The parent loop must pass `session_id` only after real verification; callers
|
|
99
|
+
that omit it cannot promote notes merely by incrementing a counter.
|
|
100
|
+
Session-recall/index consumers can read the `pwn-skill-version` stamp and
|
|
101
|
+
prefer current installed content over historical SOP text. This module does
|
|
102
|
+
not modify central loop or session-recall selection behavior.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Recon / Findings integration (P17 / P10)
|
|
2
|
+
|
|
3
|
+
## Parent integration hooks
|
|
4
|
+
|
|
5
|
+
- Existing autoloaded `PWN::Plugins::Recon` now exposes `run(opts)`; no new plugin autoload needed. New agent registration file: `lib/pwn/ai/agent/tools/recon_run.rb` (normal Registry discovery).
|
|
6
|
+
- `finding_record` handler uses `PWN::Plugins::Findings.record_structured(opts)`. The loop can treat `ArgumentError` as incomplete finding input; **do not infer a working PoC from accepted input**. No Loop/config/CLI changes in this patch.
|
|
7
|
+
- P3 hook: `Recon.run(..., ingest: true)` calls `PWN::AI::Context.ingest(path, session_id: engagement_id, **ingest_options)` after atomic persistence and JSON readback. `ingestor: callable` supports an alternate implementation with the same signature. Ingestion failure is returned in `result[:ingestion]` without discarding recon evidence. Preserve the literal asset ID in ingested chunks. Plugin callers opt in; the agent tool defaults ingestion on.
|
|
8
|
+
|
|
9
|
+
## Recon model
|
|
10
|
+
|
|
11
|
+
`Recon.run(target: '127.0.0.1', modules: %w[nmap banner tls], ports: [443], engagement_id: 'lab', ingest: true)`.
|
|
12
|
+
|
|
13
|
+
Single hostname/IP target (no arbitrary CLI switches, URL, CIDR, or shell command), explicit integer ports, per-operation timeout 15 seconds by default (maximum 300). `nmap` uses an unprivileged TCP connect scan, without sudo or NSE. Existing `NmapIt` API remains untouched. `banner` passively reads up to 4096 bytes; it does not send an HTTP request. `tls` collects the certificate with `trust_verified: false` (not a trust audit). Optional `subfinder` and `nuclei` run **only if explicitly named**, and unavailable tools return module status rather than fabricate results. Nuclei observations are not automatically promoted to findings.
|
|
14
|
+
|
|
15
|
+
Output: `{schema_version: 1, engagement_id:, assets: [{id:, address:, protocol:, port:, observations: [], evidence_paths: []}], modules: [], path:, ingestion: ...}`.
|
|
16
|
+
|
|
17
|
+
Stored at `~/.pwn/engagements/<engagement>/recon/assets.json`; `root:` overrides the engagements directory for isolated fixtures. Raw scan evidence is persisted beside it. IDs are `asset-` plus the first 24 hex SHA256 characters of lowercase address, protocol, port joined by `|`. Repeated same-identity observations merge across runs, not overwrite other assets. DNS names and resolved IPs are distinct identities unless the source explicitly supplies the same address; the pipeline does not silently infer aliases.
|
|
18
|
+
|
|
19
|
+
Use `affected_asset: asset[:id]` and `evidence_paths: asset[:evidence_paths]` in a structured finding. Stable IDs survive report generation and JSON ingestion unchanged.
|
|
20
|
+
|
|
21
|
+
## Strict findings boundary and compatibility
|
|
22
|
+
|
|
23
|
+
`record_structured` requires title, CWE identifier (`CWE-<positive integer>`), complete CVSS 3.0/3.1 base vector and matching numeric base score, affected_asset, nonempty existing readable absolute evidence_paths, nonempty PoC command/code string, attack_chain_refs array, remediation string, and numeric confidence 0..1. Chain refs must already exist in the same engagement. Invalid input raises before appending. CVSS 2/4, temporal/environmental vectors are explicitly unsupported (rejected, not normalized).
|
|
24
|
+
|
|
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
|
+
|
|
27
|
+
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
|
+
|
|
29
|
+
## Verification boundaries
|
|
30
|
+
|
|
31
|
+
Specs use disposable localhost TCP/TLS fixtures and real nmap connect scans. Optional external scanner outputs are parser fixtures, not internet engagements. Context hook failures are visible; embeddings are never synthesized. No remote scanning is necessary to run these focused specs.
|
|
@@ -47,10 +47,10 @@ This is the live numeric controller. It does not replace planning.
|
|
|
47
47
|
|
|
48
48
|
| Piece | What it is |
|
|
49
49
|
|---|---|
|
|
50
|
-
| State | request kind, task family, plan quality, answer completeness, usable-result, last action, fail bin, and
|
|
50
|
+
| State | request kind, task family, plan quality, answer completeness, usable-result, last action, fail bin, engine, and sanitized host-observed capability/verification scope |
|
|
51
51
|
| Action | tool name, or `final` |
|
|
52
52
|
| Step reward | 0; −0.01 per tool after 8 |
|
|
53
|
-
| Terminal reward |
|
|
53
|
+
| Terminal reward | Resolved training score mapped to −1..1 × confidence (sole large R), attributed only to independently linked actions. `plan_coverage` is a tag, not the score. |
|
|
54
54
|
| Updates | Q-learning (`alpha=0.15`, `gamma=0.85`) and REINFORCE (`alpha=0.05`). Stored trajectories replay twice on warmup so a short table is not empty advice. |
|
|
55
55
|
| Budget | Eight finished episodes (live or warmup-credited) unlock greedy suggestions. Until then the prompt omits them. |
|
|
56
56
|
| Steer | Q-advantage in `Registry.rank` once the episode budget is met; keyword fit and CORE_TOOLS still come first. Suggested actions follow `Registry.preference_order` (`ai.agent.tool_preference`). |
|
|
@@ -81,6 +81,58 @@ current session request and invalidated by subsequent tool execution. The API
|
|
|
81
81
|
trusts the host verifier to cover the complete request; it cannot infer missing
|
|
82
82
|
criteria or automatically verify arbitrary tasks. LLM judgments remain fallible.
|
|
83
83
|
|
|
84
|
+
### Executed acceptance checks and artifact attribution
|
|
85
|
+
|
|
86
|
+
For stronger verification, pass a host-owned `verification_contract` to
|
|
87
|
+
`Loop.run`, or use `Reward.run_verification(request:, session_id:, contract:)`.
|
|
88
|
+
The contract contains `root`, `requirements` (distinct verbatim clauses of the
|
|
89
|
+
original request), and `checks`. Each check names its `requirement` and kind:
|
|
90
|
+
|
|
91
|
+
- `file`: an in-root regular `path` whose bytes equal `expected`.
|
|
92
|
+
- `json`: an in-root regular `path` whose parsed JSON equals `expected`.
|
|
93
|
+
- `command`: a literal `argv`, expected `exit_code` (default zero), and optional
|
|
94
|
+
expected stdout. Requires `allow_commands: true`.
|
|
95
|
+
- `http`: an exact `url` listed in `allowed_urls`, expected response bytes, and
|
|
96
|
+
expected `status` (default 200). GET only, with no redirects.
|
|
97
|
+
|
|
98
|
+
Check failures are negative evidence. Missing requirements, unavailable checks,
|
|
99
|
+
and timeouts stay unknown even if the LLM judge awards a high score. Later tool
|
|
100
|
+
execution invalidates a runner report without falling back to presumed success.
|
|
101
|
+
The runner records output digests, not raw command output or expected values.
|
|
102
|
+
Artifact reads use no-follow descriptors, validate their actual location through
|
|
103
|
+
Linux `/proc/self/fd`, and enforce the byte limit while reading. If descriptor
|
|
104
|
+
location validation is unavailable, the check stays unknown rather than using
|
|
105
|
+
an unsafe pathname fallback.
|
|
106
|
+
The caller must supply the complete acceptance checklist: matching clauses to
|
|
107
|
+
the original text is not semantic proof that the checklist covers every intent.
|
|
108
|
+
|
|
109
|
+
Commands have a cleared environment, private HOME set to the selected root,
|
|
110
|
+
bounded output and timeout, and process-group cleanup. This is **not an OS
|
|
111
|
+
sandbox**: opt-in commands retain the process's filesystem/network permissions.
|
|
112
|
+
Only run trusted checks, using an external sandbox for untrusted programs.
|
|
113
|
+
|
|
114
|
+
At the final boundary, Loop executes the contract and publishes a verification
|
|
115
|
+
event through `on_tool`. Around dispatched actions it snapshots declared
|
|
116
|
+
artifact digests. Matching the final checked digest to its last observed writer
|
|
117
|
+
produces an `independent_verifier` attribution receipt. This establishes artifact
|
|
118
|
+
provenance, not universal causal proof. Controlled comparisons in isolated
|
|
119
|
+
evaluation can supply a separate `controlled_comparison` receipt.
|
|
120
|
+
|
|
121
|
+
Policy distributes the existing terminal reward across uniquely linked action
|
|
122
|
+
IDs. Unlinked actions receive no positive training credit; an accounting-only
|
|
123
|
+
final row retains unattributed terminal reward. Adding successful no-op commands
|
|
124
|
+
does not create more reward. Known per-step cost remains; unknown task outcomes
|
|
125
|
+
still do not train. Ordinary model-judged runs without attribution may retain
|
|
126
|
+
outcome/lesson records, but do not positively reinforce guessed tool contributions.
|
|
127
|
+
|
|
128
|
+
`Loop.run(trusted_context:)` accepts host observations, not model arguments.
|
|
129
|
+
Policy stores only fixed environment/capability/failure/verification categories;
|
|
130
|
+
state backoff stays within that observed scope. Registry checks declared tool
|
|
131
|
+
prerequisites before ranking, including core tools, so a known absent prerequisite
|
|
132
|
+
cannot be outweighed by a historical success score. Unknown availability is not
|
|
133
|
+
treated as absence. Default local scope observes the Ruby runtime and `/bin/sh`;
|
|
134
|
+
callers must supply observations for remote/container-specific capabilities.
|
|
135
|
+
|
|
84
136
|
Policy observations also capture allowlisted operation, argument-role/type
|
|
85
137
|
features, and result classification, without retaining raw argument values.
|
|
86
138
|
These condition next-tool ranking only after enough contextual samples exist,
|
data/documentation/Swarm.md
CHANGED
|
@@ -12,7 +12,7 @@ self-improvement loop covers the whole swarm.
|
|
|
12
12
|
|
|
13
13
|
| Path | Contains |
|
|
14
14
|
|---|---|
|
|
15
|
-
| `~/.pwn/agents.yml` | Persona registry (name → role/engine/toolsets/max_iters) |
|
|
15
|
+
| `~/.pwn/agents.yml` | Persona registry (name → role/engine/model/toolsets/max_iters) |
|
|
16
16
|
| `~/.pwn/swarm/<id>/bus.jsonl` | Append-only chat every persona reads/writes |
|
|
17
17
|
| `~/.pwn/swarm/<id>/personas.json` | persona name → `PWN::Sessions` id |
|
|
18
18
|
|
|
@@ -21,6 +21,41 @@ a `PWN::Cron` job) calling `Swarm.ask` with the same `swarm_id`.
|
|
|
21
21
|
|
|
22
22
|
## Define personas
|
|
23
23
|
|
|
24
|
+
Each persona accepts optional `engine` and `model` keys. `model` is the exact
|
|
25
|
+
provider model identifier (case and punctuation are preserved). Omit `engine`
|
|
26
|
+
to inherit the active engine; omit `model` or leave it blank to use the selected
|
|
27
|
+
provider's configured default model. Existing engine-only entries still work.
|
|
28
|
+
|
|
29
|
+
For example, edit `~/.pwn/agents.yml`:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
reviewer:
|
|
33
|
+
role: Review implementation and tests.
|
|
34
|
+
engine: openai
|
|
35
|
+
model: gpt-6-astra
|
|
36
|
+
toolsets: [terminal, pwn]
|
|
37
|
+
local_reviewer:
|
|
38
|
+
role: Independently review implementation and tests.
|
|
39
|
+
engine: ollama
|
|
40
|
+
model: your-installed-model:tag
|
|
41
|
+
toolsets: [terminal, pwn]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Replace model identifiers with models available to your configured providers.
|
|
45
|
+
Personas can use different models on the same provider or different providers.
|
|
46
|
+
The selection applies to `ask`, `debate`, and `broadcast`; overrides are scoped
|
|
47
|
+
to each persona execution, restored after nested calls/errors, and do not rewrite
|
|
48
|
+
global provider defaults. An omitted model uses the provider default, not an
|
|
49
|
+
enclosing persona's model override. Provider authentication is unchanged.
|
|
50
|
+
|
|
51
|
+
`agent_spawn` also accepts `model:` and `agent_list` reports it.
|
|
52
|
+
|
|
53
|
+
On upgrade, `pwn setup --migrate` applies schema 3: existing persona entries
|
|
54
|
+
without a `model` key receive an unset YAML `model:` field (null, meaning the
|
|
55
|
+
selected provider's default). Existing model values, engine selections, and
|
|
56
|
+
custom persona fields are preserved. Re-running migration makes no further
|
|
57
|
+
changes. A missing `agents.yml` is not created by this migration.
|
|
58
|
+
|
|
24
59
|
```ruby
|
|
25
60
|
agent_spawn(name: 'red_team',
|
|
26
61
|
role: 'Offensive operator. Propose the most aggressive next step.',
|
|
@@ -82,8 +117,8 @@ agent_ask(name: 'red_team', swarm_id: tx[:swarm_id],
|
|
|
82
117
|
request: 'blue_team raised WAF concerns - revise the payload.')
|
|
83
118
|
```
|
|
84
119
|
|
|
85
|
-
|
|
86
|
-
|
|
120
|
+
Each persona can pin a **different engine and/or model**, rather than requiring
|
|
121
|
+
every persona to use one provider's default model.
|
|
87
122
|
|
|
88
123
|
**See also:** [pwn-ai Agent](pwn-ai-Agent.md) ·
|
|
89
124
|
[Agent Tool Registry](Agent-Tool-Registry.md) · [Sessions](Sessions.md)
|