pwn 0.5.613 → 0.5.615

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -10
  3. data/README.md +110 -24
  4. data/documentation/AI-Integration.md +36 -0
  5. data/documentation/BurpSuite.md +26 -0
  6. data/documentation/Contributing.md +31 -0
  7. data/documentation/Diagrams.md +57 -0
  8. data/documentation/Drivers.md +25 -0
  9. data/documentation/General-PWN-Usage.md +40 -0
  10. data/documentation/Home.md +51 -0
  11. data/documentation/How-PWN-Works.md +60 -0
  12. data/documentation/Installation.md +50 -0
  13. data/documentation/NmapIt.md +18 -0
  14. data/documentation/Plugins.md +59 -0
  15. data/documentation/Reporting.md +28 -0
  16. data/documentation/SAST.md +32 -0
  17. data/documentation/Skills-Memory-Learning.md +44 -0
  18. data/documentation/Transparent-Browser.md +32 -0
  19. data/documentation/Troubleshooting.md +38 -0
  20. data/documentation/What-is-PWN.md +34 -0
  21. data/documentation/Why-PWN.md +18 -0
  22. data/documentation/diagrams/ai-integration-tool-calling.svg +134 -0
  23. data/documentation/diagrams/burp-vs-zap-preference.svg +84 -0
  24. data/documentation/diagrams/code-scanning-sast.svg +112 -0
  25. data/documentation/diagrams/dot/ai-integration-tool-calling.dot +23 -0
  26. data/documentation/diagrams/dot/burp-vs-zap-preference.dot +15 -0
  27. data/documentation/diagrams/dot/code-scanning-sast.dot +21 -0
  28. data/documentation/diagrams/dot/driver-framework.dot +17 -0
  29. data/documentation/diagrams/dot/fuzzing-workflow.dot +27 -0
  30. data/documentation/diagrams/dot/history-to-drivers.dot +20 -0
  31. data/documentation/diagrams/dot/memory-skills-detailed.dot +28 -0
  32. data/documentation/diagrams/dot/network-infra-testing.dot +22 -0
  33. data/documentation/diagrams/dot/overall-pwn-architecture.dot +49 -0
  34. data/documentation/diagrams/dot/penetration-testing-workflow.dot +29 -0
  35. data/documentation/diagrams/dot/plugin-ecosystem.dot +31 -0
  36. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +57 -0
  37. data/documentation/diagrams/dot/pwn-repl-prototyping.dot +37 -0
  38. data/documentation/diagrams/dot/reporting-pipeline.dot +17 -0
  39. data/documentation/diagrams/dot/reverse-engineering-flow.dot +21 -0
  40. data/documentation/diagrams/dot/sessions-cron-automation.dot +17 -0
  41. data/documentation/diagrams/dot/web-application-testing.dot +27 -0
  42. data/documentation/diagrams/dot/zero-day-research-flow.dot +27 -0
  43. data/documentation/diagrams/driver-framework.svg +90 -0
  44. data/documentation/diagrams/fuzzing-workflow.svg +147 -0
  45. data/documentation/diagrams/history-to-drivers.svg +117 -0
  46. data/documentation/diagrams/memory-skills-detailed.svg +154 -0
  47. data/documentation/diagrams/network-infra-testing.svg +121 -0
  48. data/documentation/diagrams/overall-pwn-architecture.svg +234 -0
  49. data/documentation/diagrams/penetration-testing-workflow.svg +148 -0
  50. data/documentation/diagrams/plugin-ecosystem.svg +172 -0
  51. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +234 -0
  52. data/documentation/diagrams/pwn-repl-prototyping.svg +179 -0
  53. data/documentation/diagrams/reporting-pipeline.svg +91 -0
  54. data/documentation/diagrams/reverse-engineering-flow.svg +114 -0
  55. data/documentation/diagrams/sessions-cron-automation.svg +92 -0
  56. data/documentation/diagrams/web-application-testing.svg +137 -0
  57. data/documentation/diagrams/zero-day-research-flow.svg +149 -0
  58. data/documentation/pwn-REPL.md +33 -0
  59. data/documentation/pwn-ai-Agent.md +43 -0
  60. data/lib/pwn/ai/agent/learning.rb +377 -0
  61. data/lib/pwn/ai/agent/loop.rb +18 -3
  62. data/lib/pwn/ai/agent/metrics.rb +155 -0
  63. data/lib/pwn/ai/agent/prompt_builder.rb +23 -3
  64. data/lib/pwn/ai/agent/tools/learning.rb +113 -0
  65. data/lib/pwn/ai/agent/tools/skills.rb +94 -8
  66. data/lib/pwn/ai/agent.rb +2 -0
  67. data/lib/pwn/ai/grok.rb +249 -106
  68. data/lib/pwn/config.rb +86 -13
  69. data/lib/pwn/plugins/monkey_patch.rb +2 -2
  70. data/lib/pwn/plugins/repl.rb +68 -3
  71. data/lib/pwn/version.rb +1 -1
  72. data/spec/lib/pwn/ai/agent/learning_spec.rb +68 -0
  73. data/spec/lib/pwn/ai/agent/metrics_spec.rb +27 -0
  74. data/spec/lib/pwn/ai/agent/tools/learning_spec.rb +30 -0
  75. metadata +84 -34
  76. data/documentation/PWN.png +0 -0
  77. data/documentation/PWN_Contributors_and_Users.png +0 -0
  78. data/documentation/fax-spectrogram.png +0 -0
  79. data/documentation/fax-waveform.png +0 -0
  80. data/documentation/frozen_string_literal_files_changed_from_false_to_true.txt +0 -54
  81. data/documentation/lifecycle_authz_replay.example.yaml +0 -27
  82. data/documentation/pwn_android_war_dialer_session.png +0 -0
  83. data/documentation/pwn_install.png +0 -0
  84. data/documentation/pwn_wallpaper.jpg +0 -0
  85. data/documentation/ringing-spectrogram.png +0 -0
  86. data/documentation/ringing-waveform.png +0 -0
  87. data/documentation/vulnerability_report_template.md +0 -37
@@ -31,7 +31,7 @@ module PWN
31
31
  pwn : #{pwn_version}
32
32
  session_id : #{session_id || '(none)'}
33
33
 
34
- #{memory_block}#{skills_block}TOOL USE
34
+ #{memory_block}#{skills_block}#{learning_block}#{metrics_block}TOOL USE
35
35
  Use the provided function tools to act on the host. A reply with
36
36
  no tool_calls is treated as your FINAL answer to the user.
37
37
  Prefer `pwn_eval` for anything in the PWN:: namespace and `shell`
@@ -70,15 +70,35 @@ module PWN
70
70
  return '' unless defined?(PWN::Skills) && PWN::Skills.is_a?(Hash) && !PWN::Skills.empty?
71
71
 
72
72
  lines = PWN::Skills.map do |name, meta|
73
- first = meta[:content].to_s.lines.first.to_s.strip
73
+ first = meta[:content].to_s.lines.reject { |l| l.strip.empty? || l.start_with?('---') }.first.to_s.strip
74
74
  first = first[0, 100]
75
- " - #{name}: #{first}"
75
+ rc = Array(meta[:references]).length
76
+ ref_tag = rc.positive? ? " [#{rc} refs]" : ''
77
+ " - #{name}: #{first}#{ref_tag}"
76
78
  end
77
79
  "SKILLS (call skill_view to read full body)\n#{lines.join("\n")}\n\n"
78
80
  rescue StandardError
79
81
  ''
80
82
  end
81
83
 
84
+ private_class_method def self.learning_block
85
+ return '' unless defined?(PWN::AI::Agent::Learning)
86
+
87
+ ctx = PWN::AI::Agent::Learning.to_context(limit: 5).to_s
88
+ ctx.strip.empty? ? '' : "LEARNING\n#{ctx}"
89
+ rescue StandardError
90
+ ''
91
+ end
92
+
93
+ private_class_method def self.metrics_block
94
+ return '' unless defined?(PWN::AI::Agent::Metrics)
95
+
96
+ ctx = PWN::AI::Agent::Metrics.to_context(limit: 8).to_s
97
+ ctx.strip.empty? ? '' : ctx
98
+ rescue StandardError
99
+ ''
100
+ end
101
+
82
102
  # Author(s):: 0day Inc. <support@0dayinc.com>
83
103
 
84
104
  public_class_method def self.authors
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pwn/ai/agent/registry'
4
+ require 'pwn/ai/agent/learning'
5
+ require 'pwn/ai/agent/metrics'
6
+
7
+ # Expose the self-improvement engine to the model so it can actively
8
+ # participate in the learning loop: log outcomes, trigger reflection,
9
+ # promote successful workflows into reusable skills, and inspect its own
10
+ # effectiveness metrics.
11
+
12
+ PWN::AI::Agent::Registry.register(
13
+ name: 'learning_note_outcome',
14
+ toolset: 'learning',
15
+ schema: {
16
+ name: 'learning_note_outcome',
17
+ description: 'Record the outcome of a task attempt so future pwn-ai ' \
18
+ 'runs learn from it. Successes reinforce approaches; ' \
19
+ 'failures become avoidance lessons injected into every ' \
20
+ 'subsequent system prompt.',
21
+ parameters: {
22
+ type: 'object',
23
+ properties: {
24
+ task: { type: 'string', description: 'Short description of what was attempted.' },
25
+ success: { type: 'boolean', description: 'Did the attempt achieve its goal?' },
26
+ details: { type: 'string', description: 'Evidence, error text, or notes.' },
27
+ tags: { type: 'array', items: { type: 'string' } }
28
+ },
29
+ required: %w[task success]
30
+ }
31
+ },
32
+ check: -> { defined?(PWN::AI::Agent::Learning) },
33
+ handler: lambda { |args|
34
+ PWN::AI::Agent::Learning.note_outcome(
35
+ task: args[:task],
36
+ success: args[:success],
37
+ details: args[:details],
38
+ tags: args[:tags]
39
+ )
40
+ }
41
+ )
42
+
43
+ PWN::AI::Agent::Registry.register(
44
+ name: 'learning_reflect',
45
+ toolset: 'learning',
46
+ schema: {
47
+ name: 'learning_reflect',
48
+ description: 'Analyse a completed session transcript and persist up to ' \
49
+ '5 durable lessons into PWN::Memory. Uses LLM introspection ' \
50
+ 'when enabled, otherwise a heuristic extractor.',
51
+ parameters: {
52
+ type: 'object',
53
+ properties: {
54
+ session_id: { type: 'string', description: 'PWN::Sessions id to analyse.' },
55
+ dry_run: { type: 'boolean', default: false }
56
+ },
57
+ required: %w[session_id]
58
+ }
59
+ },
60
+ check: -> { defined?(PWN::AI::Agent::Learning) },
61
+ handler: lambda { |args|
62
+ PWN::AI::Agent::Learning.reflect(
63
+ session_id: args[:session_id],
64
+ dry_run: args[:dry_run]
65
+ )
66
+ }
67
+ )
68
+
69
+ PWN::AI::Agent::Registry.register(
70
+ name: 'learning_distill_skill',
71
+ toolset: 'learning',
72
+ schema: {
73
+ name: 'learning_distill_skill',
74
+ description: 'Promote a successful workflow into a reusable skill under ' \
75
+ '~/.pwn/skills so it appears in every future system prompt.',
76
+ parameters: {
77
+ type: 'object',
78
+ properties: {
79
+ name: { type: 'string', description: 'snake_case skill name.' },
80
+ session_id: { type: 'string', description: 'Session to mine for the procedure.' },
81
+ content: { type: 'string', description: 'Explicit markdown body (overrides session mining).' },
82
+ references: { type: 'array', items: { type: 'string' }, description: 'Optional URLs / CWE / CVE / ATT&CK / NIST references.' }
83
+ },
84
+ required: %w[name]
85
+ }
86
+ },
87
+ check: -> { defined?(PWN::AI::Agent::Learning) },
88
+ handler: lambda { |args|
89
+ PWN::AI::Agent::Learning.distill_skill(
90
+ name: args[:name],
91
+ session_id: args[:session_id],
92
+ content: args[:content],
93
+ references: args[:references]
94
+ )
95
+ }
96
+ )
97
+
98
+ PWN::AI::Agent::Registry.register(
99
+ name: 'learning_stats',
100
+ toolset: 'learning',
101
+ schema: {
102
+ name: 'learning_stats',
103
+ description: 'Return self-effectiveness metrics: outcome success rate, ' \
104
+ 'skills known, memory size, and per-tool telemetry.',
105
+ parameters: { type: 'object', properties: {}, required: [] }
106
+ },
107
+ check: -> { defined?(PWN::AI::Agent::Learning) },
108
+ handler: lambda { |_args|
109
+ PWN::AI::Agent::Learning.stats.merge(
110
+ tool_metrics: PWN::AI::Agent::Metrics.summary(limit: 10)
111
+ )
112
+ }
113
+ )
@@ -6,19 +6,30 @@ require 'pwn/ai/agent/registry'
6
6
  # Thin wrappers around the PWN::Skills store (~/.pwn/skills/*.md) so the
7
7
  # model can list/read/write reusable procedures. PromptBuilder injects
8
8
  # the index (name + first line); skill_view loads the full body on demand.
9
+ #
10
+ # Each skill may carry a :references Array (URLs, CWE/CVE/ATT&CK ids,
11
+ # NIST 800-53 controls, docs) parsed from YAML front-matter or a
12
+ # "## References" markdown section. References let the agent cite
13
+ # authoritative sources when applying a skill and mirror the
14
+ # PWN::SAST::*.security_references convention.
9
15
 
10
16
  PWN::AI::Agent::Registry.register(
11
17
  name: 'skill_list',
12
18
  toolset: 'skills',
13
19
  schema: {
14
20
  name: 'skill_list',
15
- description: 'List available pwn-ai skills (name + first line of content).',
21
+ description: 'List available pwn-ai skills (name + first line + reference count).',
16
22
  parameters: { type: 'object', properties: {}, required: [] }
17
23
  },
18
24
  check: -> { defined?(PWN::Skills) && PWN::Skills.is_a?(Hash) },
19
25
  handler: lambda { |_args|
20
26
  PWN::Skills.map do |name, meta|
21
- { name: name, type: meta[:type], summary: meta[:content].to_s.lines.first.to_s.strip[0, 120] }
27
+ {
28
+ name: name,
29
+ type: meta[:type],
30
+ summary: meta[:content].to_s.lines.first.to_s.strip[0, 120],
31
+ references: Array(meta[:references]).length
32
+ }
22
33
  end
23
34
  }
24
35
  )
@@ -28,7 +39,7 @@ PWN::AI::Agent::Registry.register(
28
39
  toolset: 'skills',
29
40
  schema: {
30
41
  name: 'skill_view',
31
- description: 'Read the full content of a named skill.',
42
+ description: 'Read the full content of a named skill, including its references.',
32
43
  parameters: {
33
44
  type: 'object',
34
45
  properties: { name: { type: 'string' } },
@@ -41,7 +52,13 @@ PWN::AI::Agent::Registry.register(
41
52
  meta = PWN::Skills[key]
42
53
  raise ArgumentError, "no such skill: #{key}" unless meta
43
54
 
44
- { name: key, type: meta[:type], path: meta[:path], content: meta[:content] }
55
+ {
56
+ name: key,
57
+ type: meta[:type],
58
+ path: meta[:path],
59
+ references: Array(meta[:references]),
60
+ content: meta[:content]
61
+ }
45
62
  }
46
63
  )
47
64
 
@@ -51,12 +68,20 @@ PWN::AI::Agent::Registry.register(
51
68
  schema: {
52
69
  name: 'skill_create',
53
70
  description: 'Save a new reusable skill (markdown procedure) to ~/.pwn/skills/. ' \
54
- 'It will be listed in every future pwn-ai system prompt.',
71
+ 'It will be listed in every future pwn-ai system prompt. Optionally ' \
72
+ 'attach references (URLs, CWE/CVE ids, MITRE ATT&CK, NIST 800-53, docs).',
55
73
  parameters: {
56
74
  type: 'object',
57
75
  properties: {
58
76
  name: { type: 'string', description: 'lowercase, hyphens/underscores only' },
59
- content: { type: 'string', description: 'Full markdown body of the skill.' }
77
+ content: { type: 'string', description: 'Full markdown body of the skill.' },
78
+ references: {
79
+ type: 'array',
80
+ items: { type: 'string' },
81
+ description: 'Optional list of reference URLs / identifiers (CWE-79, T1059, ' \
82
+ 'https://..., NIST SI-3, etc.). Written as YAML front-matter and ' \
83
+ 'a "## References" section.'
84
+ }
60
85
  },
61
86
  required: %w[name content]
62
87
  }
@@ -66,11 +91,72 @@ PWN::AI::Agent::Registry.register(
66
91
  name = args[:name].to_s.gsub(/[^a-z0-9_-]/i, '_')
67
92
  raise ArgumentError, 'name is required' if name.empty?
68
93
 
94
+ body = args[:content].to_s
95
+ refs = Array(args[:references]).map(&:to_s).map(&:strip).reject(&:empty?).uniq
96
+
97
+ unless refs.empty?
98
+ # YAML front-matter (only if body doesn't already have one)
99
+ unless body.start_with?("---\n")
100
+ fm = "---\nreferences:\n#{refs.map { |r| " - #{r}" }.join("\n")}\n---\n"
101
+ body = fm + body
102
+ end
103
+ # ## References section (only if not already present)
104
+ body = "#{body.rstrip}\n\n## References\n#{refs.map { |r| "- #{r}" }.join("\n")}\n" unless body =~ /^\#{1,3}\s*References\s*$/i
105
+ end
106
+
69
107
  dir = PWN::Config.pwn_skills_path
70
108
  FileUtils.mkdir_p(dir)
71
109
  path = File.join(dir, "#{name}.md")
72
- File.write(path, args[:content].to_s)
110
+ File.write(path, body)
73
111
  PWN::Config.load_skills(pwn_skills_path: dir) # refresh PWN::Skills const
74
- { saved: true, path: path, total: PWN::Skills.keys.length }
112
+ { saved: true, path: path, references: refs, total: PWN::Skills.keys.length }
113
+ }
114
+ )
115
+
116
+ PWN::AI::Agent::Registry.register(
117
+ name: 'skill_add_reference',
118
+ toolset: 'skills',
119
+ schema: {
120
+ name: 'skill_add_reference',
121
+ description: 'Append one or more references (URL, CWE/CVE id, MITRE ATT&CK ' \
122
+ 'technique, NIST control, doc link) to an existing skill.',
123
+ parameters: {
124
+ type: 'object',
125
+ properties: {
126
+ name: { type: 'string', description: 'Existing skill name.' },
127
+ references: { type: 'array', items: { type: 'string' } }
128
+ },
129
+ required: %w[name references]
130
+ }
131
+ },
132
+ check: -> { defined?(PWN::Skills) && PWN::Skills.is_a?(Hash) },
133
+ handler: lambda { |args|
134
+ key = args[:name].to_s.to_sym
135
+ meta = PWN::Skills[key]
136
+ raise ArgumentError, "no such skill: #{key}" unless meta
137
+
138
+ new_refs = Array(args[:references]).map(&:to_s).map(&:strip).reject(&:empty?)
139
+ raise ArgumentError, 'references must be a non-empty array' if new_refs.empty?
140
+
141
+ body = File.read(meta[:path])
142
+ existing = Array(meta[:references])
143
+ add = (new_refs - existing)
144
+
145
+ if body =~ /^(\#{1,3}\s*References\s*)$/i
146
+ # Append bullets right after the References heading block (end of file safest)
147
+ body = "#{body.rstrip}\n#{add.map { |r| "- #{r}" }.join("\n")}\n" unless add.empty?
148
+ else
149
+ body = "#{body.rstrip}\n\n## References\n#{(existing + add).uniq.map { |r| "- #{r}" }.join("\n")}\n"
150
+ end
151
+
152
+ File.write(meta[:path], body)
153
+ PWN::Config.load_skills(pwn_skills_path: File.dirname(meta[:path]))
154
+ {
155
+ saved: true,
156
+ name: key,
157
+ path: meta[:path],
158
+ added: add,
159
+ references: Array(PWN::Skills[key][:references])
160
+ }
75
161
  }
76
162
  )
data/lib/pwn/ai/agent.rb CHANGED
@@ -23,6 +23,8 @@ module PWN
23
23
  autoload :Result, 'pwn/ai/agent/result'
24
24
  autoload :PromptBuilder, 'pwn/ai/agent/prompt_builder'
25
25
  autoload :Loop, 'pwn/ai/agent/loop'
26
+ autoload :Metrics, 'pwn/ai/agent/metrics'
27
+ autoload :Learning, 'pwn/ai/agent/learning'
26
28
 
27
29
  # Display a List of Every PWN::AI Module
28
30