pwn 0.5.631 → 0.5.634

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/install-matrix.yml +187 -0
  3. data/.rubocop.yml +2 -1
  4. data/Gemfile +5 -5
  5. data/README.md +2 -2
  6. data/Rakefile +1 -1
  7. data/bin/pwn_autoinc_version +0 -20
  8. data/bin/pwn_fuzz_net_app_proto +1 -7
  9. data/documentation/Agent-Tool-Registry.md +2 -2
  10. data/documentation/Configuration.md +39 -4
  11. data/documentation/Extrospection.md +61 -10
  12. data/documentation/Installation.md +6 -6
  13. data/documentation/Reinforcement-Learning.md +123 -0
  14. data/documentation/pwn-ai-Agent.md +1 -1
  15. data/etc/osint/social_sites.json +116 -0
  16. data/git_commit.sh +1 -1
  17. data/install.sh +27 -18
  18. data/lib/pwn/ai/agent/curriculum.rb +561 -0
  19. data/lib/pwn/ai/agent/extrospection/osint/bridges.rb +240 -0
  20. data/lib/pwn/ai/agent/extrospection/osint/social.rb +405 -0
  21. data/lib/pwn/ai/agent/extrospection.rb +81 -11
  22. data/lib/pwn/ai/agent/learning.rb +200 -55
  23. data/lib/pwn/ai/agent/loop.rb +62 -19
  24. data/lib/pwn/ai/agent/metrics.rb +172 -2
  25. data/lib/pwn/ai/agent/mistakes.rb +54 -5
  26. data/lib/pwn/ai/agent/prompt_builder.rb +7 -3
  27. data/lib/pwn/ai/agent/registry.rb +19 -9
  28. data/lib/pwn/ai/agent/reward.rb +523 -0
  29. data/lib/pwn/ai/agent/tools/curriculum.rb +95 -0
  30. data/lib/pwn/ai/agent/tools/extrospection.rb +9 -4
  31. data/lib/pwn/ai/agent/tools/reward.rb +117 -0
  32. data/lib/pwn/ai/agent/tools/skills.rb +122 -63
  33. data/lib/pwn/ai/agent.rb +2 -0
  34. data/lib/pwn/config.rb +256 -32
  35. data/lib/pwn/cron.rb +39 -0
  36. data/lib/pwn/ffi/stdio.rb +3 -1
  37. data/lib/pwn/memory.rb +15 -4
  38. data/lib/pwn/memory_index.rb +12 -1
  39. data/lib/pwn/plugins/fuzz.rb +9 -7
  40. data/lib/pwn/plugins/github.rb +205 -47
  41. data/lib/pwn/setup.rb +16 -5
  42. data/lib/pwn/version.rb +1 -1
  43. data/packer/provisioners/pwn.sh +58 -93
  44. data/pwn.gemspec +35 -3
  45. data/spec/documentation/installation_md_spec.rb +173 -0
  46. data/spec/documentation/prompt_version_placeholder_spec.rb +37 -0
  47. data/spec/integration/bin_drivers_spec.rb +73 -0
  48. data/spec/integration/config_spec.rb +68 -0
  49. data/spec/integration/dispatch_spec.rb +106 -0
  50. data/spec/integration/documentation_sync_spec.rb +74 -0
  51. data/spec/integration/extrospection_classify_spec.rb +90 -0
  52. data/spec/integration/fresh_install_spec.rb +73 -0
  53. data/spec/integration/help_contract_spec.rb +104 -0
  54. data/spec/integration/install_doc.sh +79 -0
  55. data/spec/integration/persistence_roundtrip_spec.rb +112 -0
  56. data/spec/integration/prompt_builder_spec.rb +68 -0
  57. data/spec/integration/reinforced_feedback_loop_spec.rb +574 -0
  58. data/spec/integration/reports_spec.rb +76 -0
  59. data/spec/integration/result_condition_spec.rb +77 -0
  60. data/spec/integration/sast_functional_spec.rb +80 -0
  61. data/spec/integration/tool_registry_spec.rb +91 -0
  62. data/spec/lib/pwn/ai/agent/curriculum_spec.rb +56 -0
  63. data/spec/lib/pwn/ai/agent/extrospection/osint/bridges_spec.rb +24 -0
  64. data/spec/lib/pwn/ai/agent/extrospection/osint/social_spec.rb +36 -0
  65. data/spec/lib/pwn/ai/agent/extrospection_spec.rb +68 -0
  66. data/spec/lib/pwn/ai/agent/learning_spec.rb +1 -1
  67. data/spec/lib/pwn/ai/agent/reward_spec.rb +58 -0
  68. data/spec/lib/pwn/ai/agent/tools/curriculum_spec.rb +18 -0
  69. data/spec/lib/pwn/ai/agent/tools/reward_spec.rb +14 -0
  70. data/spec/lib/pwn/ai/agent/tools/skills_spec.rb +88 -1
  71. data/spec/lib/pwn/ffi/adalm_pluto_spec.rb +5 -1
  72. data/spec/spec_helper.rb +2 -0
  73. data/spec/support/sandbox.rb +77 -0
  74. data/third_party/pwn_rdoc.jsonl +123 -7
  75. metadata +57 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f125b1fd9398d2de99e6b311a1950e646992736f38552b818cb269e9d8704a6d
4
- data.tar.gz: a84266bb38a93fecb6e8c5fa0b5c73b638dcaa9196838a436c31127f95eedf31
3
+ metadata.gz: 8632bee6e87072a40fbd993834156ad06e1c448918b2922125d592debd7c033d
4
+ data.tar.gz: 399687f4f6ea85666f9e7d8e4c0450998be2949218b45d68b061e8c20c452010
5
5
  SHA512:
6
- metadata.gz: 9a2114b37283201211235e16fb558b51e5eb64ebec22d164898fc5b40db763696f56fdea8a99e9cf3905701fa3738a866726822a797e970a1de64c3ded54503d
7
- data.tar.gz: fb3452718e38139450c34b8de7905188906e5d10935aabb592f6c96c0789e61ab1e29d4b4bc6e4563c1c124ffa87c835c4c417bccd99a0c919a93443e757f70f
6
+ metadata.gz: 964a576117407e3e5907a302106c2a0f657bedc9a4ebd59167ca360f7f89dbcb3975aec4b6c036e3cc2f138461044f162371f8b2e62d05e5f0ca1c498fa852b4
7
+ data.tar.gz: 8cae2ebdeb246fc190c49aed8b5a787372a482e2eecc3027116eab1bca985b33d9451cd22df4f158f1d4e0e8dc4314f6b1754f4e3b42f68dd8f7c1808b7ea3e6
@@ -0,0 +1,187 @@
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:trixie
61
+ - ubuntu:latest
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:trixie
77
+ pm: apt
78
+ ruby: apt-get update && apt-get install -y ruby-full ruby-dev build-essential git
79
+ - image: ubuntu:latest
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@v5
99
+
100
+
101
+ - name: trust git checkout owned by host UID (container job)
102
+ # actions/checkout marks the repo safe under a temp $HOME that doesn't
103
+ # survive into subsequent steps. Without this, `git ls-files` in
104
+ # pwn.gemspec (and any spec that shells out to git) fails with
105
+ # "detected dubious ownership" and the gem builds with ZERO files.
106
+ run: git config --global --add safe.directory "$PWD"
107
+ - name: build & install gem from checkout (git-checkout path in doc)
108
+ run: |
109
+ gem build pwn.gemspec
110
+ gem install --no-document ./pwn-*.gem
111
+ gem install --no-document rspec
112
+ ruby -r pwn -e 'puts "pwn #{PWN::VERSION} ruby #{RUBY_VERSION} #{RUBY_PLATFORM}"'
113
+
114
+ - name: dry-run — every package name resolves for this distro
115
+ run: pwn setup --profile full --dry-run --yes
116
+
117
+ - name: provision profile (documentation/Installation.md verbatim)
118
+ run: pwn setup --profile ${{ matrix.profile }} --yes
119
+
120
+ - name: rspec — doc↔code parity + behavioural + fresh-install functional
121
+ run: |
122
+ rspec --format documentation \
123
+ spec/documentation/installation_md_spec.rb \
124
+ spec/integration/fresh_install_spec.rb
125
+
126
+ macos:
127
+ name: macos / brew / core
128
+ runs-on: macos-latest
129
+ env:
130
+ PWN_FRESH_INSTALL: '1'
131
+ PWN_EXPECTED_PM: brew
132
+ PWN_PROFILE: core
133
+ steps:
134
+ - uses: actions/checkout@v5
135
+
136
+ - uses: ruby/setup-ruby@v1
137
+ with:
138
+ ruby-version: '3.4'
139
+
140
+ - name: build & install gem from checkout
141
+ run: |
142
+ gem build pwn.gemspec
143
+ gem install --no-document ./pwn-*.gem
144
+ gem install --no-document rspec
145
+
146
+ - name: dry-run full profile
147
+ run: pwn setup --profile full --dry-run --yes
148
+
149
+ - name: provision core
150
+ run: pwn setup --profile core --yes
151
+
152
+ - name: rspec — doc↔code parity + behavioural + fresh-install functional
153
+ run: |
154
+ rspec --format documentation \
155
+ spec/documentation/installation_md_spec.rb \
156
+ spec/integration/fresh_install_spec.rb
157
+
158
+ git-checkout:
159
+ # "From a git checkout (contributors)" section of Installation.md —
160
+ # exercises packer/provisioners/pwn.sh end-to-end.
161
+ name: kali / git-checkout / packer-provisioner
162
+ runs-on: ubuntu-latest
163
+ container:
164
+ image: kalilinux/kali-rolling
165
+ env:
166
+ DEBIAN_FRONTEND: noninteractive
167
+ PWN_FRESH_INSTALL: '1'
168
+ PWN_EXPECTED_PM: apt
169
+ PWN_PROFILE: core
170
+ PWN_ROOT: ${{ github.workspace }}
171
+ steps:
172
+ - run: apt-get update && apt-get install -y ruby-full ruby-dev build-essential git
173
+
174
+ - uses: actions/checkout@v5
175
+
176
+
177
+ - name: trust git checkout owned by host UID (container job)
178
+ run: git config --global --add safe.directory "$PWD"
179
+ - name: run legacy provisioner (must delegate to `pwn setup`)
180
+ run: bash packer/provisioners/pwn.sh
181
+
182
+ - name: rspec — fresh-install functional
183
+ run: |
184
+ gem install --no-document rspec
185
+ rspec --format documentation \
186
+ spec/documentation/installation_md_spec.rb \
187
+ spec/integration/fresh_install_spec.rb
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  UseCache: false
3
+ SuggestExtensions: false
3
4
  NewCops: enable
4
5
  Layout/LineLength:
5
6
  Max: 1620
@@ -18,7 +19,7 @@ Metrics/CyclomaticComplexity:
18
19
  Metrics/MethodLength:
19
20
  Max: 800
20
21
  Metrics/ModuleLength:
21
- Max: 2900
22
+ Max: 3000
22
23
  Metrics/PerceivedComplexity:
23
24
  Max: 157
24
25
  Style/HashEachMethods:
data/Gemfile CHANGED
@@ -49,8 +49,8 @@ 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'
53
- gem 'meshtastic', '0.0.171'
52
+ gem 'mcp', '0.24.0'
53
+ gem 'meshtastic', '0.0.172'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.24.1'
56
56
  gem 'msfrpc-client', '1.1.2'
@@ -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'
@@ -105,4 +105,4 @@ gem 'webrick', '1.9.2'
105
105
  gem 'whois', '6.0.3'
106
106
  gem 'whois-parser', '2.0.0'
107
107
  gem 'wicked_pdf', '2.8.2'
108
- gem 'yard', '0.9.44'
108
+ gem 'yard', '0.9.45'
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:
data/Rakefile CHANGED
@@ -20,5 +20,5 @@ if defined?(RDoc::Task)
20
20
  end
21
21
 
22
22
  default_tasks = %i[spec rubocop]
23
- default_tasks << :rdoc if defined?(RDoc::Task)
23
+ default_tasks << :rerdoc if defined?(RDoc::Task)
24
24
  task default: default_tasks
@@ -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
@@ -75,13 +75,7 @@ pwn_provider = 'ruby-gem'
75
75
  # pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.select { |s| s == 'PWN_PROVIDER' }.any?
76
76
  pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' }
77
77
 
78
- if pwn_provider == 'ruby-gem'
79
- require 'pwn'
80
- elsif pwn_provider == 'docker'
81
- require '/opt/pwn/lib/pwn'
82
- else
83
- raise "PWN_PROVIDER env variable is not set to 'ruby-gem' or 'docker'"
84
- end
78
+ raise "PWN_PROVIDER env variable is not set to 'ruby-gem' or 'docker'" unless %w[ruby-gem docker].include?(pwn_provider)
85
79
 
86
80
  dir_path = opts[:dir_path] ||= '.'
87
81
  target = opts[:target]
@@ -13,9 +13,9 @@ toolsets; the JSON-Schema for each tool is what the model actually sees.
13
13
  | `terminal` | `shell` | `Open3.capture3` on the host |
14
14
  | `pwn` | `pwn_eval` | `TOPLEVEL_BINDING.eval` in the live REPL process |
15
15
  | `memory` | `memory_remember` · `memory_recall` · `memory_forget` · `memory_clear` | `PWN::Memory` → `~/.pwn/memory.json` |
16
- | `skills` | `skill_list` · `skill_view` · `skill_create` · `skill_add_reference` · `skill_delete` | `~/.pwn/skills/*.md` |
16
+ | `skills` | `skill_list` · `skill_view` · `skill_create` · `skill_add_reference` · `skill_delete` · `skill_migrate_legacy` | `~/.pwn/skills/<name>/SKILL.md` (agentskills.io; legacy flat `*.md` still read) |
17
17
  | `sessions` | `sessions_list` · `sessions_view` · `sessions_current` · `sessions_delete` · `sessions_stats` | `PWN::Sessions` → `~/.pwn/sessions/` |
18
- | `learning` | `learning_note_outcome` · `learning_reflect` · `learning_distill_skill` · `learning_stats` · `learning_outcomes` · `learning_consolidate` · `learning_reset` · `learning_auto_introspect_toggle` · **`mistakes_list`** · **`mistakes_record`** · **`mistakes_resolve`** · **`mistakes_reset`** | `PWN::AI::Agent::Learning` + `PWN::AI::Agent::Mistakes` → `~/.pwn/learning.jsonl` + `~/.pwn/mistakes.json` |
18
+ | `learning` | `learning_note_outcome` · `learning_reflect` · `learning_distill_skill` · `learning_stats` · `learning_outcomes` · `learning_consolidate` · `learning_reset` · `learning_auto_introspect_toggle` · `learning_purge_noise` · **`mistakes_list`** · **`mistakes_record`** · **`mistakes_resolve`** · **`mistakes_reset`** · **`reward_judge`** · **`reward_prm`** · **`reward_sentinel`** · **`reward_preferences`** · **`reward_export_dpo`** · **`curriculum_practice`** · **`curriculum_train`** · **`curriculum_hindsight`** | `PWN::AI::Agent::Learning` + `Mistakes` + `Reward` + `Curriculum` → `~/.pwn/learning.jsonl` + `~/.pwn/mistakes.json` + `~/.pwn/preferences.jsonl` + `~/.pwn/curriculum/` |
19
19
  | `metrics` | `metrics_summary` · `metrics_reset` | `PWN::AI::Agent::Metrics` → `~/.pwn/metrics.json` |
20
20
  | `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) → `~/.pwn/extrospection.json` |
21
21
  | `cron` | `cron_list` · `cron_create` · `cron_run` · `cron_enable` · `cron_disable` · `cron_remove` | `PWN::Cron` → `~/.pwn/cron/jobs.yml` |
@@ -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` |
@@ -17,7 +17,7 @@ needed to unlock the ones you want. No `/opt/pwn` checkout, no `rvmsudo`
17
17
  chain, no bash provisioners required.
18
18
 
19
19
  Tested on **Kali / Debian / Ubuntu**, **Fedora**, **Arch**, and **macOS**
20
- (Homebrew or MacPorts). Ruby ≥ 3.4 (RVM, rbenv, asdf, or system Ruby).
20
+ (Homebrew or MacPorts). Ruby ≥ 3.3 (RVM, rbenv, asdf, or system Ruby).
21
21
 
22
22
  ---
23
23
 
@@ -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)