pwn 0.5.783 → 0.5.785

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 (264) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +8 -8
  4. data/README.md +37 -0
  5. data/Rakefile +19 -1
  6. data/bin/pwn +2 -1
  7. data/bin/pwn_setup +1 -1
  8. data/bin/pwn_web_cache_deception +35 -7
  9. data/build_gem.sh +1 -1
  10. data/documentation/AI-Integration.md +7 -5
  11. data/documentation/Agent-Tool-Registry.md +2 -2
  12. data/documentation/Backup-Retention.md +82 -0
  13. data/documentation/Configuration.md +10 -6
  14. data/documentation/Contributing.md +1 -1
  15. data/documentation/Cron.md +64 -35
  16. data/documentation/Diagrams.md +2 -2
  17. data/documentation/General-PWN-Usage.md +4 -3
  18. data/documentation/Home.md +6 -5
  19. data/documentation/How-PWN-Works.md +7 -6
  20. data/documentation/Installation.md +254 -10
  21. data/documentation/Mission-Cancellation.md +134 -0
  22. data/documentation/Operability.md +17 -0
  23. data/documentation/Persistence.md +8 -6
  24. data/documentation/Plugin-Degradation.md +12 -0
  25. data/documentation/Plugins.md +3 -2
  26. data/documentation/Reinforcement-Learning.md +5 -6
  27. data/documentation/Runtime-Source-Protection.md +162 -0
  28. data/documentation/Transparent-Browser.md +11 -0
  29. data/documentation/Troubleshooting.md +3 -2
  30. data/documentation/What-is-PWN.md +2 -2
  31. data/documentation/YubiKey-Vaults.md +183 -0
  32. data/documentation/YubiKey.md +339 -0
  33. data/documentation/diagrams/cron-scheduling.svg +130 -128
  34. data/documentation/diagrams/dot/cron-scheduling.dot +5 -5
  35. data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
  36. data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
  37. data/documentation/diagrams/persistence-filesystem.svg +172 -142
  38. data/documentation/diagrams/plugin-ecosystem.svg +64 -64
  39. data/documentation/pwn-ai-Agent.md +10 -4
  40. data/etc/default_cron/session_cleanup.rb +1245 -22
  41. data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
  42. data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
  43. data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
  44. data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
  45. data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
  46. data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
  47. data/etc/default_skills/pwn/backups/SKILL.md +54 -0
  48. data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
  49. data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
  50. data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
  51. data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
  52. data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
  53. data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
  54. data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
  55. data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
  56. data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
  57. data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
  58. data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
  59. data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
  60. data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
  61. data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
  62. data/lib/pwn/ai/agent/dispatch.rb +26 -15
  63. data/lib/pwn/ai/agent/extrospection.rb +6 -7
  64. data/lib/pwn/ai/agent/learning.rb +62 -25
  65. data/lib/pwn/ai/agent/loop.rb +17 -1
  66. data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
  67. data/lib/pwn/ai/agent/policy.rb +5 -0
  68. data/lib/pwn/ai/agent/policy_evaluation.rb +8 -8
  69. data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
  70. data/lib/pwn/ai/agent/request_runtime.rb +122 -18
  71. data/lib/pwn/ai/agent/reward.rb +5 -3
  72. data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
  73. data/lib/pwn/ai/agent/skill_review.rb +9 -5
  74. data/lib/pwn/ai/agent/swarm.rb +35 -16
  75. data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
  76. data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
  77. data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
  78. data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
  79. data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
  80. data/lib/pwn/ai/agent.rb +1 -0
  81. data/lib/pwn/ai/anthropic.rb +8 -33
  82. data/lib/pwn/ai/cli.rb +34 -5
  83. data/lib/pwn/ai/gemini.rb +1 -0
  84. data/lib/pwn/ai/grok.rb +39 -28
  85. data/lib/pwn/ai/ollama.rb +4 -2
  86. data/lib/pwn/ai/open_ai.rb +11 -32
  87. data/lib/pwn/ai/open_web_ui.rb +62 -4
  88. data/lib/pwn/ai/router.rb +53 -3
  89. data/lib/pwn/ai/server_tools.rb +231 -0
  90. data/lib/pwn/ai.rb +1 -0
  91. data/lib/pwn/backups.rb +347 -0
  92. data/lib/pwn/config.rb +70 -39
  93. data/lib/pwn/cron.rb +120 -49
  94. data/lib/pwn/migrate.rb +150 -68
  95. data/lib/pwn/module_skills.rb +7 -0
  96. data/lib/pwn/plugins/ai_sandbox.rb +538 -1
  97. data/lib/pwn/plugins/authentication_helper.rb +74 -4
  98. data/lib/pwn/plugins/basic_auth.rb +2 -1
  99. data/lib/pwn/plugins/binary_parser.rb +5 -4
  100. data/lib/pwn/plugins/ghidra.rb +4 -3
  101. data/lib/pwn/plugins/google_workspace.rb +4 -21
  102. data/lib/pwn/plugins/jobs.rb +1 -1
  103. data/lib/pwn/plugins/log.rb +21 -3
  104. data/lib/pwn/plugins/preflight_checker.rb +34 -9
  105. data/lib/pwn/plugins/recon.rb +4 -1
  106. data/lib/pwn/plugins/repl/ai.rb +305 -27
  107. data/lib/pwn/plugins/repl/ai_console.rb +273 -89
  108. data/lib/pwn/plugins/repl/ai_swarm.rb +181 -62
  109. data/lib/pwn/plugins/repl/mesh.rb +3 -16
  110. data/lib/pwn/plugins/repl/vault.rb +4 -15
  111. data/lib/pwn/plugins/repl.rb +1 -216
  112. data/lib/pwn/plugins/sandbox/driver.rb +4 -0
  113. data/lib/pwn/plugins/transparent_browser.rb +215 -0
  114. data/lib/pwn/plugins/vault.rb +412 -135
  115. data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
  116. data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
  117. data/lib/pwn/plugins/yubikey/management.rb +686 -0
  118. data/lib/pwn/plugins/yubikey/oath.rb +766 -0
  119. data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
  120. data/lib/pwn/plugins/yubikey/otp.rb +897 -0
  121. data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
  122. data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
  123. data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
  124. data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
  125. data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
  126. data/lib/pwn/plugins/yubikey.rb +844 -0
  127. data/lib/pwn/plugins.rb +1 -0
  128. data/lib/pwn/redaction.rb +69 -12
  129. data/lib/pwn/session_trace.rb +4 -0
  130. data/lib/pwn/sessions.rb +58 -16
  131. data/lib/pwn/setup.rb +123 -41
  132. data/lib/pwn/version.rb +1 -1
  133. data/lib/pwn/www/app_cobalt_io.rb +16 -5
  134. data/lib/pwn/www/bug_crowd.rb +16 -5
  135. data/lib/pwn/www/coinbase_pro.rb +16 -5
  136. data/lib/pwn/www/github.rb +30 -14
  137. data/lib/pwn/www/paypal.rb +15 -4
  138. data/lib/pwn/www/synack.rb +16 -5
  139. data/lib/pwn/www/twitter.rb +17 -6
  140. data/lib/pwn.rb +1 -0
  141. data/scripts/build_gate.rb +58 -0
  142. data/spec/documentation/installation_md_spec.rb +1 -1
  143. data/spec/fixtures/yubikey/apdu_frames.json +162 -0
  144. data/spec/integration/authentication_helper_oath_spec.rb +117 -0
  145. data/spec/integration/build_gate_spec.rb +73 -0
  146. data/spec/integration/controller_spill_spec.rb +48 -0
  147. data/spec/integration/harness_process_spec.rb +97 -0
  148. data/spec/integration/mission_cancel_spec.rb +79 -0
  149. data/spec/integration/mission_curses_pty_spec.rb +44 -0
  150. data/spec/integration/persistence_roundtrip_spec.rb +1 -7
  151. data/spec/integration/readme_security_spec.rb +47 -0
  152. data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
  153. data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
  154. data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
  155. data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
  156. data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
  157. data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
  158. data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
  159. data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
  160. data/spec/lib/pwn/ai/agent/policy_spec.rb +27 -0
  161. data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
  162. data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
  163. data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
  164. data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
  165. data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
  166. data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
  167. data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
  168. data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
  169. data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
  170. data/spec/lib/pwn/ai/cli_spec.rb +6 -9
  171. data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
  172. data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
  173. data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
  174. data/spec/lib/pwn/ai/router_spec.rb +31 -3
  175. data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
  176. data/spec/lib/pwn/backups_spec.rb +114 -0
  177. data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
  178. data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
  179. data/spec/lib/pwn/cron_spec.rb +113 -6
  180. data/spec/lib/pwn/migrate_spec.rb +108 -5
  181. data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
  182. data/spec/lib/pwn/module_skills_spec.rb +8 -0
  183. data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
  184. data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
  185. data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
  186. data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
  187. data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
  188. data/spec/lib/pwn/plugins/log_spec.rb +7 -0
  189. data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
  190. data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
  191. data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
  192. data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
  193. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +125 -78
  194. data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
  195. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +30 -16
  196. data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
  197. data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
  198. data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
  199. data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
  200. data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
  201. data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
  202. data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
  203. data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
  204. data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
  205. data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
  206. data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
  207. data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
  208. data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
  209. data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
  210. data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
  211. data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
  212. data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
  213. data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
  214. data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
  215. data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
  216. data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
  217. data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
  218. data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
  219. data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
  220. data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
  221. data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
  222. data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
  223. data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
  224. data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
  225. data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
  226. data/spec/lib/pwn/redaction_spec.rb +13 -10
  227. data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
  228. data/spec/lib/pwn/session_cleanup_concurrency_spec.rb +201 -0
  229. data/spec/lib/pwn/session_cleanup_default_spec.rb +167 -2
  230. data/spec/lib/pwn/session_cleanup_legacy_spec.rb +101 -0
  231. data/spec/lib/pwn/session_cleanup_payload_spec.rb +335 -2
  232. data/spec/lib/pwn/session_cleanup_recovery_spec.rb +403 -0
  233. data/spec/lib/pwn/sessions_spec.rb +112 -0
  234. data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
  235. data/spec/lib/pwn/setup_spec.rb +3 -1
  236. data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
  237. data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
  238. data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
  239. data/spec/spec_helper.rb +15 -0
  240. data/spec/support/gate_failure_formatter.rb +14 -0
  241. data/spec/support/harness/cli_faults.rb +13 -9
  242. data/spec/support/harness/controller_spill.rb +26 -0
  243. data/spec/support/harness/controller_state.rb +54 -0
  244. data/spec/support/harness/evaluate.rb +12 -1
  245. data/spec/support/harness/experiments.rb +1 -1
  246. data/spec/support/harness/fake_provider.rb +22 -6
  247. data/spec/support/harness/fault_preload.rb +3 -1
  248. data/spec/support/harness/mission_console.rb +54 -0
  249. data/spec/support/harness/mission_curses_pty.rb +41 -0
  250. data/spec/support/harness/mission_dispatch.rb +30 -0
  251. data/spec/support/harness/mission_fixture_preload.rb +24 -0
  252. data/spec/support/harness/mission_job_lifetime.rb +25 -0
  253. data/spec/support/harness/mission_legacy.rb +25 -0
  254. data/spec/support/harness/mission_normal_exit.rb +34 -0
  255. data/spec/support/harness/mission_swarm.rb +23 -0
  256. data/spec/support/harness/mission_unsupported.rb +29 -0
  257. data/spec/support/harness/package_acceptance.rb +33 -5
  258. data/spec/support/harness/session_acceptance.rb +4 -4
  259. data/spec/support/harness/swarm_acceptance.rb +1 -1
  260. data/spec/support/harness/swarm_control.rb +44 -0
  261. data/spec/support/mission_namespace.rb +14 -0
  262. data/spec/support/mission_pipe_queue.rb +33 -0
  263. data/third_party/pwn_rdoc.jsonl +599 -15
  264. metadata +140 -17
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: pwn-plugins-yubikey-otp
3
+ description: Use native OTP slots with explicit consent and readback.
4
+ license: MIT
5
+ allowed-tools: [pwn, pwn_eval]
6
+ metadata:
7
+ bundled: true
8
+ preserve: true
9
+ module: PWN::Plugins::YubiKey::OTP
10
+ source: pwn/plugins/yubikey/otp.rb
11
+ ---
12
+
13
+ # PWN::Plugins::YubiKey::OTP
14
+
15
+ Native Ruby OTP application commands over the selected `Connection.exchange`
16
+ boundary. No executable, subprocess, interpreter or network backend. HID framing
17
+ and CRC validation belong to `Transport`; APDU status handling belongs to
18
+ `Protocol`. The caller owns its transport connection.
19
+
20
+ ## Operations
21
+
22
+ `execute(args: [...], connection: ...)` accepts argv **after** `otp`.
23
+ `operations` lists ten leaves: `info`, `swap`, `ndef`, `delete`, `yubiotp`,
24
+ `static`, `chalresp`, `calculate`, `hotp`, `settings`. Empty args return local
25
+ metadata, not a device query. Unknown flags/commands and credential argv raise.
26
+
27
+ Typed methods: `info`, `swap`, `delete_slot`, `configure_ndef`, `settings`,
28
+ `provision_yubiotp`, `provision_static`, `provision_hotp`,
29
+ `provision_challenge_response`, `challenge_response`, `calculate`, `help`,
30
+ `authors`. Read runtime `help` for the full option contract.
31
+
32
+ ## Safe workflow
33
+
34
+ 1. Select the exact intended device using root transport selectors. Do not scan
35
+ or open real hardware in default tests. A model name does not establish its
36
+ supported/enabled applications.
37
+ 2. Read `info` without authentication. Firmware/status observations, not guessed
38
+ credentials, gate slot operations. NDEF additionally checks actual NFC support.
39
+ 3. Require literal `confirm: true` for every write or calculation. `--force` is
40
+ syntactic compatibility, not consent. Never provision to repair a vault-open
41
+ failure, stale response, wrong slot or missing device.
42
+ 4. Supply secrets as raw in-memory `key:`, `password:`, `private_id:`,
43
+ `access_code:` and `new_access_code:`. An explicit `secret_provider` receives
44
+ a missing secret's Symbol. No implicit terminal reads or factory secrets.
45
+ Generated material requires `secret_sink.call(field, raw_secret)`. The sink is
46
+ caller-controlled and must not log or publish secrets.
47
+ 5. `challenge_response(connection:, challenge:, slot: 2, confirm: true)` accepts
48
+ **1–64 raw bytes** and returns **20 raw HMAC-SHA1 bytes**. It never hex-encodes,
49
+ truncates to a TOTP or caches a response. Trailing zero challenges use distinct
50
+ padding. USB calculation requires OTP HID; smartcard calculation is NFC-only.
51
+ 6. `provision_challenge_response` defaults to touch-required and performs a fresh
52
+ native OpenSSL HMAC comparison against device output after programming and
53
+ separate status readback. Its default return is `:verified`; `details: true`
54
+ returns scoped verification metadata. `execute ['chalresp', '2']` returns the
55
+ metadata and follows prior CLI default
56
+ of no touch unless `--touch`. `--totp` explicitly decodes supplied Base32 key
57
+ text in memory; otherwise `key_encoding` defaults to raw. Keys 21–64 bytes
58
+ cannot fit this application; longer HMAC keys are SHA1-shortened per HMAC.
59
+ 7. Check `verification`, not only `verified`. Other OTP credentials/settings have
60
+ no secret/config export command: their verification is programming sequence
61
+ and slot-state readback, **not proof of keyboard output or secret equality**.
62
+ Known 5.0–5.4.2
63
+ smartcard zero-sequence responses are labeled `firmware_quirk_slot_state`;
64
+ HMAC provisioning must still pass the cryptographic challenge comparison.
65
+ Never claim that state metadata proves an NFC URI's emitted value. Failed or
66
+ stale readback raises an uncertainty error; no write is automatically replayed.
67
+
68
+ ## Formats and device restrictions
69
+
70
+ - YubiOTP: public ID is lowercase modhex, private ID six raw bytes, AES key sixteen
71
+ raw bytes. `--serial-public-id` reads the actual API serial.
72
+ - Static passwords: 1–38 characters; MODHEX, US, UK, DE, FR, IT, BEPO and NORMAN
73
+ scan-code maps. Unsupported characters fail before programming. The map data
74
+ retains Yubico's BSD license in source.
75
+ - HOTP: raw HMAC key, 6/8 digits, initial counter divisible by 16 and at most
76
+ 1048560; optional modhex/BCD token identifier.
77
+ - NDEF: URI prefix compression or UTF8 English text, encoded prefix ≤54 bytes;
78
+ requires a configured slot on an NFC-capable key.
79
+ - Settings replace omitted keyboard options with defaults, as the prior command
80
+ did; they do not infer hidden previous settings. Firmware 4.3.2–4.3.5 access-code
81
+ updates are refused without any delete/reprogram fallback.
82
+ - Explicit YubiOTP `config_output` appends Yubico CSV only after status readback to
83
+ an owned, mode-0600 regular file with no symlinks/hardlinks and an exclusive
84
+ lock. CSV contains secrets. `--upload` is retired upstream and fails without
85
+ any network call. Generated secrets still require an explicit in-memory sink.
86
+
87
+ ## Evidence and references
88
+
89
+ Offline specs use fake **connection wire responses**, real shared APDU codecs,
90
+ RFC 2202 HMAC and RFC 6238 TOTP vectors, and an independently computed fixed
91
+ 52-byte configuration/CRC vector. These are not hardware interoperability tests.
92
+
93
+ Protocol provenance: official yubikey-manager **5.9.1** source (`yubikit/yubiotp.py`,
94
+ `yubikit/core/otp.py`, `ykman/scancodes`) and
95
+ <https://developers.yubico.com/yubikey-personalization/Manuals/ykpersonalize.1.html>.
96
+ Native reference header:
97
+ <https://github.com/Yubico/yubikey-personalization/blob/master/ykcore/ykdef.h>.
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: pwn-plugins-yubikey-piv
3
+ description: Native PIV APDU keys, certificates, objects and crypto.
4
+ ---
5
+
6
+ # PWN::Plugins::YubiKey::PIV
7
+
8
+ Use native Ruby/OpenSSL PIV operations through the parent YubiKey connection and
9
+ Protocol codec. No command-line helper, Python runtime, device discovery during
10
+ load, or private-key export is used.
11
+
12
+ ## Entry points
13
+
14
+ - `execute(connection:, args:, ...)`: family-relative commands (`keys generate`,
15
+ `certificates request`, `objects export`, `access change-pin`, and so on).
16
+ Empty args or an access/keys/certificates/objects group return local help.
17
+ - `operations`: implemented leaf catalog, separate from group/help paths.
18
+ - `verify_pin(connection:, pin:, confirm: true)` and
19
+ `authenticate(connection:, management_key:, confirm: true)`.
20
+ - `sign(connection:, slot:, algorithm:, message:, pin:, confirm: true)` supports
21
+ RSA PKCS1/PSS, P-256/P-384 ECDSA, and Ed25519. `hash_algorithm:` defaults SHA256;
22
+ `prehashed: true` accepts a correctly sized digest except Ed25519 always uses
23
+ the complete message. RSA `padding:` is `:pkcs1` or `:pss`.
24
+ - `decrypt(connection:, slot:, ciphertext:, padding: :oaep, pin:, confirm: true)`
25
+ supports RSA OAEP (hash, MGF1 hash, label), PKCS1 and explicit raw `:none`.
26
+ - `calculate_secret(connection:, slot:, peer_public_key:, pin:, confirm: true)`
27
+ supports P-256/P-384 and X25519. Treat its output as raw KDF input, not an
28
+ application encryption key.
29
+
30
+ ## Consent, secrets, and output
31
+
32
+ Every mutation/authentication/private-key operation requires literal
33
+ `confirm: true`. `--force` never grants consent. PINs, PUKs, management keys,
34
+ passwords and replacements belong in options, never argv. Management keys are
35
+ raw binary bytes, not hex strings. A `secret_provider` callable can supply a
36
+ missing field by Symbol, after consent. Factory credentials are never guessed.
37
+
38
+ `-` as an input filename requires binary `input:`; no implicit terminal read.
39
+ `-` as output returns raw bytes. Named output requires confirmation and is
40
+ created exclusively with mode 0600; existing files are never overwritten.
41
+ `keys export` only returns the public key. `public_key_source:` explicitly
42
+ selects `:metadata` (default, firmware 5.3+), `:attestation` (4.3+) or
43
+ `:certificate`. There is no fallback after a failed key source.
44
+
45
+ Firmware gates include ROCA-vulnerable RSA generation, FIPS restrictions,
46
+ PIN/touch policies and biometric MATCH policy detection. RSA3072/RSA4096,
47
+ Ed25519/X25519 and move/delete require firmware 5.7+. AES management keys require
48
+ 5.4+. Supply `fips_restrictions: true` when device capability metadata identifies
49
+ a newer FIPS device.
50
+
51
+ ## Management and lifecycle
52
+
53
+ `access change-management-key` accepts `new_management_key:` and `--algorithm`
54
+ TDES/AES128/AES192/AES256. `--protect` stores the new key in PIN-protected PIVMAN
55
+ data. Explicit `use_protected_key: true` enables PIN-protected or legacy
56
+ PIN-derived management authentication; corrupt metadata is never repaired
57
+ implicitly. Unprotected `--generate` requires `secret_sink.call(field, bytes)`
58
+ to securely retain the generated key before installing it. Returned status
59
+ never includes the management key.
60
+
61
+ Key generation/import accepts PIN and touch policies. Import supports native
62
+ OpenSSL PEM/DER private keys, including encrypted inputs through `password:`.
63
+ Certificates support PEM/DER and PKCS12 import, compressed storage, device-signed
64
+ self-signed certificates and PKCS10 requests. The host builds ASN.1, but only
65
+ the device private-key operation signs the TBS/request bytes. The result is
66
+ verified with OpenSSL before storage/return. Signed or corrupt CHUID objects
67
+ are left intact. Certificate changes update unsigned CHUID GUIDs unless
68
+ `--no-update-chuid` is passed. `objects generate` accepts CHUID and CCC only.
69
+ Object writes are read back and compared before reporting success. Multi-APDU
70
+ operations are not transactions: failures must be reconciled, not auto-retried.
71
+
72
+ ## Reset
73
+
74
+ `execute(args: ['reset'], confirm: true)` only sends reset after checking for
75
+ enrolled biometrics; it never guesses or blocks PINs/PUKs. Firmware requires
76
+ both retry counters to be blocked. To explicitly authorize the destructive
77
+ blocking process, also supply `block_retries: true`. This sends deliberately
78
+ invalid empty padded PIN and PUK references until their counters reach zero,
79
+ with bounded attempts and decreasing-counter checks, then resets PIV. This
80
+ wipes application data and restores factory references; biometric/full-device
81
+ reset restrictions are not bypassed. Never enable blocking as an automatic
82
+ recovery action.
83
+
84
+ ## Implementation verification rules
85
+
86
+ - Encode RSA public material as SubjectPublicKeyInfo with rsaEncryption and NULL
87
+ parameters. Generic OpenSSL loading can interpret a bare two-INTEGER RSA
88
+ public sequence as DH parameters; the RSA generation/import regressions catch
89
+ this ambiguity.
90
+ - For RSA PKCS1 decryption on OpenSSL 3.2+, explicitly disable implicit rejection
91
+ for the ephemeral padding key. Otherwise an invalid block can return synthetic
92
+ plaintext instead of an exception. The paired negative test requires failure.
93
+ - Keep temporary biometric PINs within one selected session: retain a
94
+ `PIV::Session.new(connection: connection, confirm: true)` and call its
95
+ `verify_uv(request_pin: true)` and `verify_temporary_pin(bytes)` methods on
96
+ that same instance. Do not assume reselection preserves a temporary PIN.
97
+ - Separate the transport `serial:` selector from `certificate_serial:` when
98
+ constructing certificates. Tests preserve a zero-prefixed transport selector.
99
+
100
+ ## Offline verification and protocol references
101
+
102
+ The paired RSpec uses literal APDU fixtures and a synthetic card boundary,
103
+ independently reconstructs imported private keys, and validates actual RSA,
104
+ ECDSA, Ed25519, ECDH, X25519, certificate and CSR cryptography with OpenSSL.
105
+ It never accesses a physical reader, network, credentials or live home state.
106
+ These fixtures demonstrate host protocol behavior, not physical interoperability.
107
+
108
+ Wire specifications: NIST SP 800-73; Yubico yubikey-manager 5.9.1 source files
109
+ `yubikit/piv.py`, `ykman/piv.py`, `ykman/_cli/piv.py`. Sources were consulted as
110
+ specifications only, not imported or executed by the implementation.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: pwn-plugins-yubikey-protocol
3
+ description: Native binary framing for YubiKey device protocols.
4
+ license: MIT
5
+ allowed-tools: [pwn, pwn_eval]
6
+ metadata:
7
+ bundled: true
8
+ preserve: true
9
+ module: PWN::Plugins::YubiKey::Protocol
10
+ source: pwn/plugins/yubikey/protocol.rb
11
+ ---
12
+
13
+ # Native YubiKey protocol framing
14
+
15
+ Use the shared native Ruby codec rather than reconstructing binary headers in
16
+ callers. Preserve raw bytes, repeated TLV tags and exact identifiers. Reject
17
+ truncated/oversized frames before issuing device operations. This module does not
18
+ run an executable or discover hardware merely when loaded.
19
+
20
+ See `PWN::Plugins::YubiKey::Protocol.help` for the current public codec surface.
21
+ Offline framing regressions are in `spec/lib/pwn/plugins/yubikey/protocol_spec.rb`.
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: pwn-plugins-yubikey-security-domain
3
+ description: Manage native YubiKey Security Domain keys and channels.
4
+ license: MIT
5
+ allowed-tools: [pwn, pwn_eval]
6
+ metadata:
7
+ bundled: true
8
+ preserve: true
9
+ module: PWN::Plugins::YubiKey::SecurityDomain
10
+ source: pwn/plugins/yubikey/security_domain.rb
11
+ ---
12
+
13
+ # Native Security Domain
14
+
15
+ Use `operations`, `help`, `execute(args: ..., connection: ...)` and
16
+ `secure_connection(connection: ..., scp: ..., kvn: ..., confirm: true)`.
17
+ Key and certificate values stay in Ruby/OpenSSL memory, never command-line
18
+ arguments, environment variables, logs or subprocesses. No factory keys are
19
+ assumed. Never print key objects, raw APDUs, or key-bearing options.
20
+
21
+ ## Secure connection
22
+
23
+ - SCP03: explicit 16-byte `key_enc`, `key_mac`, optional `key_dek` binary Strings.
24
+ - SCP11a/b/c: explicit `card_public_key` EC pin, or trusted `ca_certificates`
25
+ Array of X509 objects/PEM/DER; `card_certificates` is optional leaf-last chain.
26
+ With only CA anchors, fetch the card bundle and verify it before authentication.
27
+ - SCP11a/c additionally require `oce_private_key` and leaf-last `oce_certificates`;
28
+ optional `oce_kid` and `oce_kvn` identify the card's OCE CA key.
29
+ - Every handshake requires literal `confirm: true`. The returned connection
30
+ provides `exchange(protocol: :apdu, data:, timeout:)`, `descriptor`, `close`.
31
+ - Receipt/card cryptogram failure, malformed response, bad R-MAC, uncertain I/O,
32
+ or exhausted counter closes the channel. No plaintext retry or fallback.
33
+ - SCP11b authenticates the card only. SCP11c does not grant SD administrative
34
+ privilege. Use SCP03 or SCP11a for SD writes.
35
+
36
+ ## Administrative operations
37
+
38
+ `execute` implements `info`, `reset`, `keys generate`, `keys import`,
39
+ `keys export`, `keys delete`, `keys set-allowlist`, `keys set-certificate`,
40
+ and `keys set-ca-issuer`. Group paths only return local help metadata.
41
+
42
+ Pass KID/KVN as exact unsigned decimal or `0x` hexadecimal argv tokens. Imports
43
+ and generation require KVN 1..127. KID 1 imports the complete SCP03 keyset from
44
+ `key_enc`, `key_mac`, `key_dek`; KID 0x11/0x13/0x15 imports an EC `key`;
45
+ KID 0x10/0x20..0x2f imports an EC public `key` or CA `certificate`.
46
+ All key wrapping uses the authenticated session DEK. KCV verification is mandatory.
47
+ Private-key imports may include a matching leaf-last `certificates` bundle;
48
+ `ski` sets an explicit CA issuer identifier, and CA certificate SKI is extracted
49
+ when available. These are multiple card writes, not an atomic transaction;
50
+ do not retry automatically after any failure.
51
+
52
+ `keys generate` and `keys export` return PEM bytes rather than writing files.
53
+ An optional trailing `-` is only a compatibility marker. Export never extracts
54
+ private keys. Use `certificates:` and `ski:` for certificate/issuer storage;
55
+ allowlist serials may be argv integers or the `serials:` Array. Mutations require
56
+ `confirm: true` and a genuine SCP03/SCP11a wrapper. `--force` is not consent.
57
+ Reset is deliberately plaintext-only, follows the documented bounded key-blocking
58
+ sequence, and reads back the default-key state before reporting success.
59
+
60
+ Curves are prime256v1, secp384r1, secp521r1, brainpoolP256r1,
61
+ brainpoolP384r1 and brainpoolP512r1. P-521 private-key import is explicitly
62
+ unsupported because its 66-byte scalar is not block-aligned for the pinned SDK's
63
+ un-padded CBC PUT KEY encoding; key generation and public-key handling work.
64
+ All actual card curve/capacity support remains firmware-dependent.
65
+
66
+ ## Evidence boundary
67
+
68
+ Wire behavior is based on GlobalPlatform Card Specification 2.3 Amendments D
69
+ (SCP03 S8/AES128) and F v1.4 (SCP11), and Yubico's official yubikit 5.9.1
70
+ `securitydomain.py` and `core/smartcard/scp.py`. These are references, not runtime
71
+ dependencies. NIST SP 800-38B/RFC 4493 vectors and independent synthetic APDU
72
+ peers validate the host cryptography offline; no physical device compatibility
73
+ is claimed. No SCP03 S16, key diversification, CRL/OCSP retrieval, or guaranteed
74
+ Ruby/OpenSSL memory zeroization is provided. Unauthenticated post-handshake
75
+ status-only replies fail closed rather than releasing an unverified status.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: pwn-plugins-yubikey-security-domain-scp
3
+ description: Use the native SCP03 and SCP11 APDU channel wrapper.
4
+ license: MIT
5
+ allowed-tools: [pwn, pwn_eval]
6
+ metadata:
7
+ bundled: true
8
+ preserve: true
9
+ module: PWN::Plugins::YubiKey::SecurityDomain::SCP
10
+ source: pwn/plugins/yubikey/security_domain/scp.rb
11
+ ---
12
+
13
+ # SCP channel implementation
14
+
15
+ Prefer `SecurityDomain.secure_connection` to direct `SCP.open`. Both require
16
+ explicit keys and literal confirmation; see the parent SecurityDomain skill
17
+ for in-memory key and certificate options. Public-key pins and certificates are
18
+ explicit trust decisions: do not discover a card key and silently pin it. Pass
19
+ unencrypted PEM/DER or already-decrypted OpenSSL key objects; this module never
20
+ prompts a terminal for encrypted-PEM passwords. This module never discovers devices
21
+ or spawns executables. Returned APDU wrappers retain verified status words,
22
+ serialize counter/MAC updates and invalidate themselves after any uncertain
23
+ exchange or authentication failure. Do not reuse an underlying connection after
24
+ handshake failure, and never replace a failed channel with plaintext I/O.
25
+
26
+ SCP03 supports YubiKey S8/AES128: derives S-ENC/S-MAC/S-RMAC, verifies the full
27
+ card cryptogram, authenticates the host at security level 0x33, uses CBC with
28
+ counter-derived IVs and ISO7816 padding, and verifies R-MAC before decryption.
29
+ SCP11a/b/c use native OpenSSL EC agreement, SHA256 X9.63 KDF and receipt CMAC;
30
+ certificate trust is explicit, not silently bypassed. CA validation is offline
31
+ and does not retrieve CRLs/OCSP. No guaranteed memory erasure is claimed.
32
+
33
+ The descriptor advertises `extended_apdu: true`. The shared Protocol layer must
34
+ honor this before command chaining: upstream SCP wraps the whole command before
35
+ transport fragmentation, not each plaintext chunk. The wrapper accepts short and
36
+ extended input and upgrades encrypted payloads over 255 bytes to extended frames.
37
+ Transport `61xx` fragments are collected with raw GET RESPONSE before one R-MAC
38
+ verification, preserving the counter/MAC chain. This is continuation retrieval,
39
+ never authentication failure fallback. Fragment count, total response size and
40
+ elapsed time are bounded. Noncanonical receipt encodings and padding longer than
41
+ one AES block fail closed.
42
+
43
+ References: GlobalPlatform Card Specification 2.3 Amendments D/F; official
44
+ Yubico yubikit 5.9.1 core/smartcard/scp.py and smartcard/__init__.py. Tests use
45
+ RFC 4493 known vectors and independent synthetic card-side cryptographic
46
+ transcripts, not implementation-produced fixture outputs or real secrets.
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: pwn-plugins-yubikey-transport
3
+ description: Use native HIDAPI and PC/SC YubiKey transports.
4
+ license: MIT
5
+ allowed-tools: [pwn, pwn_eval]
6
+ metadata:
7
+ bundled: true
8
+ preserve: true
9
+ module: PWN::Plugins::YubiKey::Transport
10
+ source: pwn/plugins/yubikey/transport.rb
11
+ ---
12
+
13
+ # Native YubiKey transport
14
+
15
+ ## Procedure
16
+
17
+ 1. `Transport.available?(application: :piv)` probes **host shared libraries only**.
18
+ Optional `backend: :hid` or `:pcsc` narrows the probe. Requiring the module and
19
+ checking availability never initialize HIDAPI, enumerate devices, establish a
20
+ PC/SC context, or open hardware. Availability does not prove permission,
21
+ service health, card presence, or application support.
22
+ 2. `Transport.devices(timeout: 5)` explicitly enumerates HID interfaces and
23
+ PC/SC readers. Reader descriptors do not prove that a card is inserted.
24
+ `backend:`, exact `serial:`, `reader:` and `device_path:` filter inventory.
25
+ This inventory call does not connect to cards to obtain their serials.
26
+ 3. `Transport.open(application: :otp, serial: '0007', timeout: 30)` selects one
27
+ endpoint or raises on absent/ambiguous selection. Preserve the operator's
28
+ exact serial String; never normalize, trim or parse it as an Integer.
29
+ Use `device_path:` or `reader:` for serial-less Security Key models. A reader
30
+ explicitly selects PC/SC, including OTP/management smartcard variants. Generic
31
+ NFC readers require explicit selection. Default OTP/FIDO prefers matching HID;
32
+ other applications prefer PC/SC. PC/SC ownership is exclusive, with no shared
33
+ fallback, process killing, automatic authentication or command replay.
34
+ 4. Call `connection.exchange(opts)` and always close in `ensure`. The descriptor's
35
+ `:protocol` is authoritative:
36
+ - `:otp`: `command:` byte, binary `data:` up to 64 bytes, optional
37
+ `response_length:`. CRC is checked and removed; status writes require a
38
+ programming-sequence acknowledgment. Command `0` with empty data reads six
39
+ status bytes without writing a command. Supply expected data lengths whenever
40
+ known; variable-length replies require an unambiguous CRC/padding boundary.
41
+ - `:ctap`: `command:` in 1..127, excluding internal INIT/CANCEL/KEEPALIVE/ERROR,
42
+ binary `data:` up to 7609 bytes. `open` performs nonce-checked CTAPHID INIT and
43
+ exposes `:version` and `:capabilities`. Responses retain the CTAP status byte.
44
+ - `:apdu`: binary `data:` containing the complete C-APDU. The result is the
45
+ complete R-APDU **including SW1/SW2**; shared `Protocol.apdu` owns chaining,
46
+ GET RESPONSE and application-status validation.
47
+ 5. `timeout:` is a finite positive monotonic budget. Optional `cancelled:` is a
48
+ callable returning truthy to stop; `on_keepalive:` receives 1 (processing) or
49
+ 2 (touch/user presence). Callbacks must be bounded and thread-safe. They run
50
+ on the connection's owner worker. Never log packet bodies or secret replies.
51
+
52
+ ## Lifecycle and limits
53
+
54
+ - A transport error, interrupted exchange or deadline retires the connection.
55
+ CTAPHID CANCEL / OTP reset is sent serially after the current native call.
56
+ Application-error status bytes are returned unchanged and do not retire it.
57
+ Do not expect subsequent application cleanup on a retired connection to work.
58
+ - `close` is idempotent. `:closed` means native cleanup completed; `:closing`
59
+ means it is deferred. Timed-out C calls retain their owned thread, buffers and
60
+ handle until the native call returns. Never kill that Ruby thread or close its
61
+ in-flight handle: this would risk a use-after-free and is not native cancellation.
62
+ - HID interrupt reads use `hid_read_timeout`. HID feature/write calls and PC/SC
63
+ `SCardTransmit` have no portable hard deadline. `SCardCancel` does not universally
64
+ cancel transmit. A permanently stuck driver may retain a thread and native
65
+ resources indefinitely; a bounded Ruby waiter is not driver-independent abort.
66
+ - Explicit serial selection may perform read-only OTP identity APDUs, with a
67
+ management-information fallback using shared Protocol codecs when OTP is
68
+ disabled. Unobservable identities fail closed; no serial is invented.
69
+ - PC/SC `:transport` derives from the Yubico ATR USB/NFC distinction; it is not
70
+ cryptographic device/vendor attestation. HID transport is USB. NEO's historic
71
+ cached-programming-sequence workaround is not issued automatically; uncertain
72
+ acknowledgments fail rather than trigger an implicit write or retry.
73
+
74
+ ## Offline verification
75
+
76
+ Run `bundle exec rspec spec/lib/pwn/plugins/yubikey/transport_spec.rb` and focused
77
+ RuboCop on the paired source/spec. Default tests use fake native C-library/IO
78
+ boundaries with independent fixed CTAPHID and OTP CRC/framing fixtures. They
79
+ exercise ordering, bounds, exact selectors, platform ABI widths/wide strings,
80
+ blocked-call ownership, cancellation, and native cleanup without real hardware.
81
+ Linux library-symbol load checks are separate from physical interoperability;
82
+ Windows/macOS ABI simulations are not tests executed on those operating systems.
83
+
84
+ ## Protocol provenance
85
+
86
+ This is a native Ruby implementation, not a launcher or a vendored SDK runtime.
87
+ Protocol references:
88
+
89
+ - YubiKey Manager 5.9.1 `yubikit/core/otp.py`, `yubikit/yubiotp.py`, and
90
+ `ykman/pcsc/__init__.py` (read-only reference).
91
+ - python-fido2 2.2.1 `fido2/hid/__init__.py` (read-only reference), and the
92
+ [FIDO HID protocol](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-hid).
93
+ - [HIDAPI 0.14.0 C header](https://github.com/libusb/hidapi/blob/hidapi-0.14.0/hidapi/hidapi.h).
94
+ - [PC/SC ABI definitions](https://github.com/LudovicRousseau/PCSC/blob/master/src/PCSC/wintypes.h):
95
+ Unix `DWORD=unsigned long`, Darwin `DWORD=uint32`, Windows handles pointer-sized;
96
+ exported PCI symbols avoid guessed structure packing.
97
+ - [Microsoft SCardCancel](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardcancel)
98
+ and [SCardTransmit](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardtransmit).
@@ -6,13 +6,14 @@ allowed-tools: [pwn, pwn_eval]
6
6
  metadata:
7
7
  bundled: true
8
8
  generated: true
9
+ preserve: true
9
10
  module: PWN::Redaction
10
11
  source: pwn/redaction.rb
11
12
  ---
12
13
 
13
14
  # PWN::Redaction
14
15
 
15
- Write-boundary redaction; reject cyclic or over-100-level containers before inspecting secrets. Never opens a credential store.
16
+ Write-boundary redaction; reject cyclic or over-100-level containers before inspecting secrets. `redact` never opens a credential store; `capture` can explicitly store ordinary secret matches.
16
17
 
17
18
  ## When to use
18
19
 
@@ -26,6 +27,17 @@ Class methods take `(opts = {})` and read `opts`.
26
27
 
27
28
  ## How to call
28
29
 
30
+ PINs, PUKs, MFA/OTP values, access codes, management keys, OATH secrets, static
31
+ passwords and SCP key fields use the constant `[REDACTED]` mask in hashes and
32
+ key-value text. Do not replace those masks with hash markers: low-entropy values
33
+ can be brute-forced from unsalted digests. `capture` masks these fields without
34
+ storing them. Ordinary API/password tokens retain the existing digest behavior.
35
+
36
+ For other captured loot, pass `yubikey: {slot: 2, serial: '12345678', timeout: 30}`
37
+ to select hardware encryption. Hardware capture failure must leave the returned
38
+ text redacted and must never trigger software fallback. A returned capture
39
+ reference is not proof of successful persistence; verify metadata separately.
40
+
29
41
  ```ruby
30
42
  PWN::Redaction.help
31
43
  PWN::Redaction.redact(opts)
@@ -72,11 +72,14 @@ module PWN
72
72
  violation = JSONSchemer.schema(JSON.parse(JSON.generate(type_schema))).validate(JSON.parse(JSON.generate(args))).first
73
73
  return schema_denial(violation.transform_keys(&:to_sym)) if violation
74
74
 
75
- blob = args.inspect
76
75
  if defined?(PWN::Plugins::Vault)
77
- blob = PWN::Plugins::Vault.expand(text: blob)
78
- args = expand_vault_args(args: args)
76
+ begin
77
+ args = expand_vault_args(args: args)
78
+ rescue StandardError
79
+ return JSON.generate(success: false, code: 'VAULT_EXPANSION_FAILED', error: 'Vault access failed; check the selected device and retry. The tool was not executed.')
80
+ end
79
81
  end
82
+ blob = args.inspect
80
83
  denied = Manifest.check(opts.merge(name: entry.name, args: args))
81
84
  return JSON.generate(denied) if denied
82
85
 
@@ -129,7 +132,11 @@ module PWN
129
132
  begin
130
133
  runtime = Thread.current[:pwn_request_runtime]
131
134
  result = if runtime
132
- runtime.execute_tool(name: entry.name, args: args, effect: effect(name: entry.name, args: args), action_id: tool_call[:id] || tool_call['id']) { entry.handler.call(args) }
135
+ runtime.execute_tool(name: entry.name, args: args, effect: effect(name: entry.name, args: args), action_id: tool_call[:id] || tool_call['id']) do
136
+ PWN::Plugins::AISandbox.protect_call(handler: { name: entry.name, args: args }, runtime: runtime, workspace: runtime.contract[:cwd], write_roots: runtime.contract[:required_artifacts]) do
137
+ entry.handler.call(args)
138
+ end
139
+ end
133
140
  else
134
141
  entry.handler.call(args)
135
142
  end
@@ -141,13 +148,18 @@ module PWN
141
148
  result = Result.page(value: result, entry: entry, session_id: opts[:session_id])
142
149
  result = ToolGuard.quarantine_output(text: result) if defined?(ToolGuard) && result.is_a?(String) && ToolGuard.respond_to?(:quarantine_output)
143
150
  note_taint(text: result)
144
- if defined?(PWN::Plugins::Vault) && result.is_a?(String)
145
- result = PWN::Plugins::Vault.redact(text: result)
146
- elsif defined?(PWN::Plugins::Vault) && result.is_a?(Hash)
147
- result = JSON.parse(PWN::Plugins::Vault.redact(text: JSON.generate(Result.utf8_value(value: result))))
151
+ begin
152
+ if defined?(PWN::Plugins::Vault) && result.is_a?(String)
153
+ result = PWN::Plugins::Vault.redact(text: result)
154
+ elsif defined?(PWN::Plugins::Vault) && (result.is_a?(Hash) || result.is_a?(Array))
155
+ result = JSON.parse(PWN::Plugins::Vault.redact(text: JSON.generate(Result.utf8_value(value: result))))
156
+ end
157
+ rescue StandardError
158
+ return JSON.generate(success: false, code: 'VAULT_REDACTION_FAILED', handler_executed: true,
159
+ error: 'Vault redaction failed; result withheld. The tool already executed; verify its effects before retrying.')
148
160
  end
149
161
  response = { success: true, result: result, effect: effect(name: entry.name, args: args) }
150
- response[:success] = false if result.is_a?(Hash) && (result[:error] || result['error'] || result[:success] == false || result['success'] == false)
162
+ response[:success] = false if result.is_a?(Hash) && (result[:error] || result['error'] || result[:success] == false || result['success'] == false || result[:ok] == false || result['ok'] == false || (result.key?(:exit) && result[:exit] != 0) || (result.key?('exit') && result['exit'] != 0))
151
163
  response[:budget] = telemetry if telemetry
152
164
  if telemetry && telemetry[:remaining_s].zero?
153
165
  response[:success] = false
@@ -591,13 +603,12 @@ module PWN
591
603
 
592
604
  private_class_method def self.expand_vault_args(opts = {})
593
605
  args = opts[:args]
594
- return args unless args.is_a?(Hash)
595
-
596
- args.transform_values do |v|
597
- v.is_a?(String) ? PWN::Plugins::Vault.expand(text: v) : v
606
+ case args
607
+ when Hash then args.transform_values { |value| expand_vault_args(args: value) }
608
+ when Array then args.map { |value| expand_vault_args(args: value) }
609
+ when String then PWN::Plugins::Vault.expand(text: args)
610
+ else args
598
611
  end
599
- rescue StandardError
600
- opts[:args]
601
612
  end
602
613
 
603
614
  private_class_method def self.note_taint(opts = {})
@@ -3572,16 +3572,15 @@ module PWN
3572
3572
  [true, log]
3573
3573
  when :festival
3574
3574
  # Use plugin if text file available
3575
- tmp = File.join(Dir.tmpdir, "pwn_tts_#{Process.pid}.txt")
3576
- File.write(tmp, text)
3575
+ require_relative '../../../../etc/default_cron/session_cleanup' unless defined?(PWN::SessionCleanup)
3577
3576
  begin
3578
- require 'pwn/plugins/voice' unless defined?(PWN::Plugins::Voice)
3579
- PWN::Plugins::Voice.text_to_speech(text_path: tmp)
3580
- [true, 'festival via PWN::Plugins::Voice']
3577
+ PWN::SessionCleanup.temporary(bytes: text) do |tmp|
3578
+ require 'pwn/plugins/voice' unless defined?(PWN::Plugins::Voice)
3579
+ PWN::Plugins::Voice.text_to_speech(text_path: tmp)
3580
+ [true, 'festival via PWN::Plugins::Voice']
3581
+ end
3581
3582
  rescue StandardError => e
3582
3583
  [false, "#{e.class}: #{e.message}"]
3583
- ensure
3584
- FileUtils.rm_f(tmp)
3585
3584
  end
3586
3585
  else
3587
3586
  [false, "unsupported TTS engine: #{engine}"]