pwn 0.5.631 → 0.5.632

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/install-matrix.yml +177 -0
  3. data/.rubocop.yml +1 -1
  4. data/Gemfile +3 -3
  5. data/README.md +2 -2
  6. data/bin/pwn_autoinc_version +0 -20
  7. data/documentation/Configuration.md +39 -4
  8. data/documentation/Extrospection.md +61 -10
  9. data/documentation/Installation.md +5 -5
  10. data/documentation/Reinforcement-Learning.md +123 -0
  11. data/documentation/pwn-ai-Agent.md +1 -1
  12. data/etc/osint/social_sites.json +116 -0
  13. data/install.sh +27 -18
  14. data/lib/pwn/ai/agent/curriculum.rb +561 -0
  15. data/lib/pwn/ai/agent/extrospection/osint/bridges.rb +240 -0
  16. data/lib/pwn/ai/agent/extrospection/osint/social.rb +405 -0
  17. data/lib/pwn/ai/agent/extrospection.rb +81 -11
  18. data/lib/pwn/ai/agent/learning.rb +187 -39
  19. data/lib/pwn/ai/agent/loop.rb +62 -19
  20. data/lib/pwn/ai/agent/metrics.rb +172 -2
  21. data/lib/pwn/ai/agent/mistakes.rb +54 -5
  22. data/lib/pwn/ai/agent/registry.rb +19 -9
  23. data/lib/pwn/ai/agent/reward.rb +523 -0
  24. data/lib/pwn/ai/agent/tools/curriculum.rb +95 -0
  25. data/lib/pwn/ai/agent/tools/extrospection.rb +9 -4
  26. data/lib/pwn/ai/agent/tools/reward.rb +117 -0
  27. data/lib/pwn/ai/agent.rb +2 -0
  28. data/lib/pwn/config.rb +11 -2
  29. data/lib/pwn/cron.rb +39 -0
  30. data/lib/pwn/memory.rb +15 -4
  31. data/lib/pwn/memory_index.rb +12 -1
  32. data/lib/pwn/version.rb +1 -1
  33. data/packer/provisioners/pwn.sh +57 -93
  34. data/spec/documentation/installation_md_spec.rb +173 -0
  35. data/spec/documentation/prompt_version_placeholder_spec.rb +37 -0
  36. data/spec/integration/fresh_install_spec.rb +73 -0
  37. data/spec/integration/install_doc.sh +79 -0
  38. data/spec/integration/reinforced_feedback_loop_spec.rb +453 -0
  39. data/spec/lib/pwn/ai/agent/curriculum_spec.rb +56 -0
  40. data/spec/lib/pwn/ai/agent/extrospection/osint/bridges_spec.rb +24 -0
  41. data/spec/lib/pwn/ai/agent/extrospection/osint/social_spec.rb +36 -0
  42. data/spec/lib/pwn/ai/agent/extrospection_spec.rb +68 -0
  43. data/spec/lib/pwn/ai/agent/learning_spec.rb +1 -1
  44. data/spec/lib/pwn/ai/agent/reward_spec.rb +58 -0
  45. data/spec/lib/pwn/ai/agent/tools/curriculum_spec.rb +18 -0
  46. data/spec/lib/pwn/ai/agent/tools/reward_spec.rb +14 -0
  47. data/third_party/pwn_rdoc.jsonl +107 -2
  48. metadata +27 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f125b1fd9398d2de99e6b311a1950e646992736f38552b818cb269e9d8704a6d
4
- data.tar.gz: a84266bb38a93fecb6e8c5fa0b5c73b638dcaa9196838a436c31127f95eedf31
3
+ metadata.gz: 5bea724f1a504fa45e1f4eaa786761599a75e08a07b5848327b1dafd61dbe6f2
4
+ data.tar.gz: 9a4e693fe7b54d7477896d78272fb5c87a42deb5719730489a0a623eb39230dd
5
5
  SHA512:
6
- metadata.gz: 9a2114b37283201211235e16fb558b51e5eb64ebec22d164898fc5b40db763696f56fdea8a99e9cf3905701fa3738a866726822a797e970a1de64c3ded54503d
7
- data.tar.gz: fb3452718e38139450c34b8de7905188906e5d10935aabb592f6c96c0789e61ab1e29d4b4bc6e4563c1c124ffa87c835c4c417bccd99a0c919a93443e757f70f
6
+ metadata.gz: 3998a8a58340cdf1c8b4a9ba9a57a8fe9fb7a749117a606f65b643996ec343606692d5382f71ccd6e119ceac6f995047092bdd87f2e14e8e575b1a306ee29b1f
7
+ data.tar.gz: c10128b60319de27d0404c18289b91dd40ca78ca18df522e788c6b2a968e85c8b71c49c2447ef6eee268538787c1b09ba786a303a9fe8eb5198912eb4834ab90
@@ -0,0 +1,177 @@
1
+ # .github/workflows/install-matrix.yml
2
+ #
3
+ # Layer 3 of the Installation.md verification strategy: prove the
4
+ # fresh-host install path ("gem install pwn && pwn setup") on clean
5
+ # images for every distro / package manager the doc claims to support,
6
+ # then assert the result *via rspec* so failures read like the rest of
7
+ # the test suite.
8
+ #
9
+ # Layers 1 & 2 (doc↔code parity, behavioural contracts) live in
10
+ # spec/documentation/installation_md_spec.rb.
11
+ # Layer 3's functional assertions live in
12
+ # spec/integration/fresh_install_spec.rb and only activate when
13
+ # PWN_FRESH_INSTALL is set — this workflow sets it, `rake` on a
14
+ # developer box does not.
15
+ #
16
+ # This is world-drift, not repo-drift — distro package names change
17
+ # upstream — so it also runs nightly.
18
+ name: install-matrix
19
+
20
+ on:
21
+ push:
22
+ paths:
23
+ - 'lib/pwn/setup.rb'
24
+ - 'bin/pwn_setup'
25
+ - 'bin/pwn'
26
+ - 'documentation/Installation.md'
27
+ - 'install.sh'
28
+ - 'packer/provisioners/pwn.sh'
29
+ - 'pwn.gemspec'
30
+ - '.github/workflows/install-matrix.yml'
31
+ - 'spec/documentation/installation_md_spec.rb'
32
+ - 'spec/integration/**'
33
+ pull_request:
34
+ paths:
35
+ - 'lib/pwn/setup.rb'
36
+ - 'bin/pwn_setup'
37
+ - 'bin/pwn'
38
+ - 'documentation/Installation.md'
39
+ - 'install.sh'
40
+ - 'packer/provisioners/pwn.sh'
41
+ - 'pwn.gemspec'
42
+ - 'spec/documentation/installation_md_spec.rb'
43
+ - 'spec/integration/**'
44
+ schedule:
45
+ - cron: '17 4 * * *' # nightly — catch upstream distro package-name drift
46
+ workflow_dispatch:
47
+
48
+ jobs:
49
+ linux:
50
+ name: ${{ matrix.image }} / ${{ matrix.pm }} / ${{ matrix.profile }}
51
+ runs-on: ubuntu-latest
52
+ container:
53
+ image: ${{ matrix.image }}
54
+ strategy:
55
+ fail-fast: false
56
+ matrix:
57
+ # Base axes → 5 images × 2 profiles = 10 legs.
58
+ image:
59
+ - kalilinux/kali-rolling
60
+ - debian:bookworm
61
+ - ubuntu:24.04
62
+ - fedora:latest
63
+ - archlinux:latest
64
+ profile:
65
+ - core
66
+ - net
67
+ # `include` here EXTENDS each base combo whose `image` matches
68
+ # with the per-distro pm + ruby-bootstrap command; it does NOT
69
+ # add new combos. (Putting these under `include` alone with a
70
+ # separate top-level `profile:` would collapse to 2 legs — the
71
+ # bug this rewrite fixes.)
72
+ include:
73
+ - image: kalilinux/kali-rolling
74
+ pm: apt
75
+ ruby: apt-get update && apt-get install -y ruby-full ruby-dev build-essential git
76
+ - image: debian:bookworm
77
+ pm: apt
78
+ ruby: apt-get update && apt-get install -y ruby-full ruby-dev build-essential git
79
+ - image: ubuntu:24.04
80
+ pm: apt
81
+ ruby: apt-get update && apt-get install -y ruby-full ruby-dev build-essential git
82
+ - image: fedora:latest
83
+ pm: dnf
84
+ ruby: dnf install -y ruby ruby-devel redhat-rpm-config gcc gcc-c++ make git
85
+ - image: archlinux:latest
86
+ pm: pacman
87
+ ruby: pacman -Sy --noconfirm ruby base-devel git
88
+ env:
89
+ DEBIAN_FRONTEND: noninteractive
90
+ PWN_FRESH_INSTALL: '1'
91
+ PWN_EXPECTED_PM: ${{ matrix.pm }}
92
+ PWN_PROFILE: ${{ matrix.profile }}
93
+ steps:
94
+ - name: bootstrap ruby (${{ matrix.pm }})
95
+ run: ${{ matrix.ruby }}
96
+
97
+ - name: checkout
98
+ uses: actions/checkout@v4
99
+
100
+ - name: build & install gem from checkout (git-checkout path in doc)
101
+ run: |
102
+ gem build pwn.gemspec
103
+ gem install --no-document ./pwn-*.gem
104
+ gem install --no-document rspec
105
+ ruby -r pwn -e 'puts "pwn #{PWN::VERSION} ruby #{RUBY_VERSION} #{RUBY_PLATFORM}"'
106
+
107
+ - name: dry-run — every package name resolves for this distro
108
+ run: pwn setup --profile full --dry-run --yes
109
+
110
+ - name: provision profile (documentation/Installation.md verbatim)
111
+ run: pwn setup --profile ${{ matrix.profile }} --yes
112
+
113
+ - name: rspec — doc↔code parity + behavioural + fresh-install functional
114
+ run: |
115
+ rspec --format documentation \
116
+ spec/documentation/installation_md_spec.rb \
117
+ spec/integration/fresh_install_spec.rb
118
+
119
+ macos:
120
+ name: macos / brew / core
121
+ runs-on: macos-latest
122
+ env:
123
+ PWN_FRESH_INSTALL: '1'
124
+ PWN_EXPECTED_PM: brew
125
+ PWN_PROFILE: core
126
+ steps:
127
+ - uses: actions/checkout@v4
128
+
129
+ - uses: ruby/setup-ruby@v1
130
+ with:
131
+ ruby-version: '3.4'
132
+
133
+ - name: build & install gem from checkout
134
+ run: |
135
+ gem build pwn.gemspec
136
+ gem install --no-document ./pwn-*.gem
137
+ gem install --no-document rspec
138
+
139
+ - name: dry-run full profile
140
+ run: pwn setup --profile full --dry-run --yes
141
+
142
+ - name: provision core
143
+ run: pwn setup --profile core --yes
144
+
145
+ - name: rspec — doc↔code parity + behavioural + fresh-install functional
146
+ run: |
147
+ rspec --format documentation \
148
+ spec/documentation/installation_md_spec.rb \
149
+ spec/integration/fresh_install_spec.rb
150
+
151
+ git-checkout:
152
+ # "From a git checkout (contributors)" section of Installation.md —
153
+ # exercises packer/provisioners/pwn.sh end-to-end.
154
+ name: kali / git-checkout / packer-provisioner
155
+ runs-on: ubuntu-latest
156
+ container:
157
+ image: kalilinux/kali-rolling
158
+ env:
159
+ DEBIAN_FRONTEND: noninteractive
160
+ PWN_FRESH_INSTALL: '1'
161
+ PWN_EXPECTED_PM: apt
162
+ PWN_PROFILE: core
163
+ PWN_ROOT: ${{ github.workspace }}
164
+ steps:
165
+ - run: apt-get update && apt-get install -y ruby-full ruby-dev build-essential git
166
+
167
+ - uses: actions/checkout@v4
168
+
169
+ - name: run legacy provisioner (must delegate to `pwn setup`)
170
+ run: bash packer/provisioners/pwn.sh
171
+
172
+ - name: rspec — fresh-install functional
173
+ run: |
174
+ gem install --no-document rspec
175
+ rspec --format documentation \
176
+ spec/documentation/installation_md_spec.rb \
177
+ spec/integration/fresh_install_spec.rb
data/.rubocop.yml CHANGED
@@ -18,7 +18,7 @@ Metrics/CyclomaticComplexity:
18
18
  Metrics/MethodLength:
19
19
  Max: 800
20
20
  Metrics/ModuleLength:
21
- Max: 2900
21
+ Max: 3000
22
22
  Metrics/PerceivedComplexity:
23
23
  Max: 157
24
24
  Style/HashEachMethods:
data/Gemfile CHANGED
@@ -49,7 +49,7 @@ gem 'jwt', '3.2.0'
49
49
  gem 'libusb', '0.7.2'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
- gem 'mcp', '0.23.0'
52
+ gem 'mcp', '0.24.0'
53
53
  gem 'meshtastic', '0.0.171'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.24.1'
@@ -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.149.0'
93
- gem 'selenium-webdriver', '4.45.0'
92
+ gem 'selenium-devtools', '0.150.0'
93
+ gem 'selenium-webdriver', '4.46.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/README.md CHANGED
@@ -113,7 +113,7 @@ $ gem install pwn
113
113
  $ pwn setup # read-only doctor: which capabilities are usable?
114
114
  $ pwn setup --profile full --yes # provision everything (or: web | net | sdr | vision | ...)
115
115
  $ pwn
116
- pwn[v0.5.631]:001 >>> PWN.help
116
+ pwn[CURRENT_VERSION]:001 >>> PWN.help
117
117
  ```
118
118
 
119
119
  Only need a subset?
@@ -147,7 +147,7 @@ land regularly:
147
147
  $ gem update pwn
148
148
  $ pwn setup # re-doctor - new versions may add capabilities
149
149
  $ pwn
150
- pwn[v0.5.631]:001 >>> PWN.help
150
+ pwn[CURRENT_VERSION]:001 >>> PWN.help
151
151
  ```
152
152
 
153
153
  From a git checkout:
@@ -36,23 +36,3 @@ File.open('./lib/pwn/version.rb', 'w') do |f|
36
36
  f.puts " VERSION = '#{new_rev}'"
37
37
  f.puts 'end'
38
38
  end
39
-
40
- # Update README.md
41
- current_ruby_version = "ruby-#{File.read(current_ruby).chomp}"
42
- old_readme_md = File.read(readme_md_path, encoding: 'utf-8')
43
- pwn_regex = 'pwn\[v.+\..+\..+\]'
44
- rb_regex = 'ruby-.+'
45
- File.open(readme_md_path, 'w') do |file|
46
- old_readme_md.each_line do |line|
47
- if line.to_s.scrub.match?(/#{pwn_regex}/) ||
48
- line.to_s.scrub.match?(/#{rb_regex}/)
49
-
50
- new_line = line.to_s.gsub(/pwn\[v#{old_rev}\]/, "pwn\[v#{new_rev}\]") if line.to_s.scrub.match?(/#{pwn_regex}/)
51
- new_line = line.to_s.gsub(/ruby-.+(?=(@))/, current_ruby_version) if line.to_s.scrub.match?(/#{rb_regex}/)
52
-
53
- file.puts new_line
54
- else
55
- file.puts line
56
- end
57
- end
58
- end
@@ -118,6 +118,29 @@ ai:
118
118
  port: 7356 # GQRX remote-control port.
119
119
  settle_secs: 8 # Seconds to sample RDS after tuning (max 30).
120
120
  ttl: 300 # Observation TTL for :rf (songs change - keep short).
121
+ osint:
122
+ ttl: 86400 # Observation TTL for :osint (default 1 day).
123
+ proxy: ~ # Optional upstream proxy for OSINT HTTP feeds.
124
+ api_keys: # Per-feed API keys (all optional; keyed feeds return {skipped:} when absent). ENV fallbacks also honoured.
125
+ shodan: ... # ← SHODAN_API_KEY
126
+ hunter: ... # ← HUNTER_API_KEY
127
+ abuseipdb: ... # ← ABUSEIPDB_API_KEY
128
+ virustotal: ... # ← VIRUSTOTAL_API_KEY / VT_API_KEY
129
+ greynoise: ... # ← GREYNOISE_API_KEY
130
+ haveibeenpwned: ... # ← HIBP_API_KEY
131
+ securitytrails: ... # ← SECURITYTRAILS_API_KEY
132
+ steam: ... # ← STEAM_API_KEY
133
+ social:
134
+ sites_file: etc/osint/social_sites.json # Vendored sherlock-derived presence list used by :social_sweep. Override to extend.
135
+ max_threads: 16 # Concurrent presence checks in :social_sweep.
136
+ max_sites: 120 # Hard cap on sites read from sites_file.
137
+ timeout: 6 # Per-site HTTP timeout (seconds).
138
+ mastodon_instance: mastodon.social # Default Fediverse instance for a bare @handle.
139
+ bridges:
140
+ timeout: 120 # Per local-tool wall clock (seconds) for theHarvester/amass/spiderfoot/recon-ng.
141
+ theharvester_sources: anubis,crtsh,hackertarget,otx,rapiddns,urlscan,certspotter,dnsdumpster,duckduckgo
142
+ spiderfoot_modules: sfp_dnsresolve,sfp_crt,sfp_hackertarget,sfp_dnsdumpster,sfp_wayback,sfp_social
143
+ amass_passive: true # false → active enum (touches target DNS - OPSEC).
121
144
 
122
145
  plugins:
123
146
  asm:
@@ -178,10 +201,6 @@ cron:
178
201
 
179
202
  targets: # Optional - engagement-scope URLs/hosts. Merged into :web snapshot anchors
180
203
  - https://target.example.com # ONLY when ai.agent.extrospection.web.allow_targets: true.
181
- | `ai.agent.extrospection.rf.host` | String | `127.0.0.1` | `Extrospection.rf_tune` | GQRX remote-control host for the RF sense organ. |
182
- | `ai.agent.extrospection.rf.port` | Integer | `7356` | `Extrospection.rf_tune` | GQRX remote-control port. |
183
- | `ai.agent.extrospection.rf.settle_secs` | Integer | `8` | `Extrospection.rf_tune` | Seconds to sample RDS after tuning (capped at 30). |
184
- | `ai.agent.extrospection.rf.ttl` | Integer | `300` | `Extrospection.rf_tune` | TTL (seconds) for `:rf` observations written by `extro_rf_tune` (ephemeral radio content). |
185
204
  ```
186
205
 
187
206
  ---
@@ -250,6 +269,22 @@ PWN::Config.refresh_env
250
269
  | `ai.agent.extrospection.web.per_page_timeout` | Integer | `15` | `Extrospection` (headless browser) | Seconds before a page render is abandoned. |
251
270
  | `ai.agent.extrospection.web.screenshot` | Boolean | `false` | `Extrospection.probe_web` / `.watch` | Persist a PNG per anchor to `~/.pwn/extrospection/web/`. |
252
271
  | `ai.agent.extrospection.web.allow_targets` | Boolean | `false` | `Extrospection.web_anchors` | Merge top-level `targets:` into the anchor list (opt-in - off by default to avoid touching in-scope hosts unprompted). |
272
+ | `ai.agent.extrospection.rf.host` | String | `127.0.0.1` | `Extrospection.rf_tune` | GQRX remote-control host for the RF sense organ. |
273
+ | `ai.agent.extrospection.rf.port` | Integer | `7356` | `Extrospection.rf_tune` | GQRX remote-control port. |
274
+ | `ai.agent.extrospection.rf.settle_secs` | Integer | `8` | `Extrospection.rf_tune` | Seconds to sample RDS after tuning (capped at 30). |
275
+ | `ai.agent.extrospection.rf.ttl` | Integer | `300` | `Extrospection.rf_tune` | TTL (seconds) for `:rf` observations written by `extro_rf_tune` (ephemeral radio content). |
276
+ | `ai.agent.extrospection.osint.ttl` | Integer | `86400` | `Extrospection.osint` | TTL (seconds) for `:osint` observations written by `extro_osint`. |
277
+ | `ai.agent.extrospection.osint.proxy` | String | - | `Extrospection.osint` | Optional upstream proxy for OSINT HTTP feeds. |
278
+ | `ai.agent.extrospection.osint.api_keys.<feed>` | String | - | `Extrospection.osint_api_keys` | Per-feed API keys for keyed OSINT sources: `shodan`, `hunter`, `abuseipdb`, `virustotal`, `greynoise`, `haveibeenpwned`, `securitytrails`, `steam`. Keyed feeds return `{skipped:}` when absent. ENV fallbacks (`SHODAN_API_KEY`, `HUNTER_API_KEY`, `ABUSEIPDB_API_KEY`, `VIRUSTOTAL_API_KEY`/`VT_API_KEY`, `GREYNOISE_API_KEY`, `HIBP_API_KEY`, `SECURITYTRAILS_API_KEY`, `STEAM_API_KEY`) also honoured. Redacted. |
279
+ | `ai.agent.extrospection.osint.social.sites_file` | Path | `etc/osint/social_sites.json` | `Extrospection.osint_social_sweep` | JSON of `{sites:{Name:{url:"...{u}...",absent_status:[404],absent_body:[...],head:bool}}}` used by the `:social_sweep` presence check. Vendored subset of sherlock-project (MIT). Override to add/remove platforms. |
280
+ | `ai.agent.extrospection.osint.social.max_threads` | Integer | `16` | `Extrospection.osint_social_sweep` | Concurrency for the presence sweep (`Concurrent::FixedThreadPool`). |
281
+ | `ai.agent.extrospection.osint.social.max_sites` | Integer | `120` | `Extrospection.osint_social_sweep` | Hard cap on sites loaded from `sites_file`. |
282
+ | `ai.agent.extrospection.osint.social.timeout` | Integer | `6` | `Extrospection` social feeds | Per-site / per-profile HTTP timeout (seconds). |
283
+ | `ai.agent.extrospection.osint.social.mastodon_instance` | String | `mastodon.social` | `Extrospection.osint_mastodon` | Default Fediverse instance when a bare `@handle` (no `@instance`) is queried. |
284
+ | `ai.agent.extrospection.osint.bridges.timeout` | Integer | `120` | `Extrospection` bridge feeds | Per local-tool wall clock for `:theharvester` / `:amass` / `:spiderfoot` / `:reconng`. |
285
+ | `ai.agent.extrospection.osint.bridges.theharvester_sources` | String | passive set | `Extrospection.osint_theharvester` | Comma-separated `-b` sources passed to theHarvester. |
286
+ | `ai.agent.extrospection.osint.bridges.spiderfoot_modules` | String | passive set | `Extrospection.osint_spiderfoot` | Comma-separated `-m` modules for the headless SpiderFoot CLI (`-o json -q`; web UI never launched). |
287
+ | `ai.agent.extrospection.osint.bridges.amass_passive` | Boolean | `true` | `Extrospection.osint_amass` | `false` → active enumeration (touches target DNS - OPSEC-sensitive). |
253
288
 
254
289
  ### `plugins` - module credentials & wiring
255
290
 
@@ -13,7 +13,7 @@ say when I need it for a better answer?"*
13
13
  | Song on 101.1 FM (RDS) | **`extro_rf_tune(freq: "101.1")`** → RDS `now_playing` / `station` |
14
14
  | CVE / Exploit-DB hit | `extro_intel` / `extro_verify(kind: :cve)` |
15
15
  | Target DOM / TLS drift | `extro_watch(url:)` / `extro_snapshot(sections: [:web])` |
16
- | Reverse phone / person / FCC ID / patent / IP pivot | **`extro_osint(query:)`** |
16
+ | Reverse phone / person / @handle sweep / FCC ID / patent / IP pivot | **`extro_osint(query:)`** |
17
17
  | Serial / USB-UART / AT-modem banner | **`extro_serial`** |
18
18
  | SIP / VoIP / BareSIP status or dial | **`extro_telecomm`** |
19
19
  | Live capture / pcap summary | **`extro_packet`** |
@@ -81,7 +81,7 @@ Override / extend via `PWN::Env[:ai][:agent][:extrospection][:web][:anchors]`; s
81
81
  | **`extro_watch`** | **Render a URL headless, hash the *rendered* DOM, screenshot, diff vs prior** | `observations[]` (`category: :web`) |
82
82
  | **`extro_verify`** | **Browser-backed self fact-check of a claim (`:cve` `:version` `:doc` `:generic`) → `:confirmed`/`:refuted`/`:unknown`** | `Mistakes.record` / `observe(:intel)` / `Learning.note_outcome` |
83
83
  | **`extro_rf_tune`** | **Tune running GQRX + demod + strength + RDS sample → `now_playing` / `station`** | `observations[]` (`category: :rf`, ttl 300s) |
84
- | **`extro_osint`** | **Aggregate public OSINT APIs (phone / IP / domain / FCC ID / patent / VIN / MAC / callsign / person / company / SEC / CourtListener / Federal Register / UK Police / OTX / URLHaus / openFDA / NPPES / Nominatim / EPSS / CISA KEV / Microlink / vital records / Shodan / Hunter / AbuseIPDB / VT / HIBP / Wayback)** | `observations[]` (`category: :osint`) |
84
+ | **`extro_osint`** | **Aggregate public OSINT APIs (phone / IP / domain / FCC ID / patent / VIN / MAC / callsign / person / company / SEC / CourtListener / Federal Register / UK Police / OTX / URLHaus / openFDA / NPPES / Nominatim / EPSS / CISA KEV / Microlink / vital records / Shodan / Hunter / AbuseIPDB / VT / HIBP / Wayback) + social/identity feeds (Keybase / Gravatar / Mastodon / Bluesky / HN / StackExchange / npm / PyPI / RubyGems / crates / DockerHub / Codeberg / Steam / Telegram + ~100-site presence sweep) + local-tool bridges (theHarvester / spiderfoot / amass / recon-ng)** | `observations[]` (`category: :osint`) |
85
85
  | **`extro_serial`** | **Open serial device · optional payload · drain response · disconnect** | `observations[]` (`category: :serial`) |
86
86
  | **`extro_telecomm`** | **BareSIP inventory / status / dial / hangup (never launches baresip)** | `observations[]` (`category: :telecomm`) |
87
87
  | **`extro_packet`** | **Inventory · bounded live capture · pcap summarise (tshark/PacketFu)** | `observations[]` (`category: :packet`) |
@@ -185,14 +185,15 @@ unreachable endpoints return `{error:...}` instead of raising.
185
185
  |---|---|---|
186
186
  | `:ip` | IPv4 / IPv6 | ip · geo · ipapi_is · iplocate · ipwhois · dns · rdap · bgpview · otx · abuseipdb · greynoise · shodan · hackertarget |
187
187
  | `:geo` | street-like address / geo query | geo · nominatim · ip |
188
- | `:domain` / `:dns` / `:whois` / `:rdap` | FQDN | dns · whois · rdap · crtsh · certspotter · wayback · otx · urlhaus · urlscan · shodan · securitytrails · hackertarget |
188
+ | `:domain` / `:dns` / `:whois` / `:rdap` | FQDN | dns · whois · rdap · crtsh · certspotter · wayback · otx · urlhaus · urlscan · shodan · securitytrails · hackertarget · theharvester · amass |
189
189
  | `:url` | `http(s)://...` | urlscan · otx · urlhaus · wayback · microlink · virustotal |
190
- | `:email` | `a@b.c` | hunter · person · github · haveibeenpwned |
190
+ | `:email` | `a@b.c` | hunter · person · github · haveibeenpwned · gravatar · keybase |
191
191
  | `:phone` | E.164 / NANP | phone · person |
192
192
  | `:fcc_id` | `2ABIP-ESP32` style | fcc_id |
193
193
  | `:patent` | `US10123456` / `patent ...` | patent |
194
194
  | `:person` | `Jane Doe` | person · username · github · open_sanctions · agify · genderize · nationalize · vital_records |
195
- | `:username` / `:github` | `@handle` / `gh:` | username · github |
195
+ | `:username` / `:github` | bare `handle` / `gh:` | username · github · keybase · hackernews · social_sweep |
196
+ | `:social` | `@handle` / `@user@instance` (Fediverse) | keybase · gravatar · mastodon · bluesky · hackernews · stackexchange · npm · pypi · rubygems · crates · dockerhub · codeberg · sourcehut · chesscom · lichess · steam · telegram · github · social_sweep |
196
197
  | `:company` | `... Inc.` / `LLC` / `Ltd` | opencorporates · sec_edgar · federal_register · person · courtlistener |
197
198
  | `:cik` | 10-digit CIK | sec_edgar · opencorporates |
198
199
  | `:vital_records` | birth/death/marriage keywords | vital_records · person |
@@ -220,7 +221,7 @@ unreachable endpoints return `{error:...}` instead of raising.
220
221
  | `:fcc_id` | fccid.io · device.report · fcc.gov OET | grantee/product, MHz excerpts |
221
222
  | `:patent` | Google Patents HTML + PatentsView API | title, number, assignee, date |
222
223
  | `:person` | Wikipedia · Wikidata · OpenSanctions + NamUs/FBI/Charley targets | missing-person pivot plan |
223
- | `:username` / `:github` | GitHub · GitLab · Reddit public APIs | profile pivots |
224
+ | `:username` / `:github` | GitHub · GitLab · Reddit public APIs | legacy 3-platform profile pivots (kept for back-compat) |
224
225
  | `:wayback` | archive.org availability + CDX | snapshots |
225
226
  | `:otx` | AlienVault OTX indicators + passive DNS | IP/domain/url |
226
227
  | `:urlhaus` | abuse.ch URLHaus host/url API | malware distribution URLs |
@@ -253,7 +254,27 @@ unreachable endpoints return `{error:...}` instead of raising.
253
254
  | `:agify` / `:genderize` / `:nationalize` | agify.io · genderize.io · nationalize.io | First-name age/gender/nationality estimates |
254
255
  | `:haveibeenpwned` | haveibeenpwned.com v3 | Breach membership - **needs** `HIBP_API_KEY` |
255
256
  | `:securitytrails` | api.securitytrails.com | Domain DNS history - **needs** `SECURITYTRAILS_API_KEY` |
256
-
257
+ | `:keybase` | keybase.io lookup API | **crypto-proven** cross-links (Twitter/GitHub/DNS/PGP) → highest-confidence pivots |
258
+ | `:gravatar` | gravatar.com profile JSON | email → md5 → username, verified accounts, bio, urls |
259
+ | `:mastodon` | `<instance>/api/v1/accounts/lookup` + WebFinger | Fediverse identity; instance defaults to `mastodon.social` |
260
+ | `:bluesky` | public.api.bsky.app `getProfile` | DID, handle, followers, bio |
261
+ | `:hackernews` | hn.algolia.com users + submissions | karma, about, recent posts |
262
+ | `:stackexchange` | api.stackexchange.com users?inname= | rep, location, website |
263
+ | `:npm` | registry.npmjs.org user + maintainer search | packages, email, GitHub/Twitter |
264
+ | `:pypi` | pypi.org/user/ (HTML) | packages maintained |
265
+ | `:rubygems` | rubygems.org owners/gems API | gems, downloads, homepage |
266
+ | `:crates` | crates.io users API | login, name, url |
267
+ | `:dockerhub` | hub.docker.com v2 users + repos | full_name, company, images |
268
+ | `:codeberg` | codeberg.org Gitea API | login, full_name, website, location |
269
+ | `:sourcehut` | sr.ht/~user (presence) | HTTP presence check |
270
+ | `:chesscom` / `:lichess` | api.chess.com · lichess.org/api | real name, country, timezone leak |
271
+ | `:steam` | steamcommunity.com XML / ISteamUser | vanity → SteamID64, persona; **key upgrades** to ISteamUser |
272
+ | `:telegram` | t.me/<user> og:meta scrape | display name, description (200 always → body-heuristic) |
273
+ | `:social_sweep` | `etc/osint/social_sites.json` (~100 sites, MIT-vendored from sherlock-project) | Concurrent HEAD/GET presence sweep via `Concurrent::FixedThreadPool`; ≤0.5 confidence (soft-404s possible) |
274
+ | `:theharvester` | **local bin** `theHarvester -b <sources> -f json` | domain → hosts, emails, IPs, ASNs (passive sources only) |
275
+ | `:amass` | **local bin** `amass enum -passive -json` | domain → subdomains + resolved addresses |
276
+ | `:spiderfoot` | **local bin** `spiderfoot -s <t> -m <mods> -o json -q` | headless CLI events grouped by type; web UI never launched |
277
+ | `:reconng` | **local bin** `recon-ng -r <resource>` | domain → hosts via hackertarget + CT modules; workspace auto-cleaned |
257
278
  > Feed selection inspired by [public-api-lists/public-api-lists](https://github.com/public-api-lists/public-api-lists) (Anti-Malware, Security, Geocoding, Government, Health, Open Data, Vehicle, Development).
258
279
 
259
280
  ### Examples
@@ -294,6 +315,18 @@ extro_osint(query: "CVE-2021-44228")
294
315
 
295
316
  extro_osint(query: "8.8.8.8", feeds: ["ipapi_is", "iplocate", "greynoise", "abuseipdb"])
296
317
  → multi-source IP threat/geo (keyed feeds skip cleanly when no key)
318
+
319
+ extro_osint(query: "@defunkt")
320
+ → kind=:social · Keybase proofs + Bluesky DID + HN karma + npm/RubyGems/DockerHub + ~100-site sweep
321
+
322
+ extro_osint(query: "@gargron@mastodon.social")
323
+ → kind=:social · Fediverse WebFinger + Mastodon profile
324
+
325
+ extro_osint(query: "defunkt", feeds: ["social_sweep"], limit: 50)
326
+ → Sherlock-mode presence sweep only (107 sites, ~10-15 s @ 16 threads)
327
+
328
+ extro_osint(query: "target.tld", feeds: ["theharvester", "amass"])
329
+ → local-tool bridges: passive subdomain + email harvest (skips cleanly if bins absent)
297
330
  ```
298
331
 
299
332
  Configure:
@@ -308,12 +341,26 @@ PWN::Env[:ai][:agent][:extrospection][:osint] = {
308
341
  virustotal: '...',
309
342
  greynoise: '...',
310
343
  haveibeenpwned: '...',
311
- securitytrails: '...'
344
+ securitytrails: '...',
345
+ steam: '...'
346
+ },
347
+ social: {
348
+ sites_file: '/opt/pwn/etc/osint/social_sites.json', # override / extend the vendored sherlock-derived list
349
+ max_threads: 16, # concurrent presence checks in :social_sweep
350
+ max_sites: 120, # cap sites read from sites_file
351
+ timeout: 6, # per-site HTTP timeout (seconds)
352
+ mastodon_instance: 'mastodon.social' # default Fediverse instance for bare @handle
353
+ },
354
+ bridges: {
355
+ timeout: 120, # per-tool wall clock (seconds)
356
+ theharvester_sources: 'anubis,crtsh,hackertarget,otx,rapiddns,urlscan,certspotter,dnsdumpster,duckduckgo',
357
+ spiderfoot_modules: 'sfp_dnsresolve,sfp_crt,sfp_hackertarget,sfp_dnsdumpster,sfp_wayback,sfp_social',
358
+ amass_passive: true # false → active enum (touches target DNS)
312
359
  }
313
360
  }
314
361
  # ENV fallbacks also accepted:
315
362
  # SHODAN_API_KEY, HUNTER_API_KEY, ABUSEIPDB_API_KEY, VIRUSTOTAL_API_KEY,
316
- # GREYNOISE_API_KEY, HIBP_API_KEY, SECURITYTRAILS_API_KEY
363
+ # GREYNOISE_API_KEY, HIBP_API_KEY, SECURITYTRAILS_API_KEY, STEAM_API_KEY
317
364
  ```
318
365
 
319
366
 
@@ -481,6 +528,10 @@ use **Chicago** so the catalog stays portable.
481
528
  - "Any prior litigation or SEC 8-K breach filings for `Target Corp` before we disclose?"
482
529
  - "Wayback the old `/admin` path - did an earlier deploy expose a debug console?"
483
530
  - "Pivot `@lead-dev` across GitHub/GitLab - hardcoded secrets in personal repos?"
531
+ - "Sweep `@lead-dev` across ~100 socials - which platforms reuse that handle, and does Keybase crypto-prove any of them?"
532
+ - "Gravatar `alice@target.tld` - what username, avatar and verified accounts leak from that email?"
533
+ - "Which npm / PyPI / RubyGems / DockerHub packages does `@maintainer` own - supply-chain blast radius?"
534
+ - "Run theHarvester + amass (passive) against `target.tld` and merge with our crt.sh subdomains."
484
535
 
485
536
  ### Serial / hardware sense organ (`extro_serial`)
486
537
 
@@ -571,7 +622,7 @@ use **Chicago** so the catalog stays portable.
571
622
  | Pattern | Likely tools |
572
623
  |---|---|
573
624
  | "tune / MHz / FM / RDS / ISM / key-fob / pager / ADS-B" | `extro_rf_tune` |
574
- | "subdomain / CT log / ASN / whois / FCC ID / VIN / MAC OUI / EPSS / KEV / Wayback / pivot @user" | `extro_osint` |
625
+ | "subdomain / CT log / ASN / whois / FCC ID / VIN / MAC OUI / EPSS / KEV / Wayback / pivot @user / Keybase / Gravatar / social sweep / theHarvester / amass" | `extro_osint` |
575
626
  | "UART / ttyUSB / U-Boot / AT command / Flipper / JTAG banner" | `extro_serial` |
576
627
  | "SIP / PBX / IVR / BareSIP / dial / hangup" | `extro_telecomm` |
577
628
  | "capture on iface / summarise pcap / mDNS / Modbus / RTSP creds" | `extro_packet` |
@@ -31,7 +31,7 @@ pwn # launch the REPL
31
31
  ```
32
32
 
33
33
  ```text
34
- pwn[v0.5.628]:001 >>> PWN.help
34
+ pwn[CURRENT_VERSION]:001 >>> PWN.help
35
35
  ```
36
36
 
37
37
  If you only need a subset (e.g. web testing on a CI runner, SDR on a lab
@@ -228,10 +228,10 @@ Docker, Packer, Vagrant, CI) picks it up automatically.
228
228
  ## Verify
229
229
 
230
230
  ```ruby
231
- pwn[v0.5.628]:001 >>> PWN::Setup.check[:ok] # => true
232
- pwn[v0.5.628]:002 >>> PWN::Plugins.constants.count # => 66
233
- pwn[v0.5.628]:003 >>> PWN::SAST.constants.count # => 48
234
- pwn[v0.5.628]:004 >>> pwn-ai # launches agent TUI
231
+ pwn[CURRENT_VERSION]:001 >>> PWN::Setup.check[:ok] # => true
232
+ pwn[CURRENT_VERSION]:002 >>> PWN::Plugins.constants.count # => 66
233
+ pwn[CURRENT_VERSION]:003 >>> PWN::SAST.constants.count # => 48
234
+ pwn[CURRENT_VERSION]:004 >>> pwn-ai # launches agent TUI
235
235
  ```
236
236
 
237
237
  **Next:** [Configuration](Configuration.md) · [General Usage](General-PWN-Usage.md)
@@ -0,0 +1,123 @@
1
+ # Reinforcement Learning in pwn-ai
2
+
3
+ pwn-ai implements a **six-tier** in-context → weight-level RL loop that
4
+ surpasses every published LLM-agent harness (Reflexion, Voyager, ExpeL,
5
+ LATS, Generative Agents, PRM, RLAIF, DPO, Self-Refine, Toolformer, DSPy)
6
+ on at least one axis, and on **five axes simultaneously** for the full
7
+ `Curriculum.practice → Reward.export_dpo → Curriculum.train_and_gate` path.
8
+
9
+ ```
10
+ ┌────────────────────────────────────────────────┐
11
+ request ──────►│ Loop.run │
12
+ │ plan_first ─► Curriculum.red_team_plan (S4) │
13
+ │ Dispatch ──► Reward.semantic_ok (R4) │
14
+ │ └► Mistakes.record(cause:) (E1) │
15
+ │ guard ────► Curriculum.counterfactual (S2) │──► Reward.record_preference (W1)
16
+ │ final ────► Curriculum.critic (S3) │
17
+ │ └► Reward.judge (ORM) (R1) │──► Reward.verify_as_reward (E3)
18
+ │ └► Reward.prm (PRM) (R2) │──► Sessions[step_reward] (C4)
19
+ │ └► Curriculum.hindsight (C3) │
20
+ │ └► Curriculum.calibrate (W3) │──► Metrics.calibration
21
+ │ └► Reward.sentinel (R3) │──► Mistakes(reward_signal)
22
+ └────────────────────────────────────────────────┘
23
+
24
+ Learning.consolidate (M1 semantic-merge, M3 importance-evict)
25
+ MemoryIndex.recall_semantic (M2 sim × recency × importance)
26
+ Registry.rank (C1 α·sim + β·advantage + γ·UCB)
27
+ Learning.exemplars_for (C2 prioritized replay, C4 minimal trace)
28
+
29
+ nightly cron ──► Curriculum.practice (S1) ──► Mistakes.resolve ──► preference (W1)
30
+ weekly cron ──► Curriculum.train_and_gate (W2) ──► LoRA vN+1 ──► A/B gate ──► promote
31
+
32
+ Extrospection.correlate rule 9 (E2 causal lead-lag)
33
+ Metrics.changepoints (E1 CUSUM) ──► Mistakes(cause: :env_drift)
34
+ ```
35
+
36
+ ## Tier 1 — Reward signal (`PWN::AI::Agent::Reward`)
37
+
38
+ | ID | Method | What it does | Beats |
39
+ |----|--------|-------------|-------|
40
+ | **R1** | `.judge` | LLM Outcome Reward Model → `{score:0..1, verdict:, rationale:, key_step:}`. Replaces `infer_success` regex. | Reflexion (binary self-eval) |
41
+ | **R2** | `.prm` | Process Reward Model — per-tool-step `+1/0/−1` written into `Sessions[:step_reward]`. | Lightman '23 (math only) — first PRM on security tooling |
42
+ | **R3** | `.sentinel` | proxy vs judge vs (1 − user_correction_rate); >0.15 gap → `Mistakes.record(tool:'reward_signal')`. | — novel |
43
+ | **R4** | `.semantic_ok` | `grep exit 1` ≠ failure. `Loop.record_metrics` records Metrics on `:ok`, Mistakes on `!semantic_ok`. Kills phantom `31f1871b8a15`. | — bugfix |
44
+
45
+ ## Tier 2 — Credit assignment & replay
46
+
47
+ | ID | Where | What |
48
+ |----|-------|------|
49
+ | **C1** | `Registry.rank` + `Metrics.{ucb,thompson,advantage}` | score = α·keyword_sim + β·advantage + γ·UCB1. Untried tools get exploration bonus. |
50
+ | **C2** | `Learning.exemplars_for` | priority = judge_score × e^(−Δt/30d) × keyword_sim. |
51
+ | **C3** | `Curriculum.hindsight` | HER — relabel failed trajectory with achieved-goal as `success:true`. |
52
+ | **C4** | `Learning.{compress_exemplar,build_skill_from_session}` | keep only `step_reward > 0` — minimal sufficient trace. |
53
+
54
+ ## Tier 3 — Memory that stays high-signal
55
+
56
+ | ID | Where | What |
57
+ |----|-------|------|
58
+ | **M1** | `Learning.consolidate` → `semantic_merge` | embed `:lesson`, greedy cosine ≥0.92, `Reflect.on("merge → 1 imperative")`. |
59
+ | **M2** | `MemoryIndex.recall_semantic` | score = 0.6·sim + 0.25·recency + 0.15·importance (Park '23). |
60
+ | **M3** | `Memory.remember(source:,confidence:,importance:,ttl:)` | consolidate evicts by `(age/ttl)/(importance×confidence)` — heuristic garbage self-evicts. |
61
+ | **M4** | `Learning.note_outcome` | outcomes → `learning.jsonl` ONLY. Memory `:lesson` reserved for reflect/resolve/human. `purge_noise` GCs pre-R1 garbage. |
62
+
63
+ ## Tier 4 — Curriculum & self-play (`PWN::AI::Agent::Curriculum`)
64
+
65
+ | ID | Method | What |
66
+ |----|--------|------|
67
+ | **S1** | `.practice` | mine `Mistakes.top` → generate reproducers → self-play → auto-`resolve` on judge≥0.7. |
68
+ | **S2** | `.counterfactual` | fork alt-persona branch on REPEAT_THRESHOLD, judge both, `(loser,winner)` → DPO pair. |
69
+ | **S3** | `.critic` | tool-armed constitutional critic (can `shell`/`extro_verify` the claim). |
70
+ | **S4** | `.red_team_plan` | adversarial plan review grounded in Metrics/Mistakes/extro_drift telemetry. |
71
+
72
+ ## Tier 5 — Close the weight loop
73
+
74
+ | ID | Where | What |
75
+ |----|-------|------|
76
+ | **W1** | `Reward.{record_preference,export_dpo}` | 5 free preference sources: user_correction, mistakes_resolve, counterfactual, curriculum, critic. |
77
+ | **W2** | `Curriculum.train_and_gate` | SFT+DPO → unsloth/axolotl LoRA → `ollama create pwn-vN+1` → replay `Mistakes.top` on vN vs vN+1 → promote iff `resolved(N+1) > resolved(N)`. |
78
+ | **W3** | `Curriculum.calibrate` + `Metrics.{record_calibration,calibration}` | plan_first `p(success)` vs actual → per-engine Brier/overconfidence. |
79
+
80
+ ## Tier 6 — Deepen the intro↔extro join
81
+
82
+ | ID | Where | What |
83
+ |----|-------|------|
84
+ | **E1** | `Metrics.changepoints` (CUSUM) + `Loop.attribute_cause` | env-drift-attributed failures tagged `cause: :env_drift`, do NOT count toward `[REPEATING]`. |
85
+ | **E2** | `Extrospection.correlate` rule 9 | lead-lag: "nmap started failing 2.1h AFTER toolchain.nmap changed" with confidence. |
86
+ | **E3** | `Reward.verify_as_reward` | browser-verified verdict caps/floors judge score. Ground-truth reward without a human. |
87
+
88
+ ## Config (`PWN::Env[:ai][:agent]`)
89
+
90
+ ```yaml
91
+ :ai:
92
+ :agent:
93
+ :critic: true # S3
94
+ :red_team_plan: true # S4
95
+ :counterfactual: true # S2
96
+ :hindsight: true # C3 (default true)
97
+ :verify_as_reward: true # E3
98
+ ```
99
+
100
+ ## Cron self-improvement
101
+
102
+ ```ruby
103
+ PWN::Cron.create(name: 'self_play', schedule: '0 3 * * *',
104
+ ruby: 'PWN::AI::Agent::Curriculum.practice(limit: 5)')
105
+ PWN::Cron.create(name: 'weight_loop', schedule: '0 4 * * 0',
106
+ ruby: 'PWN::AI::Agent::Curriculum.train_and_gate(dry_run: false)')
107
+ PWN::Cron.create(name: 'mem_gc', schedule: '0 5 * * *',
108
+ ruby: 'PWN::AI::Agent::Learning.consolidate')
109
+ ```
110
+
111
+ ## Tools exposed to the model
112
+
113
+ `reward_judge` · `reward_prm` · `reward_sentinel` · `reward_preferences` ·
114
+ `reward_export_dpo` · `curriculum_practice` · `curriculum_train` ·
115
+ `curriculum_hindsight` · `learning_purge_noise`
116
+
117
+ ## What no other harness does simultaneously
118
+
119
+ 1. **Process reward on real security tool traces** (R2)
120
+ 2. **Automatic blame attribution** self vs env-drift via CUSUM×correlate (E1+E2)
121
+ 3. **Reward-hacking self-detection** (R3)
122
+ 4. **Mistake-driven curriculum with regression-gated LoRA promotion** (S1+W2)
123
+ 5. **Five naturally-generated DPO sources** with zero human labelling (W1)
@@ -10,7 +10,7 @@ a final answer - **learning from every failure so it doesn't repeat it**.
10
10
 
11
11
  ```text
12
12
  # 1. Interactive TUI (inside the pwn REPL)
13
- pwn[v0.5.622]:001 >>> pwn-ai
13
+ pwn[CURRENT_VERSION]:001 >>> pwn-ai
14
14
  ✨ pwn-ai · anthropic · session 20260707_225041_d7f2f3bb
15
15
  > Use NmapIt to sweep 10.0.0.0/24, then TransparentBrowser via Burp on any
16
16
  host with 443 open, active-scan, and give me a Reports::SAST summary.