pwn 0.5.621 → 0.5.626

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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/Gemfile +1 -1
  4. data/README.md +7 -1
  5. data/bin/pwn_gqrx_scanner +78 -9
  6. data/documentation/Agent-Tool-Registry.md +3 -3
  7. data/documentation/Configuration.md +301 -47
  8. data/documentation/Cron.md +5 -3
  9. data/documentation/Diagrams.md +1 -1
  10. data/documentation/Extrospection.md +472 -26
  11. data/documentation/FFI.md +72 -8
  12. data/documentation/Home.md +4 -4
  13. data/documentation/How-PWN-Works.md +7 -3
  14. data/documentation/Mistakes.md +9 -0
  15. data/documentation/SDR.md +83 -10
  16. data/documentation/Skills-Memory-Learning.md +101 -11
  17. data/documentation/diagrams/agent-tool-registry.svg +56 -55
  18. data/documentation/diagrams/ai-integration-tool-calling.svg +24 -24
  19. data/documentation/diagrams/aws-cloud-security.svg +25 -25
  20. data/documentation/diagrams/burp-vs-zap-preference.svg +12 -12
  21. data/documentation/diagrams/code-scanning-sast.svg +25 -25
  22. data/documentation/diagrams/cron-scheduling.svg +23 -23
  23. data/documentation/diagrams/dot/agent-tool-registry.dot +2 -2
  24. data/documentation/diagrams/dot/extrospection-world-awareness.dot +33 -6
  25. data/documentation/diagrams/dot/hardware-hacking.dot +3 -1
  26. data/documentation/diagrams/dot/memory-skills-detailed.dot +4 -2
  27. data/documentation/diagrams/dot/mistakes-negative-feedback.dot +4 -2
  28. data/documentation/diagrams/dot/overall-pwn-architecture.dot +3 -3
  29. data/documentation/diagrams/dot/persistence-filesystem.dot +3 -3
  30. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +10 -4
  31. data/documentation/diagrams/dot/sdr-radio-flow.dot +39 -16
  32. data/documentation/diagrams/driver-framework.svg +13 -13
  33. data/documentation/diagrams/extrospection-world-awareness.svg +383 -177
  34. data/documentation/diagrams/fuzzing-workflow.svg +24 -24
  35. data/documentation/diagrams/hardware-hacking.svg +84 -69
  36. data/documentation/diagrams/history-to-drivers.svg +18 -18
  37. data/documentation/diagrams/memory-skills-detailed.svg +137 -120
  38. data/documentation/diagrams/mistakes-negative-feedback.svg +152 -137
  39. data/documentation/diagrams/network-infra-testing.svg +27 -27
  40. data/documentation/diagrams/overall-pwn-architecture.svg +97 -97
  41. data/documentation/diagrams/penetration-testing-workflow.svg +30 -30
  42. data/documentation/diagrams/persistence-filesystem.svg +100 -94
  43. data/documentation/diagrams/plugin-ecosystem.svg +35 -35
  44. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +254 -207
  45. data/documentation/diagrams/pwn-repl-prototyping.svg +20 -20
  46. data/documentation/diagrams/reporting-pipeline.svg +18 -18
  47. data/documentation/diagrams/reverse-engineering-flow.svg +21 -21
  48. data/documentation/diagrams/sdr-radio-flow.svg +223 -91
  49. data/documentation/diagrams/sessions-cron-automation.svg +18 -18
  50. data/documentation/diagrams/swarm-multi-agent.svg +39 -39
  51. data/documentation/diagrams/web-application-testing.svg +26 -26
  52. data/documentation/diagrams/zero-day-research-flow.svg +25 -25
  53. data/documentation/pwn-ai-Agent.md +16 -10
  54. data/documentation/pwn_silent_help_learn_demo.gif +0 -0
  55. data/lib/pwn/ai/agent/assembly.rb +1 -1
  56. data/lib/pwn/ai/agent/btc.rb +1 -1
  57. data/lib/pwn/ai/agent/burp_suite.rb +1 -1
  58. data/lib/pwn/ai/agent/extrospection.rb +2562 -57
  59. data/lib/pwn/ai/agent/gqrx.rb +2 -2
  60. data/lib/pwn/ai/agent/hacker_one.rb +1 -1
  61. data/lib/pwn/ai/agent/learning.rb +16 -16
  62. data/lib/pwn/ai/agent/loop.rb +1 -1
  63. data/lib/pwn/ai/agent/{introspection.rb → reflect.rb} +12 -12
  64. data/lib/pwn/ai/agent/sast.rb +1 -1
  65. data/lib/pwn/ai/agent/tools/extrospection.rb +311 -8
  66. data/lib/pwn/ai/agent/tools/learning.rb +7 -7
  67. data/lib/pwn/ai/agent/transparent_browser.rb +1 -1
  68. data/lib/pwn/ai/agent/vuln_gen.rb +2 -2
  69. data/lib/pwn/ai/agent.rb +1 -1
  70. data/lib/pwn/ai/open_ai.rb +10 -6
  71. data/lib/pwn/config.rb +5 -4
  72. data/lib/pwn/ffi/adalm_pluto.rb +498 -0
  73. data/lib/pwn/ffi/fftw.rb +192 -0
  74. data/lib/pwn/ffi/hack_rf.rb +193 -0
  75. data/lib/pwn/ffi/liquid.rb +244 -0
  76. data/lib/pwn/ffi/rtl_sdr.rb +210 -0
  77. data/lib/pwn/ffi/soapy_sdr.rb +177 -0
  78. data/lib/pwn/ffi/stdio.rb +9 -1
  79. data/lib/pwn/ffi/volk.rb +241 -0
  80. data/lib/pwn/ffi.rb +45 -2
  81. data/lib/pwn/plugins/burp_suite.rb +2 -2
  82. data/lib/pwn/sast/pom_version.rb +2 -2
  83. data/lib/pwn/sast/test_case_engine.rb +1 -1
  84. data/lib/pwn/sdr/decoder/adsb.rb +201 -12
  85. data/lib/pwn/sdr/decoder/apt.rb +19 -5
  86. data/lib/pwn/sdr/decoder/base.rb +314 -1
  87. data/lib/pwn/sdr/decoder/bluetooth.rb +91 -18
  88. data/lib/pwn/sdr/decoder/dect.rb +81 -23
  89. data/lib/pwn/sdr/decoder/dsp.rb +229 -15
  90. data/lib/pwn/sdr/decoder/flex.rb +23 -6
  91. data/lib/pwn/sdr/decoder/gps.rb +80 -18
  92. data/lib/pwn/sdr/decoder/gsm.rb +83 -28
  93. data/lib/pwn/sdr/decoder/iridium.rb +80 -18
  94. data/lib/pwn/sdr/decoder/lora.rb +84 -28
  95. data/lib/pwn/sdr/decoder/lte.rb +80 -18
  96. data/lib/pwn/sdr/decoder/morse.rb +23 -6
  97. data/lib/pwn/sdr/decoder/p25.rb +90 -31
  98. data/lib/pwn/sdr/decoder/pager.rb +23 -6
  99. data/lib/pwn/sdr/decoder/pocsag.rb +23 -6
  100. data/lib/pwn/sdr/decoder/rds.rb +219 -52
  101. data/lib/pwn/sdr/decoder/rfid.rb +81 -27
  102. data/lib/pwn/sdr/decoder/rtl433.rb +85 -27
  103. data/lib/pwn/sdr/decoder/rtty.rb +23 -6
  104. data/lib/pwn/sdr/decoder/wifi.rb +86 -32
  105. data/lib/pwn/sdr/decoder/zigbee.rb +88 -35
  106. data/lib/pwn/sdr/decoder.rb +6 -5
  107. data/lib/pwn/sdr/frequency_allocation.rb +124 -81
  108. data/lib/pwn/sdr/gqrx.rb +1513 -165
  109. data/lib/pwn/version.rb +1 -1
  110. data/spec/lib/pwn/ai/agent/extrospection_spec.rb +96 -0
  111. data/spec/lib/pwn/ai/agent/{introspection_spec.rb → reflect_spec.rb} +3 -3
  112. data/spec/lib/pwn/ai/agent/tools/extrospection_spec.rb +8 -24
  113. data/spec/lib/pwn/ffi/adalm_pluto_spec.rb +37 -0
  114. data/spec/lib/pwn/ffi/fftw_spec.rb +26 -0
  115. data/spec/lib/pwn/ffi/hack_rf_spec.rb +25 -0
  116. data/spec/lib/pwn/ffi/liquid_spec.rb +42 -0
  117. data/spec/lib/pwn/ffi/rtl_sdr_spec.rb +24 -0
  118. data/spec/lib/pwn/ffi/soapy_sdr_spec.rb +25 -0
  119. data/spec/lib/pwn/ffi/volk_spec.rb +36 -0
  120. data/spec/lib/pwn/sdr/decoder/base_iq_spec.rb +65 -0
  121. data/spec/lib/pwn/sdr/decoder/dsp_spec.rb +50 -4
  122. data/spec/lib/pwn/sdr/decoder/rds_spec.rb +45 -4
  123. data/third_party/pwn_rdoc.jsonl +279 -30
  124. metadata +21 -5
@@ -3,7 +3,7 @@
3
3
  module PWN
4
4
  module AI
5
5
  module Agent
6
- # This module is an AI agent designed to analyze signal data captured by a software-defined-radio using GQRX. It uses the PWN::AI::Agent::Introspection.reflect_on method to analyze the signal data and provide insights based on the location where the data was captured. The agent can determine if the frequency is licensed or unlicensed based on FCC records and provide relevant information about the transmission. This module is useful for security professionals, researchers, and hobbyists interested in analyzing radio signals and understanding their context.
6
+ # This module is an AI agent designed to analyze signal data captured by a software-defined-radio using GQRX. It uses the PWN::AI::Agent::Reflect.on method to analyze the signal data and provide insights based on the location where the data was captured. The agent can determine if the frequency is licensed or unlicensed based on FCC records and provide relevant information about the transmission. This module is useful for security professionals, researchers, and hobbyists interested in analyzing radio signals and understanding their context.
7
7
  module GQRX
8
8
  # Supported Method Parameters::
9
9
  # ai_analysis = PWN::AI::Agent::GQRX.analyze(
@@ -20,7 +20,7 @@ module PWN
20
20
 
21
21
  system_role_content = "Analyze signal data captured by a software-defined-radio using GQRX at the following location: #{location}. Respond with just FCC information about the transmission if available. If the frequency is unlicensed or not found in FCC records, state that clearly. Be clear and concise in your analysis."
22
22
 
23
- PWN::AI::Agent::Introspection.reflect_on(
23
+ PWN::AI::Agent::Reflect.on(
24
24
  system_role_content: system_role_content,
25
25
  request: request,
26
26
  suppress_pii_warning: true
@@ -29,7 +29,7 @@ module PWN
29
29
  raise "ERROR: type parameter value of #{type} is not supported"
30
30
  end
31
31
 
32
- PWN::AI::Agent::Introspection.reflect_on(
32
+ PWN::AI::Agent::Reflect.on(
33
33
  system_role_content: system_role_content,
34
34
  request: request,
35
35
  spinner: true,
@@ -16,7 +16,7 @@ module PWN
16
16
  #
17
17
  # Data flows:
18
18
  # Loop.run --(tool telemetry)--> Metrics.record
19
- # Loop.run --(final answer)----> Learning.auto_reflect (opt-in)
19
+ # Loop.run --(final answer)----> Learning.auto_introspect (opt-in)
20
20
  # model --(tool calls)------> learning_note_outcome / _distill_skill
21
21
  # PromptBuilder <----------------- Learning.to_context + Metrics.to_context
22
22
  #
@@ -164,9 +164,9 @@ module PWN
164
164
  # dry_run: 'optional - when true, do not write to Memory/Skills (default false)'
165
165
  # )
166
166
  #
167
- # Uses PWN::AI::Agent::Introspection (when available) to LLM-summarise the
167
+ # Uses PWN::AI::Agent::Reflect (when available) to LLM-summarise the
168
168
  # session into structured lessons. Falls back to a heuristic
169
- # extractor when introspection is disabled so learning never stops.
169
+ # extractor when module_reflection is disabled so learning never stops.
170
170
 
171
171
  public_class_method def self.reflect(opts = {})
172
172
  session_id = opts[:session_id]
@@ -193,19 +193,19 @@ module PWN
193
193
  end
194
194
 
195
195
  # Supported Method Parameters::
196
- # PWN::AI::Agent::Learning.auto_reflect(
196
+ # PWN::AI::Agent::Learning.auto_introspect(
197
197
  # session_id: 'required - id of the just-completed session',
198
198
  # request: 'optional - original user request (for outcome logging)',
199
199
  # final: 'optional - final assistant answer (for outcome logging)'
200
200
  # )
201
201
  #
202
- # Called by Loop.run when PWN::Env[:ai][:agent][:auto_reflect] is
202
+ # Called by Loop.run when PWN::Env[:ai][:agent][:auto_introspect] is
203
203
  # truthy. Never raises — learning must not break the primary loop.
204
204
 
205
- public_class_method def self.auto_reflect(opts = {})
205
+ public_class_method def self.auto_introspect(opts = {})
206
206
  session_id = opts[:session_id]
207
207
  return unless session_id
208
- return unless auto_reflect_enabled?
208
+ return unless auto_introspect_enabled?
209
209
 
210
210
  ok = infer_success(session_id: session_id, final: opts[:final])
211
211
  note_outcome(
@@ -218,7 +218,7 @@ module PWN
218
218
  reflect(session_id: session_id)
219
219
  Extrospection.auto_extrospect(session_id: session_id) if defined?(Extrospection)
220
220
  rescue StandardError => e
221
- warn "[pwn-ai/learning] auto_reflect swallowed: #{e.class}: #{e.message}"
221
+ warn "[pwn-ai/learning] auto_introspect swallowed: #{e.class}: #{e.message}"
222
222
  nil
223
223
  end
224
224
 
@@ -301,10 +301,10 @@ module PWN
301
301
  # privates
302
302
  # -------------------------------------------------------------
303
303
 
304
- private_class_method def self.auto_reflect_enabled?
304
+ private_class_method def self.auto_introspect_enabled?
305
305
  return false unless defined?(PWN::Env) && PWN::Env.is_a?(Hash)
306
306
 
307
- PWN::Env.dig(:ai, :agent, :auto_reflect) ? true : false
307
+ PWN::Env.dig(:ai, :agent, :auto_introspect) ? true : false
308
308
  rescue StandardError
309
309
  false
310
310
  end
@@ -314,7 +314,7 @@ module PWN
314
314
  # Derive a success signal stronger than "final answer non-empty":
315
315
  # look at the tool-failure ratio inside the just-completed turn AND
316
316
  # scan the final text for self-reported failure language. Without
317
- # this, auto_reflect logs ~100 % success and the negative-feedback
317
+ # this, auto_introspect logs ~100 % success and the negative-feedback
318
318
  # side of the learning loop never fires.
319
319
  private_class_method def self.infer_success(opts = {})
320
320
  final = opts[:final].to_s
@@ -353,11 +353,11 @@ module PWN
353
353
 
354
354
  private_class_method def self.introspective_lessons(opts = {})
355
355
  transcript = opts[:transcript] || []
356
- return [] unless defined?(PWN::AI::Agent::Introspection)
357
- return [] unless defined?(PWN::Env) && PWN::Env.is_a?(Hash) && PWN::Env.dig(:ai, :introspection)
356
+ return [] unless defined?(PWN::AI::Agent::Reflect)
357
+ return [] unless defined?(PWN::Env) && PWN::Env.is_a?(Hash) && PWN::Env.dig(:ai, :module_reflection)
358
358
 
359
359
  req = "Analyse this pwn-ai session transcript and emit up to 5 durable, generalizable lessons (one per line, no numbering, imperative voice) that would make future runs faster or more reliable. Focus on tool selection, error recovery, and target-agnostic technique. Ignore trivia.\n\nTRANSCRIPT:\n#{transcript_text(transcript: transcript)}"
360
- resp = PWN::AI::Agent::Introspection.reflect_on(request: req, suppress_pii_warning: true)
360
+ resp = PWN::AI::Agent::Reflect.on(request: req, suppress_pii_warning: true)
361
361
  resp.to_s.lines.map(&:strip).reject(&:empty?).first(5)
362
362
  rescue StandardError
363
363
  []
@@ -420,7 +420,7 @@ module PWN
420
420
  PWN::AI::Agent::Learning.note_outcome(task: 'nmap sweep 10.0.0.0/24', success: true, details: '12 hosts up')
421
421
  PWN::AI::Agent::Learning.outcomes(limit: 20, success: false)
422
422
  PWN::AI::Agent::Learning.reflect(session_id: sid) # LLM or heuristic → PWN::Memory
423
- PWN::AI::Agent::Learning.auto_reflect(session_id: sid, request: req, final: text)
423
+ PWN::AI::Agent::Learning.auto_introspect(session_id: sid, request: req, final: text)
424
424
  PWN::AI::Agent::Learning.distill_skill(name: 'quick_recon', session_id: sid)
425
425
  PWN::AI::Agent::Learning.consolidate(max_entries: 200) # dedupe + prune Memory
426
426
  PWN::AI::Agent::Learning.to_context(limit: 5) # injected by PromptBuilder
@@ -428,7 +428,7 @@ module PWN
428
428
  PWN::AI::Agent::Learning.reset
429
429
 
430
430
  Enable end-of-run auto-learning with:
431
- PWN::Env[:ai][:agent][:auto_reflect] = true
431
+ PWN::Env[:ai][:agent][:auto_introspect] = true
432
432
 
433
433
  #{self}.authors
434
434
  USAGE
@@ -241,7 +241,7 @@ module PWN
241
241
  if calls.empty?
242
242
  text = msg[:content].to_s
243
243
  append_session(session_id: session_id, role: 'assistant', content: text)
244
- Learning.auto_reflect(session_id: session_id, request: request, final: text) if defined?(Learning)
244
+ Learning.auto_introspect(session_id: session_id, request: request, final: text) if defined?(Learning)
245
245
  return text
246
246
  end
247
247
 
@@ -5,14 +5,14 @@ require 'json'
5
5
  module PWN
6
6
  module AI
7
7
  module Agent
8
- # PWN::AI::Agent::Introspection is the inward-facing counterpart to
8
+ # PWN::AI::Agent::Reflect is the inward-facing counterpart to
9
9
  # PWN::AI::Agent::Extrospection. Where Extrospection looks OUTWARD at
10
10
  # the world the agent operates in (host state, toolchain, network,
11
- # threat-intel), Introspection looks INWARD - it lets pwn hand a
12
- # request to the active AI engine and reflect on its own artifacts,
13
- # transcripts, findings, code, or decisions.
11
+ # threat-intel), Reflect looks INWARD - it lets pwn hand a request to
12
+ # the active AI engine and reflect on its own artifacts, transcripts,
13
+ # findings, code, or decisions.
14
14
  #
15
- # This module is gated by `PWN::Env[:ai][:introspection]` so that
15
+ # This module is gated by `PWN::Env[:ai][:module_reflection]` so that
16
16
  # potentially-sensitive local data is never shipped to a remote LLM
17
17
  # unless the operator has explicitly opted in via pwn-vault / config.
18
18
  #
@@ -21,16 +21,16 @@ module PWN
21
21
  # through when it wants an LLM opinion on locally-produced data, and
22
22
  # it is also what PWN::AI::Agent::Learning.reflect uses to distill
23
23
  # session transcripts into durable PWN::Memory lessons.
24
- module Introspection
24
+ module Reflect
25
25
  # Supported Method Parameters::
26
- # response = PWN::AI::Agent::Introspection.reflect_on(
26
+ # response = PWN::AI::Agent::Reflect.on(
27
27
  # request: 'required - String - What you want the AI to reflect on',
28
28
  # system_role_content: 'optional - context to set up the model behavior for reflection',
29
29
  # spinner: 'optional - Boolean - Display spinner during operation (default: false)',
30
30
  # suppress_pii_warning: 'optional - Boolean - Suppress PII Warnings (default: false)'
31
31
  # )
32
32
 
33
- public_class_method def self.reflect_on(opts = {})
33
+ public_class_method def self.on(opts = {})
34
34
  request = opts[:request]
35
35
  raise 'ERROR: request must be provided' if request.nil?
36
36
 
@@ -42,14 +42,14 @@ module PWN
42
42
 
43
43
  response = nil
44
44
 
45
- ai_introspection = PWN::Env[:ai][:introspection]
45
+ ai_module_reflection = PWN::Env[:ai][:module_reflection]
46
46
 
47
- if ai_introspection && request.length.positive?
47
+ if ai_module_reflection && request.length.positive?
48
48
  engine = PWN::Env[:ai][:active].to_s.downcase.to_sym
49
49
  valid_ai_engines = PWN::AI.help.reject { |e| e.downcase == :agent }.map(&:downcase)
50
50
  raise "ERROR: Unsupported AI engine. Supported engines are: #{valid_ai_engines}" unless valid_ai_engines.include?(engine)
51
51
 
52
- warn "AI Introspection is enabled. Ensure #{engine} has been authorized for use and/or requests are sanitized properly." unless suppress_pii_warning
52
+ warn "AI Reflection is enabled. Ensure #{engine} has been authorized for use and/or requests are sanitized properly." unless suppress_pii_warning
53
53
  response = PWN::AI::Agent::Loop.run(
54
54
  request: request.chomp,
55
55
  system_role_content: system_role_content,
@@ -75,7 +75,7 @@ module PWN
75
75
 
76
76
  public_class_method def self.help
77
77
  puts "USAGE:
78
- #{self}.reflect_on(
78
+ #{self}.on(
79
79
  request: 'required - String - What you want the AI to reflect on',
80
80
  system_role_content: 'optional - context to set up the model behavior for reflection',
81
81
  spinner: 'optional - Boolean - Display spinner during operation (default: false)',
@@ -16,7 +16,7 @@ module PWN
16
16
 
17
17
  system_role_content = 'Your sole purpose is to analyze source code snippets and generate an Exploit Prediction Scoring System (EPSS) score between 0% - 100%. Just generate a score unless score is >= 75% in which a PoC and code fix should also be included.'
18
18
 
19
- PWN::AI::Agent::Introspection.reflect_on(
19
+ PWN::AI::Agent::Reflect.on(
20
20
  system_role_content: system_role_content,
21
21
  request: request,
22
22
  suppress_pii_warning: true
@@ -29,7 +29,7 @@ PWN::AI::Agent::Registry.register(
29
29
  type: 'object',
30
30
  properties: {
31
31
  persist: { type: 'boolean', default: true, description: 'Write to disk & rotate previous baseline.' },
32
- sections: { type: 'array', items: { type: 'string', enum: %w[host net toolchain repo env rf web] }, description: 'Subset of probes to run (default all).' }
32
+ sections: { type: 'array', items: { type: 'string', enum: %w[host net toolchain repo env rf web osint serial telecomm packet vision voice] }, description: 'Subset of probes (default all except web). auto_extrospect uses host/repo/env only; toolchain never launches GUI bins.' }
33
33
  },
34
34
  required: []
35
35
  }
@@ -86,7 +86,7 @@ PWN::AI::Agent::Registry.register(
86
86
  properties: {
87
87
  source: { type: 'string', description: 'Where it came from (nmap, shodan, burp, cve, human, ...).' },
88
88
  data: { type: 'string', description: 'The observation itself.' },
89
- category: { type: 'string', enum: %w[recon vuln intel target network env rf web misc], default: 'misc' },
89
+ category: { type: 'string', enum: %w[recon vuln intel target network env rf web osint serial telecomm packet vision voice misc], default: 'misc' },
90
90
  target: { type: 'string', description: 'Host / IP / URL / asset the observation is about.' },
91
91
  tags: { type: 'array', items: { type: 'string' } },
92
92
  ttl: { type: 'integer', description: 'Seconds until stale (omit = forever).' }
@@ -256,12 +256,13 @@ PWN::AI::Agent::Registry.register(
256
256
  toolset: 'extrospection',
257
257
  schema: {
258
258
  name: 'extro_auto_toggle',
259
- description: 'Enable/disable end-of-run auto-extrospection ' \
260
- '(PWN::Env[:ai][:agent][:auto_extrospect]). When enabled, ' \
261
- 'Learning.auto_reflect ALSO calls Extrospection.auto_' \
262
- 'extrospect after every final answer, so host drift is ' \
263
- 'captured continuously without an explicit extro_snapshot ' \
264
- 'call. Omit `enabled` to just read the current state.',
259
+ description: 'Enable/disable OPTIONAL ambient baseline after every ' \
260
+ 'final answer (PWN::Env[:ai][:agent][:auto_extrospect]). ' \
261
+ 'When on, Learning.auto_introspect runs Extrospection.' \
262
+ 'auto_extrospect with AUTO_SECTIONS (host/repo/env only) ' \
263
+ 'never toolchain/rf/web, never launches burpsuite/zaproxy/' \
264
+ 'msfconsole/gqrx. Primary sensing stays on-demand (intel/' \
265
+ 'verify/watch/observe). Omit `enabled` to only query.',
265
266
  parameters: {
266
267
  type: 'object',
267
268
  properties: {
@@ -363,3 +364,305 @@ PWN::AI::Agent::Registry.register(
363
364
  )
364
365
  }
365
366
  )
367
+
368
+ PWN::AI::Agent::Registry.register(
369
+ name: 'extro_rf_tune',
370
+ toolset: 'extrospection',
371
+ schema: {
372
+ name: 'extro_rf_tune',
373
+ description: 'RF sense organ — the radio analogue of extro_watch / ' \
374
+ 'extro_verify. Tunes a *running* GQRX instance (remote ' \
375
+ 'control, never launches the GUI), demodulates, measures ' \
376
+ 'signal strength, and samples RDS (PI / PS / RadioText) so ' \
377
+ 'questions like "what\'s playing on 101.1 FM?" get a live ' \
378
+ 'answer. Auto-detects band plan (fm_radio → WFM_ST + RDS). ' \
379
+ 'Returns {ok:, freq:, hz:, strength_dbfs:, demodulator_mode:, ' \
380
+ 'rds:{pi,ps_name,radiotext,station}, now_playing:, station:, ' \
381
+ 'summary:}. On success also observe(category: :rf) so the ' \
382
+ 'EXTROSPECTION prompt block and extro_correlate see it. ' \
383
+ 'Requires GQRX remote control listening (default 7356) + ' \
384
+ 'an SDR attached; fails fast with actionable advice otherwise.',
385
+ parameters: {
386
+ type: 'object',
387
+ properties: {
388
+ freq: {
389
+ type: 'string',
390
+ description: 'Frequency to tune. Free-form: "101.1", "101.1 FM", ' \
391
+ '"101.1 MHz", "101.100.000", "101100000", "433.92", …'
392
+ },
393
+ host: { type: 'string', description: 'GQRX remote-control host (default 127.0.0.1).' },
394
+ port: { type: 'integer', description: 'GQRX remote-control port (default 7356).' },
395
+ settle_secs: {
396
+ type: 'number',
397
+ description: 'Seconds to sample RDS after tuning (default 8, max 30).'
398
+ },
399
+ rds: {
400
+ type: 'boolean',
401
+ description: 'Force RDS sampling on/off. Default: auto (on for FM ' \
402
+ 'broadcast / band-plans with decoder: :rds).'
403
+ },
404
+ demodulator_mode: {
405
+ type: 'string',
406
+ description: 'Override demod (WFM_ST, WFM, FM, AM, USB, LSB, …). ' \
407
+ 'Default from band-plan / FM range heuristic.'
408
+ },
409
+ bandwidth: {
410
+ type: 'string',
411
+ description: 'Passband e.g. "200.000" (Hz, PWN dotted form). Default from band-plan.'
412
+ },
413
+ record: {
414
+ type: 'boolean',
415
+ default: true,
416
+ description: 'Also observe(category: :rf) so it hits EXTROSPECTION (default true).'
417
+ },
418
+ ttl: {
419
+ type: 'integer',
420
+ description: 'Observation TTL seconds (default 300 — radio content is ephemeral).'
421
+ }
422
+ },
423
+ required: %w[freq]
424
+ }
425
+ },
426
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:rf_tune) },
427
+ handler: lambda { |args|
428
+ o = { freq: args[:freq] }
429
+ o[:host] = args[:host] if args[:host]
430
+ o[:port] = args[:port] if args[:port]
431
+ o[:settle_secs] = args[:settle_secs] if args[:settle_secs]
432
+ o[:rds] = args[:rds] if args.key?(:rds)
433
+ o[:demodulator_mode] = args[:demodulator_mode] if args[:demodulator_mode]
434
+ o[:bandwidth] = args[:bandwidth] if args[:bandwidth]
435
+ o[:record] = args[:record] if args.key?(:record)
436
+ o[:ttl] = args[:ttl] if args[:ttl]
437
+ PWN::AI::Agent::Extrospection.rf_tune(o)
438
+ }
439
+ )
440
+
441
+ PWN::AI::Agent::Registry.register(
442
+ name: 'extro_osint',
443
+ toolset: 'extrospection',
444
+ schema: {
445
+ name: 'extro_osint',
446
+ description: 'OSINT sense organ — aggregates public open APIs (also drawn ' \
447
+ 'from public-api-lists) for reverse phone, IP/geo/ASN/BGP + ' \
448
+ 'threat reputation (ipapi.is/iplocate/ipwho.is/AbuseIPDB/' \
449
+ 'GreyNoise), DNS/WHOIS/RDAP, CT (crt.sh + Cert Spotter), FCC ' \
450
+ 'ID, patent, VIN (NHTSA), MAC OUI, ham callsign (Callook), ' \
451
+ 'person / missing-person (Wikipedia/Wikidata/OpenSanctions + ' \
452
+ 'NamUs/FBI/Charley), username pivots (GitHub/GitLab/Reddit), ' \
453
+ 'name demographics (Agify/Genderize/Nationalize), Shodan/' \
454
+ 'Hunter/VirusTotal/HIBP/SecurityTrails (keyed), Wayback, ' \
455
+ 'OTX/URLHaus/ThreatFox/urlscan, HackerTarget, openFDA, NPPES, ' \
456
+ 'Nominatim, OpenCorporates, CourtListener, SEC EDGAR, Federal ' \
457
+ 'Register, UK Police, EPSS + CISA KEV, Microlink unfurl, ' \
458
+ 'universities, and vital-records plans. kind auto-detects. ' \
459
+ 'Best-effort per-feed; unreachable → error hashes. ' \
460
+ 'observe(category: :osint) by default.',
461
+ parameters: {
462
+ type: 'object',
463
+ properties: {
464
+ query: { type: 'string', description: 'Phone, IP, domain, email, URL, person name, company, CIK, FCC ID, patent number, username, address, …' },
465
+ kind: {
466
+ type: 'string',
467
+ enum: %w[auto ip geo dns whois rdap crtsh bgp shodan hunter phone fcc_id patent person username github wayback email domain url company cik openfda vital_records threat vin mac callsign npi cve],
468
+ description: 'Force an OSINT kind. Omit / auto to detect from query shape.'
469
+ },
470
+ feeds: {
471
+ type: 'array',
472
+ items: { type: 'string', enum: %w[ip geo dns whois rdap crtsh bgpview shodan hunter phone fcc_id patent person username github wayback otx urlhaus threatfox urlscan hackertarget openfda nominatim opencorporates courtlistener sec_edgar vital_records ipapi_is iplocate ipwhois abuseipdb virustotal greynoise certspotter epss cisa_kev nhtsa nppes federal_register uk_police callook mac_vendor universities microlink agify genderize nationalize haveibeenpwned securitytrails] }
473
+ },
474
+ limit: { type: 'integer', default: 5 },
475
+ record: { type: 'boolean', default: true },
476
+ ttl: { type: 'integer', description: 'Observation TTL seconds (default 86400).' }
477
+ },
478
+ required: %w[query]
479
+ }
480
+ },
481
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:osint) },
482
+ handler: lambda { |args|
483
+ o = { query: args[:query] }
484
+ o[:kind] = args[:kind] if args[:kind]
485
+ o[:feeds] = args[:feeds] if args[:feeds]
486
+ o[:limit] = args[:limit] if args[:limit]
487
+ o[:record] = args[:record] if args.key?(:record)
488
+ o[:ttl] = args[:ttl] if args[:ttl]
489
+ PWN::AI::Agent::Extrospection.osint(o)
490
+ }
491
+ )
492
+
493
+ PWN::AI::Agent::Registry.register(
494
+ name: 'extro_serial',
495
+ toolset: 'extrospection',
496
+ schema: {
497
+ name: 'extro_serial',
498
+ description: 'Serial sense organ — open a USB-UART / modem / Arduino / ' \
499
+ 'RFID reader (PWN::Plugins::Serial), optional payload write ' \
500
+ '(AT commands, hex bytes), drain response, disconnect. ' \
501
+ 'Returns text + hex + line-state + modem-params + device ' \
502
+ 'inventory. Never holds the port across calls. ' \
503
+ 'observe(category: :serial).',
504
+ parameters: {
505
+ type: 'object',
506
+ properties: {
507
+ block_dev: { type: 'string', description: 'Device path (default first /dev/ttyUSB* or /dev/ttyACM*).' },
508
+ baud: { type: 'integer', description: 'Baud rate (default 9600).' },
509
+ payload: { type: 'string', description: 'String payload to write (e.g. "ATI\\r"). For raw bytes use pwn_eval.' },
510
+ settle_secs: { type: 'number', description: 'Seconds to read after write (default 1.5, max 30).' },
511
+ data_bits: { type: 'integer', default: 8 },
512
+ stop_bits: { type: 'integer', default: 1 },
513
+ parity: { type: 'string', enum: %w[none even odd mark space], default: 'none' },
514
+ record: { type: 'boolean', default: true },
515
+ ttl: { type: 'integer' }
516
+ },
517
+ required: []
518
+ }
519
+ },
520
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:serial_sense) },
521
+ handler: lambda { |args|
522
+ o = {}
523
+ o[:block_dev] = args[:block_dev] if args[:block_dev]
524
+ o[:baud] = args[:baud] if args[:baud]
525
+ o[:payload] = args[:payload] if args[:payload]
526
+ o[:settle_secs] = args[:settle_secs] if args[:settle_secs]
527
+ o[:data_bits] = args[:data_bits] if args[:data_bits]
528
+ o[:stop_bits] = args[:stop_bits] if args[:stop_bits]
529
+ o[:parity] = args[:parity] if args[:parity]
530
+ o[:record] = args[:record] if args.key?(:record)
531
+ o[:ttl] = args[:ttl] if args[:ttl]
532
+ PWN::AI::Agent::Extrospection.serial_sense(o)
533
+ }
534
+ )
535
+
536
+ PWN::AI::Agent::Registry.register(
537
+ name: 'extro_telecomm',
538
+ toolset: 'extrospection',
539
+ schema: {
540
+ name: 'extro_telecomm',
541
+ description: 'Telecomm sense organ (SIP / VoIP / PSTN) — senses a running ' \
542
+ 'BareSIP instance over HTTP control (never launches it). ' \
543
+ 'Actions: :inventory / :status / :dial / :hangup. Dial is ' \
544
+ 'OPSEC-sensitive (real call). observe(category: :telecomm).',
545
+ parameters: {
546
+ type: 'object',
547
+ properties: {
548
+ action: { type: 'string', enum: %w[inventory status dial hangup], default: 'inventory' },
549
+ target: { type: 'string', description: 'SIP URI or E.164 number for action:dial (e.g. sip:alice@example.com or +13125551212).' },
550
+ host: { type: 'string', description: 'BareSIP HTTP host (default 127.0.0.1).' },
551
+ port: { type: 'integer', description: 'BareSIP HTTP port (default 8000).' },
552
+ record: { type: 'boolean', default: true },
553
+ ttl: { type: 'integer' }
554
+ },
555
+ required: []
556
+ }
557
+ },
558
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:telecomm) },
559
+ handler: lambda { |args|
560
+ o = {}
561
+ o[:action] = args[:action] if args[:action]
562
+ o[:target] = args[:target] if args[:target]
563
+ o[:host] = args[:host] if args[:host]
564
+ o[:port] = args[:port] if args[:port]
565
+ o[:record] = args[:record] if args.key?(:record)
566
+ o[:ttl] = args[:ttl] if args[:ttl]
567
+ PWN::AI::Agent::Extrospection.telecomm(o)
568
+ }
569
+ )
570
+
571
+ PWN::AI::Agent::Registry.register(
572
+ name: 'extro_packet',
573
+ toolset: 'extrospection',
574
+ schema: {
575
+ name: 'extro_packet',
576
+ description: 'Packet sense organ — L2/L3 capture & pcap summarisation via ' \
577
+ 'tshark/tcpdump + PWN::Plugins::Packet. Actions: :inventory, ' \
578
+ ':capture (bounded count/timeout → ~/.pwn/extrospection/packet/*.pcap), ' \
579
+ ':summarize_pcap. observe(category: :packet).',
580
+ parameters: {
581
+ type: 'object',
582
+ properties: {
583
+ action: { type: 'string', enum: %w[inventory capture summarize_pcap], default: 'inventory' },
584
+ iface: { type: 'string', description: 'Capture interface (default first non-lo or any).' },
585
+ filter: { type: 'string', description: 'BPF filter e.g. "tcp port 443".' },
586
+ count: { type: 'integer', description: 'Packets to capture (default 20, max 200).' },
587
+ timeout: { type: 'integer', description: 'Capture seconds (default 5, max 60).' },
588
+ path: { type: 'string', description: 'Existing pcap path for action:summarize_pcap.' },
589
+ record: { type: 'boolean', default: true },
590
+ ttl: { type: 'integer' }
591
+ },
592
+ required: []
593
+ }
594
+ },
595
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:packet_sense) },
596
+ handler: lambda { |args|
597
+ o = {}
598
+ %i[action iface filter count timeout path ttl].each { |k| o[k] = args[k] if args[k] }
599
+ o[:record] = args[:record] if args.key?(:record)
600
+ PWN::AI::Agent::Extrospection.packet_sense(o)
601
+ }
602
+ )
603
+
604
+ PWN::AI::Agent::Registry.register(
605
+ name: 'extro_vision',
606
+ toolset: 'extrospection',
607
+ schema: {
608
+ name: 'extro_vision',
609
+ description: 'Vision / OCR sense organ — OCR an image via PWN::Plugins::OCR ' \
610
+ '(tesseract / RTesseract) or decode barcodes/QR via zbarimg. ' \
611
+ 'Actions: :ocr, :barcode, :inventory. observe(category: :vision).',
612
+ parameters: {
613
+ type: 'object',
614
+ properties: {
615
+ file: { type: 'string', description: 'Path to image (png/jpg/tiff/webp) for :ocr / :barcode.' },
616
+ action: { type: 'string', enum: %w[ocr barcode inventory], description: 'Default :ocr when file given, else :inventory.' },
617
+ lang: { type: 'string', description: 'Tesseract language (default eng).' },
618
+ record: { type: 'boolean', default: true },
619
+ ttl: { type: 'integer' }
620
+ },
621
+ required: []
622
+ }
623
+ },
624
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:vision) },
625
+ handler: lambda { |args|
626
+ o = {}
627
+ o[:file] = args[:file] if args[:file]
628
+ o[:action] = args[:action] if args[:action]
629
+ o[:lang] = args[:lang] if args[:lang]
630
+ o[:record] = args[:record] if args.key?(:record)
631
+ o[:ttl] = args[:ttl] if args[:ttl]
632
+ PWN::AI::Agent::Extrospection.vision(o)
633
+ }
634
+ )
635
+
636
+ PWN::AI::Agent::Registry.register(
637
+ name: 'extro_voice',
638
+ toolset: 'extrospection',
639
+ schema: {
640
+ name: 'extro_voice',
641
+ description: 'Voice sense organ — text-to-speech (espeak-ng / festival / ' \
642
+ 'spd-say via PWN::Plugins::Voice) and speech-to-text (whisper). ' \
643
+ 'Actions: :inventory, :tts, :stt. Artefacts under ' \
644
+ '~/.pwn/extrospection/voice/. observe(category: :voice).',
645
+ parameters: {
646
+ type: 'object',
647
+ properties: {
648
+ action: { type: 'string', enum: %w[inventory tts stt], default: 'inventory' },
649
+ text: { type: 'string', description: 'Text to speak for action:tts.' },
650
+ text_path: { type: 'string', description: 'Path to text file for action:tts.' },
651
+ audio: { type: 'string', description: 'Path to audio file for action:stt.' },
652
+ out: { type: 'string', description: 'Output wav path for action:tts.' },
653
+ engine: { type: 'string', enum: %w[espeak festival spd_say whisper], description: 'Force engine (auto by default).' },
654
+ model: { type: 'string', description: 'Whisper model for STT (default tiny).' },
655
+ record: { type: 'boolean', default: true },
656
+ ttl: { type: 'integer' }
657
+ },
658
+ required: []
659
+ }
660
+ },
661
+ check: -> { defined?(PWN::AI::Agent::Extrospection) && PWN::AI::Agent::Extrospection.respond_to?(:voice_sense) },
662
+ handler: lambda { |args|
663
+ o = {}
664
+ %i[action text text_path audio out engine model ttl].each { |k| o[k] = args[k] if args[k] }
665
+ o[:record] = args[:record] if args.key?(:record)
666
+ PWN::AI::Agent::Extrospection.voice_sense(o)
667
+ }
668
+ )
@@ -190,13 +190,13 @@ PWN::AI::Agent::Registry.register(
190
190
  )
191
191
 
192
192
  PWN::AI::Agent::Registry.register(
193
- name: 'learning_auto_reflect_toggle',
193
+ name: 'learning_auto_introspect_toggle',
194
194
  toolset: 'learning',
195
195
  schema: {
196
- name: 'learning_auto_reflect_toggle',
196
+ name: 'learning_auto_introspect_toggle',
197
197
  description: 'Enable/disable end-of-run auto-reflection ' \
198
- '(PWN::Env[:ai][:agent][:auto_reflect]). When enabled, ' \
199
- 'Loop.run calls Learning.auto_reflect on the session after ' \
198
+ '(PWN::Env[:ai][:agent][:auto_introspect]). When enabled, ' \
199
+ 'Loop.run calls Learning.auto_introspect on the session after ' \
200
200
  'every final answer. Disable during noisy fuzzing loops; ' \
201
201
  're-enable for the summary turn. Omit `enabled` to just ' \
202
202
  'read the current state.',
@@ -214,8 +214,8 @@ PWN::AI::Agent::Registry.register(
214
214
  raise 'PWN::Env[:ai] is unavailable or immutable' unless ai.is_a?(Hash) && !ai.frozen?
215
215
 
216
216
  ai[:agent] = (ai[:agent] || {}).dup if ai[:agent].nil? || ai[:agent].frozen?
217
- prev = ai[:agent][:auto_reflect] ? true : false
218
- ai[:agent][:auto_reflect] = (args[:enabled] ? true : false) if args.key?(:enabled)
219
- { previous: prev, current: ai[:agent][:auto_reflect] ? true : false }
217
+ prev = ai[:agent][:auto_introspect] ? true : false
218
+ ai[:agent][:auto_introspect] = (args[:enabled] ? true : false) if args.key?(:enabled)
219
+ { previous: prev, current: ai[:agent][:auto_introspect] ? true : false }
220
220
  }
221
221
  )
@@ -20,7 +20,7 @@ module PWN
20
20
 
21
21
  system_role_content = "Being an expert penetration tester skilled in code analysis, debugging, and exploitation while stepping through JavaScript in a Chrome DevTools debugging session: 1. Your sole purpose is to analyze each JavaScript step and generate an Exploit Prediction Scoring System (EPSS) score between 0% - 100%. The step currently resides in this block of JavaScript:\n```\n#{source_to_review}\n```\n2. If the score is >= 75%, generate a JavaScript proof-of-concept that would allow a threat actor to directly exploit or target a user for exploitation (i.e. no self-exploit). 3. If the EPSS score is >= 75% also provide a code fix. *** If the EPSS score is < 75%, no explanations or summaries - just the EPSS score."
22
22
 
23
- PWN::AI::Agent::Introspection.reflect_on(
23
+ PWN::AI::Agent::Reflect.on(
24
24
  system_role_content: system_role_content,
25
25
  request: request,
26
26
  suppress_pii_warning: true
@@ -12,7 +12,7 @@ module PWN
12
12
  # 4. CVSS score, vector string, and first.org calculator URI
13
13
  # 5. CWE category, brief description, and CWE URI
14
14
  # 6. Relevant NIST 800-53 control
15
- # It leverages the PWN::AI::Agent::Introspection.reflect_on method. Defaults to Jira for existing workflow compatibility.
15
+ # It leverages the PWN::AI::Agent::Reflect.on method. Defaults to Jira for existing workflow compatibility.
16
16
  module VulnGen
17
17
  # Supported Method Parameters::
18
18
  # ai_analysis = PWN::AI::Agent::VulnGen.analyze(
@@ -60,7 +60,7 @@ module PWN
60
60
  6. NIST 800-53 Security Control that is impacted by this vulnerability.
61
61
  "
62
62
 
63
- analysis = PWN::AI::Agent::Introspection.reflect_on(
63
+ analysis = PWN::AI::Agent::Reflect.on(
64
64
  system_role_content: system_role_content,
65
65
  request: request,
66
66
  suppress_pii_warning: true
data/lib/pwn/ai/agent.rb CHANGED
@@ -27,7 +27,7 @@ module PWN
27
27
  autoload :Learning, 'pwn/ai/agent/learning'
28
28
  autoload :Mistakes, 'pwn/ai/agent/mistakes'
29
29
  autoload :Extrospection, 'pwn/ai/agent/extrospection'
30
- autoload :Introspection, 'pwn/ai/agent/introspection'
30
+ autoload :Reflect, 'pwn/ai/agent/reflect'
31
31
  autoload :Swarm, 'pwn/ai/agent/swarm'
32
32
 
33
33
  # Display a List of Every PWN::AI Module