@vigolium/piolium 0.0.1

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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/agents/access-auditor.md +300 -0
  4. package/agents/assumption-breaker.md +154 -0
  5. package/agents/attack-designer.md +116 -0
  6. package/agents/code-scanner.md +139 -0
  7. package/agents/concurrency-auditor.md +238 -0
  8. package/agents/confirm-writer.md +257 -0
  9. package/agents/context-reviewer.md +274 -0
  10. package/agents/cross-verifier.md +165 -0
  11. package/agents/cve-scout.md +381 -0
  12. package/agents/env-builder.md +282 -0
  13. package/agents/env-profiler.md +205 -0
  14. package/agents/evidence-collector.md +140 -0
  15. package/agents/finding-grader.md +142 -0
  16. package/agents/finding-writer.md +148 -0
  17. package/agents/flow-tracer.md +106 -0
  18. package/agents/goal-backtracer.md +146 -0
  19. package/agents/history-miner.md +467 -0
  20. package/agents/independent-verifier.md +118 -0
  21. package/agents/intent-mapper.md +183 -0
  22. package/agents/longshot-collector.md +128 -0
  23. package/agents/longshot-prober.md +126 -0
  24. package/agents/patch-auditor.md +73 -0
  25. package/agents/poc-author.md +124 -0
  26. package/agents/poc-runner.md +194 -0
  27. package/agents/probe-lead.md +269 -0
  28. package/agents/red-challenger.md +101 -0
  29. package/agents/report-composer.md +208 -0
  30. package/agents/review-adjudicator.md +216 -0
  31. package/agents/spec-auditor.md +155 -0
  32. package/agents/taint-tracer.md +265 -0
  33. package/agents/test-locator.md +209 -0
  34. package/agents/threat-modeler.md +132 -0
  35. package/agents/variant-scanner.md +108 -0
  36. package/agents/variant-spotter.md +110 -0
  37. package/bin/piolium.mjs +376 -0
  38. package/extensions/piolium/_vendor/yaml.bundle.d.mts +6 -0
  39. package/extensions/piolium/_vendor/yaml.bundle.mjs +139 -0
  40. package/extensions/piolium/agent-runner.ts +322 -0
  41. package/extensions/piolium/agents.ts +266 -0
  42. package/extensions/piolium/audit-state.ts +522 -0
  43. package/extensions/piolium/bundled-resources.ts +97 -0
  44. package/extensions/piolium/candidate-scan.ts +966 -0
  45. package/extensions/piolium/command-target.ts +177 -0
  46. package/extensions/piolium/console-stream.ts +57 -0
  47. package/extensions/piolium/export-results.ts +380 -0
  48. package/extensions/piolium/findings.ts +448 -0
  49. package/extensions/piolium/heartbeat.ts +182 -0
  50. package/extensions/piolium/help.ts +234 -0
  51. package/extensions/piolium/index.ts +1865 -0
  52. package/extensions/piolium/longshot.ts +530 -0
  53. package/extensions/piolium/matcher-suggestions.ts +196 -0
  54. package/extensions/piolium/matcher-utils.ts +83 -0
  55. package/extensions/piolium/modes/balanced.ts +750 -0
  56. package/extensions/piolium/modes/confirm-bootstrap.ts +186 -0
  57. package/extensions/piolium/modes/confirm.ts +697 -0
  58. package/extensions/piolium/modes/deep.ts +917 -0
  59. package/extensions/piolium/modes/diff.ts +177 -0
  60. package/extensions/piolium/modes/lite.ts +540 -0
  61. package/extensions/piolium/modes/longshot.ts +595 -0
  62. package/extensions/piolium/modes/merge.ts +204 -0
  63. package/extensions/piolium/modes/phase-runner.ts +267 -0
  64. package/extensions/piolium/modes/reinvest.ts +546 -0
  65. package/extensions/piolium/modes/revisit.ts +279 -0
  66. package/extensions/piolium/modes.ts +48 -0
  67. package/extensions/piolium/phase-labels.ts +123 -0
  68. package/extensions/piolium/phase-status-strip.ts +92 -0
  69. package/extensions/piolium/prompt-prefix-editor.ts +39 -0
  70. package/extensions/piolium/providers/anthropic-vertex.ts +836 -0
  71. package/extensions/piolium/recon.ts +409 -0
  72. package/extensions/piolium/result-stats.ts +105 -0
  73. package/extensions/piolium/retry.ts +120 -0
  74. package/extensions/piolium/scheduler.ts +212 -0
  75. package/extensions/piolium/secrets.ts +368 -0
  76. package/extensions/piolium/tools/web-tools.ts +148 -0
  77. package/package.json +77 -0
  78. package/skills/agentic-actions-auditor/SKILL.md +327 -0
  79. package/skills/agentic-actions-auditor/references/action-profiles.md +186 -0
  80. package/skills/agentic-actions-auditor/references/cross-file-resolution.md +209 -0
  81. package/skills/agentic-actions-auditor/references/foundations.md +94 -0
  82. package/skills/agentic-actions-auditor/references/vector-a-env-var-intermediary.md +77 -0
  83. package/skills/agentic-actions-auditor/references/vector-b-direct-expression-injection.md +83 -0
  84. package/skills/agentic-actions-auditor/references/vector-c-cli-data-fetch.md +83 -0
  85. package/skills/agentic-actions-auditor/references/vector-d-pr-target-checkout.md +88 -0
  86. package/skills/agentic-actions-auditor/references/vector-e-error-log-injection.md +88 -0
  87. package/skills/agentic-actions-auditor/references/vector-f-subshell-expansion.md +82 -0
  88. package/skills/agentic-actions-auditor/references/vector-g-eval-of-ai-output.md +91 -0
  89. package/skills/agentic-actions-auditor/references/vector-h-dangerous-sandbox-configs.md +102 -0
  90. package/skills/agentic-actions-auditor/references/vector-i-wildcard-allowlists.md +88 -0
  91. package/skills/audit/SKILL.md +562 -0
  92. package/skills/audit/assets/icon.svg +7 -0
  93. package/skills/audit/hooks/scripts/validate_phase_output.py +550 -0
  94. package/skills/audit/references/adversarial-review.md +148 -0
  95. package/skills/audit/references/architecture-aware-sast.md +306 -0
  96. package/skills/audit/references/audit-workflow.md +737 -0
  97. package/skills/audit/references/chamber-protocol.md +384 -0
  98. package/skills/audit/references/creative-attack-modes.md +221 -0
  99. package/skills/audit/references/deep-analysis.md +273 -0
  100. package/skills/audit/references/domain-attack-playbooks.md +1129 -0
  101. package/skills/audit/references/knowledge-base-template.md +513 -0
  102. package/skills/audit/references/real-env-validation.md +191 -0
  103. package/skills/audit/references/report-templates.md +417 -0
  104. package/skills/audit/references/triage-and-prereqs.md +134 -0
  105. package/skills/audit/scripts/consolidate_drafts.py +554 -0
  106. package/skills/audit/scripts/partition_findings.py +152 -0
  107. package/skills/audit/scripts/rg-hotspots.sh +121 -0
  108. package/skills/audit/scripts/stamp_file_state.py +349 -0
  109. package/skills/code-reviewer/SKILL.md +65 -0
  110. package/skills/codeql/SKILL.md +281 -0
  111. package/skills/codeql/references/build-fixes.md +90 -0
  112. package/skills/codeql/references/diagnostic-query-templates.md +339 -0
  113. package/skills/codeql/references/extension-yaml-format.md +209 -0
  114. package/skills/codeql/references/important-only-suite.md +153 -0
  115. package/skills/codeql/references/language-details.md +207 -0
  116. package/skills/codeql/references/macos-arm64e-workaround.md +179 -0
  117. package/skills/codeql/references/performance-tuning.md +111 -0
  118. package/skills/codeql/references/quality-assessment.md +172 -0
  119. package/skills/codeql/references/ruleset-catalog.md +63 -0
  120. package/skills/codeql/references/run-all-suite.md +92 -0
  121. package/skills/codeql/references/sarif-processing.md +79 -0
  122. package/skills/codeql/references/threat-models.md +51 -0
  123. package/skills/codeql/workflows/build-database.md +280 -0
  124. package/skills/codeql/workflows/create-data-extensions.md +261 -0
  125. package/skills/codeql/workflows/run-analysis.md +301 -0
  126. package/skills/differential-review/SKILL.md +220 -0
  127. package/skills/differential-review/adversarial.md +203 -0
  128. package/skills/differential-review/methodology.md +234 -0
  129. package/skills/differential-review/patterns.md +300 -0
  130. package/skills/differential-review/reporting.md +369 -0
  131. package/skills/fp-check/SKILL.md +125 -0
  132. package/skills/fp-check/references/bug-class-verification.md +114 -0
  133. package/skills/fp-check/references/deep-verification.md +143 -0
  134. package/skills/fp-check/references/evidence-templates.md +91 -0
  135. package/skills/fp-check/references/false-positive-patterns.md +115 -0
  136. package/skills/fp-check/references/gate-reviews.md +27 -0
  137. package/skills/fp-check/references/standard-verification.md +78 -0
  138. package/skills/insecure-defaults/SKILL.md +117 -0
  139. package/skills/insecure-defaults/references/examples.md +409 -0
  140. package/skills/last30days/SKILL.md +444 -0
  141. package/skills/sarif-parsing/SKILL.md +483 -0
  142. package/skills/sarif-parsing/resources/jq-queries.md +162 -0
  143. package/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
  144. package/skills/security-threat-model/LICENSE.txt +201 -0
  145. package/skills/security-threat-model/SKILL.md +81 -0
  146. package/skills/security-threat-model/agents/openai.yaml +4 -0
  147. package/skills/security-threat-model/references/prompt-template.md +255 -0
  148. package/skills/security-threat-model/references/security-controls-and-assets.md +32 -0
  149. package/skills/semgrep/SKILL.md +212 -0
  150. package/skills/semgrep/references/rulesets.md +162 -0
  151. package/skills/semgrep/references/scan-modes.md +110 -0
  152. package/skills/semgrep/references/scanner-task-prompt.md +140 -0
  153. package/skills/semgrep/scripts/merge_sarif.py +203 -0
  154. package/skills/semgrep/workflows/scan-workflow.md +311 -0
  155. package/skills/semgrep-rule-creator/SKILL.md +168 -0
  156. package/skills/semgrep-rule-creator/references/quick-reference.md +202 -0
  157. package/skills/semgrep-rule-creator/references/workflow.md +240 -0
  158. package/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
  159. package/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
  160. package/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
  161. package/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
  162. package/skills/sharp-edges/SKILL.md +292 -0
  163. package/skills/sharp-edges/references/auth-patterns.md +252 -0
  164. package/skills/sharp-edges/references/case-studies.md +274 -0
  165. package/skills/sharp-edges/references/config-patterns.md +333 -0
  166. package/skills/sharp-edges/references/crypto-apis.md +190 -0
  167. package/skills/sharp-edges/references/lang-c.md +205 -0
  168. package/skills/sharp-edges/references/lang-csharp.md +285 -0
  169. package/skills/sharp-edges/references/lang-go.md +270 -0
  170. package/skills/sharp-edges/references/lang-java.md +263 -0
  171. package/skills/sharp-edges/references/lang-javascript.md +269 -0
  172. package/skills/sharp-edges/references/lang-kotlin.md +265 -0
  173. package/skills/sharp-edges/references/lang-php.md +245 -0
  174. package/skills/sharp-edges/references/lang-python.md +274 -0
  175. package/skills/sharp-edges/references/lang-ruby.md +273 -0
  176. package/skills/sharp-edges/references/lang-rust.md +272 -0
  177. package/skills/sharp-edges/references/lang-swift.md +287 -0
  178. package/skills/sharp-edges/references/language-specific.md +588 -0
  179. package/skills/spec-to-code-compliance/SKILL.md +357 -0
  180. package/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
  181. package/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
  182. package/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
  183. package/skills/supply-chain-risk-auditor/SKILL.md +67 -0
  184. package/skills/supply-chain-risk-auditor/resources/results-template.md +41 -0
  185. package/skills/variant-analysis/METHODOLOGY.md +327 -0
  186. package/skills/variant-analysis/SKILL.md +142 -0
  187. package/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
  188. package/skills/variant-analysis/resources/codeql/go.ql +69 -0
  189. package/skills/variant-analysis/resources/codeql/java.ql +71 -0
  190. package/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
  191. package/skills/variant-analysis/resources/codeql/python.ql +80 -0
  192. package/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
  193. package/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
  194. package/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
  195. package/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
  196. package/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
  197. package/skills/variant-analysis/resources/variant-report-template.md +75 -0
  198. package/skills/vuln-report/SKILL.md +137 -0
  199. package/skills/vuln-report/agents/openai.yaml +4 -0
  200. package/skills/vuln-report/references/report-template.md +135 -0
  201. package/skills/wooyun-legacy/SKILL.md +367 -0
  202. package/skills/wooyun-legacy/references/bank-penetration.md +222 -0
  203. package/skills/wooyun-legacy/references/checklists/command-execution-checklist.md +119 -0
  204. package/skills/wooyun-legacy/references/checklists/csrf-checklist.md +74 -0
  205. package/skills/wooyun-legacy/references/checklists/file-upload-checklist.md +108 -0
  206. package/skills/wooyun-legacy/references/checklists/info-disclosure-checklist.md +114 -0
  207. package/skills/wooyun-legacy/references/checklists/logic-flaws-checklist.md +95 -0
  208. package/skills/wooyun-legacy/references/checklists/misconfig-checklist.md +124 -0
  209. package/skills/wooyun-legacy/references/checklists/path-traversal-checklist.md +87 -0
  210. package/skills/wooyun-legacy/references/checklists/rce-checklist.md +93 -0
  211. package/skills/wooyun-legacy/references/checklists/sql-injection-checklist.md +97 -0
  212. package/skills/wooyun-legacy/references/checklists/ssrf-checklist.md +99 -0
  213. package/skills/wooyun-legacy/references/checklists/unauthorized-access-checklist.md +89 -0
  214. package/skills/wooyun-legacy/references/checklists/weak-password-checklist.md +115 -0
  215. package/skills/wooyun-legacy/references/checklists/xss-checklist.md +103 -0
  216. package/skills/wooyun-legacy/references/checklists/xxe-checklist.md +130 -0
  217. package/skills/wooyun-legacy/references/info-disclosure.md +975 -0
  218. package/skills/wooyun-legacy/references/logic-flaws.md +721 -0
  219. package/skills/wooyun-legacy/references/path-traversal.md +1191 -0
  220. package/skills/wooyun-legacy/references/telecom-penetration.md +156 -0
  221. package/skills/wooyun-legacy/references/unauthorized-access.md +980 -0
  222. package/skills/wooyun-legacy/references/xss.md +746 -0
  223. package/skills/zeroize-audit/SKILL.md +371 -0
  224. package/skills/zeroize-audit/configs/c.yaml +21 -0
  225. package/skills/zeroize-audit/configs/default.yaml +128 -0
  226. package/skills/zeroize-audit/configs/rust.yaml +83 -0
  227. package/skills/zeroize-audit/prompts/report_template.md +238 -0
  228. package/skills/zeroize-audit/prompts/system.md +163 -0
  229. package/skills/zeroize-audit/prompts/task.md +97 -0
  230. package/skills/zeroize-audit/references/compile-commands.md +231 -0
  231. package/skills/zeroize-audit/references/detection-strategy.md +191 -0
  232. package/skills/zeroize-audit/references/ir-analysis.md +252 -0
  233. package/skills/zeroize-audit/references/mcp-analysis.md +221 -0
  234. package/skills/zeroize-audit/references/poc-generation.md +470 -0
  235. package/skills/zeroize-audit/references/rust-zeroization-patterns.md +867 -0
  236. package/skills/zeroize-audit/schemas/input.json +83 -0
  237. package/skills/zeroize-audit/schemas/output.json +140 -0
  238. package/skills/zeroize-audit/tools/analyze_asm.sh +202 -0
  239. package/skills/zeroize-audit/tools/analyze_cfg.py +381 -0
  240. package/skills/zeroize-audit/tools/analyze_heap.sh +211 -0
  241. package/skills/zeroize-audit/tools/analyze_ir_semantic.py +429 -0
  242. package/skills/zeroize-audit/tools/diff_ir.sh +135 -0
  243. package/skills/zeroize-audit/tools/diff_rust_mir.sh +189 -0
  244. package/skills/zeroize-audit/tools/emit_asm.sh +67 -0
  245. package/skills/zeroize-audit/tools/emit_ir.sh +77 -0
  246. package/skills/zeroize-audit/tools/emit_rust_asm.sh +178 -0
  247. package/skills/zeroize-audit/tools/emit_rust_ir.sh +150 -0
  248. package/skills/zeroize-audit/tools/emit_rust_mir.sh +158 -0
  249. package/skills/zeroize-audit/tools/extract_compile_flags.py +284 -0
  250. package/skills/zeroize-audit/tools/generate_poc.py +1329 -0
  251. package/skills/zeroize-audit/tools/mcp/apply_confidence_gates.py +113 -0
  252. package/skills/zeroize-audit/tools/mcp/check_mcp.sh +68 -0
  253. package/skills/zeroize-audit/tools/mcp/normalize_mcp_evidence.py +125 -0
  254. package/skills/zeroize-audit/tools/scripts/check_llvm_patterns.py +481 -0
  255. package/skills/zeroize-audit/tools/scripts/check_mir_patterns.py +554 -0
  256. package/skills/zeroize-audit/tools/scripts/check_rust_asm.py +424 -0
  257. package/skills/zeroize-audit/tools/scripts/check_rust_asm_aarch64.py +300 -0
  258. package/skills/zeroize-audit/tools/scripts/check_rust_asm_x86.py +283 -0
  259. package/skills/zeroize-audit/tools/scripts/find_dangerous_apis.py +375 -0
  260. package/skills/zeroize-audit/tools/scripts/semantic_audit.py +923 -0
  261. package/skills/zeroize-audit/tools/track_dataflow.sh +196 -0
  262. package/skills/zeroize-audit/tools/validate_rust_toolchain.sh +298 -0
  263. package/skills/zeroize-audit/workflows/phase-0-preflight.md +150 -0
  264. package/skills/zeroize-audit/workflows/phase-1-source-analysis.md +144 -0
  265. package/skills/zeroize-audit/workflows/phase-2-compiler-analysis.md +139 -0
  266. package/skills/zeroize-audit/workflows/phase-3-interim-report.md +46 -0
  267. package/skills/zeroize-audit/workflows/phase-4-poc-generation.md +46 -0
  268. package/skills/zeroize-audit/workflows/phase-5-poc-validation.md +136 -0
  269. package/skills/zeroize-audit/workflows/phase-6-final-report.md +44 -0
  270. package/skills/zeroize-audit/workflows/phase-7-test-generation.md +42 -0
  271. package/themes/piolium-srcery.json +94 -0
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = []
5
+ # ///
6
+ """
7
+ find_dangerous_apis.py — Token/grep-based scanner for dangerous Rust API patterns.
8
+
9
+ Scans .rs files for API calls that bypass zeroization guarantees (mem::forget,
10
+ Box::leak, ptr::write_bytes, etc.) and async suspension points that expose
11
+ secret-named locals to the heap-allocated Future state machine.
12
+
13
+ Does NOT require compilation — pure source text analysis.
14
+
15
+ Usage:
16
+ uv run find_dangerous_apis.py --src <source_dir> --out <findings.json>
17
+
18
+ Exit codes:
19
+ 0 — ran successfully (findings may be empty)
20
+ 1 — source directory not found
21
+ 2 — argument error
22
+ """
23
+
24
+ import argparse
25
+ import json
26
+ import re
27
+ import sys
28
+ from pathlib import Path
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Sensitive name patterns (used for context filtering)
32
+ # ---------------------------------------------------------------------------
33
+
34
+ SENSITIVE_NAME_RE = re.compile(
35
+ # PascalCase type names use \b (no underscore in names like SecretKey).
36
+ # Lowercase keywords use (?<![a-zA-Z])...(?![a-zA-Z]) so that snake_case
37
+ # names like 'secret_key', 'private_key', and 'auth_token' are matched
38
+ # while avoiding spurious hits on words like 'monkey' or 'tokenize'.
39
+ r"(?i)(?:\b(Key|PrivateKey|SecretKey|SigningKey|MasterKey|HmacKey|"
40
+ r"Password|Passphrase|Pin|Token|AuthToken|BearerToken|ApiKey|"
41
+ r"Secret|SharedSecret|PreSharedKey|Nonce|Seed|Entropy|"
42
+ r"Credential|SessionKey|DerivedKey)\b"
43
+ r"|(?<![a-zA-Z])(key|secret|password|token|nonce|seed|private|master|credential)(?![a-zA-Z]))"
44
+ )
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Dangerous API patterns: (regex, category, severity, detail)
48
+ # ---------------------------------------------------------------------------
49
+
50
+ PATTERNS: list[tuple[str, str, str, str]] = [
51
+ (
52
+ r"\bmem::forget\s*\(",
53
+ "MISSING_SOURCE_ZEROIZE",
54
+ "critical",
55
+ "mem::forget() prevents Drop/ZeroizeOnDrop from running — secret never wiped",
56
+ ),
57
+ (
58
+ r"\bManuallyDrop\s*::\s*new\s*\(",
59
+ "MISSING_SOURCE_ZEROIZE",
60
+ "critical",
61
+ "ManuallyDrop::new() suppresses automatic drop — "
62
+ "secret not wiped unless drop() called explicitly",
63
+ ),
64
+ (
65
+ r"\bBox\s*::\s*leak\s*\(",
66
+ "MISSING_SOURCE_ZEROIZE",
67
+ "critical",
68
+ "Box::leak() — leaked allocation is never dropped or zeroed",
69
+ ),
70
+ (
71
+ r"\bBox\s*::\s*into_raw\s*\(",
72
+ "MISSING_SOURCE_ZEROIZE",
73
+ "high",
74
+ "Box::into_raw() — raw pointer escapes Drop; "
75
+ "must call Box::from_raw() + zeroize to reclaim",
76
+ ),
77
+ (
78
+ r"\bptr\s*::\s*write_bytes\s*\(",
79
+ "OPTIMIZED_AWAY_ZEROIZE",
80
+ "high",
81
+ "ptr::write_bytes() is non-volatile — LLVM may eliminate as dead store. "
82
+ "Use zeroize crate or add compiler_fence(SeqCst) after",
83
+ ),
84
+ (
85
+ # Matches both turbofish form (transmute::<T, U>(v)) and type-inferred form (transmute(v))
86
+ r"\bmem\s*::\s*transmute\b",
87
+ "SECRET_COPY",
88
+ "high",
89
+ "mem::transmute creates a bitwise copy — original and transmuted value both exist on stack",
90
+ ),
91
+ (
92
+ r"\bslice\s*::\s*from_raw_parts\s*\(",
93
+ "SECRET_COPY",
94
+ "medium",
95
+ "slice::from_raw_parts creates a slice alias over raw memory — may alias a secret buffer",
96
+ ),
97
+ (
98
+ r"\bmem\s*::\s*take\s*\(",
99
+ "MISSING_SOURCE_ZEROIZE",
100
+ "medium",
101
+ "mem::take() replaces the value in-place without zeroing the original location",
102
+ ),
103
+ (
104
+ r"\bmem\s*::\s*uninitialized\s*\(",
105
+ "MISSING_SOURCE_ZEROIZE",
106
+ "critical",
107
+ "mem::uninitialized() is deprecated and unsafe — "
108
+ "may expose prior secret bytes from stack memory",
109
+ ),
110
+ ]
111
+
112
+ # Pre-compile all pattern regexes at module load time (avoids recompiling per file).
113
+ _COMPILED_PATTERNS: list[tuple[re.Pattern, str, str, str]] = [
114
+ (re.compile(pattern), category, severity, detail)
115
+ for pattern, category, severity, detail in PATTERNS
116
+ ]
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Finding counter
120
+ # ---------------------------------------------------------------------------
121
+
122
+ _finding_counter = [0]
123
+
124
+
125
+ def make_finding(
126
+ category: str,
127
+ severity: str,
128
+ detail: str,
129
+ file: str,
130
+ line: int,
131
+ symbol: str = "",
132
+ confidence: str = "likely",
133
+ ) -> dict:
134
+ _finding_counter[0] += 1
135
+ fid = f"F-RUST-SRC-{_finding_counter[0]:04d}"
136
+ return {
137
+ "id": fid,
138
+ "language": "rust",
139
+ "category": category,
140
+ "severity": severity,
141
+ "confidence": confidence,
142
+ "detail": detail,
143
+ "symbol": symbol,
144
+ "location": {"file": file, "line": line},
145
+ "evidence": [{"source": "source_grep", "detail": detail}],
146
+ }
147
+
148
+
149
+ # ---------------------------------------------------------------------------
150
+ # Context sensitivity check
151
+ # ---------------------------------------------------------------------------
152
+
153
+
154
+ def has_sensitive_context(lines: list[str], center_idx: int, window: int = 15) -> bool:
155
+ """Return True if any sensitive name appears within `window` lines of `center_idx`.
156
+
157
+ `center_idx` is a 0-based array index (i.e. ``lineno - 1``). Callers must
158
+ NOT pass 1-based line numbers here or the window will be off by one.
159
+ """
160
+ start = max(0, center_idx - window)
161
+ end = min(len(lines), center_idx + window + 1)
162
+ context = "\n".join(lines[start:end])
163
+ return bool(SENSITIVE_NAME_RE.search(context))
164
+
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # Grep-based pattern scanner
168
+ # ---------------------------------------------------------------------------
169
+
170
+ _BLOCK_COMMENT_START = re.compile(r"/\*")
171
+ _BLOCK_COMMENT_END = re.compile(r"\*/")
172
+
173
+
174
+ def _is_commented_out(line: str, in_block_comment: bool) -> tuple[bool, bool]:
175
+ """Return (skip_this_line, updated_in_block_comment).
176
+
177
+ Handles single-line `//` comments and block `/* ... */` comments. A line
178
+ that merely *contains* a comment start (e.g. `foo(); /* note */`) is NOT
179
+ fully skipped — only lines where the match site is inside the comment region
180
+ would be skipped. For simplicity this implementation skips the entire line
181
+ when it starts with `//` (after stripping) or when we are inside a block
182
+ comment. This is intentionally conservative: it may miss a pattern on the
183
+ same source line as an unrelated comment, but that is a very rare case.
184
+ """
185
+ stripped = line.strip()
186
+ if in_block_comment:
187
+ if _BLOCK_COMMENT_END.search(line):
188
+ return True, False # end of block comment on this line; skip line
189
+ return True, True # still inside block comment
190
+ if stripped.startswith("//"):
191
+ return True, False # single-line comment
192
+ if stripped.startswith("/*"):
193
+ if _BLOCK_COMMENT_END.search(line):
194
+ return True, False # block comment opens and closes on this line
195
+ return True, True # block comment opens; skip remainder
196
+ # Mid-line block comment: code precedes the /* (e.g. `code(); /* comment ...`).
197
+ # Do not skip this line (the match site may be in the code portion), but mark
198
+ # subsequent lines as inside a block comment.
199
+ if _BLOCK_COMMENT_START.search(stripped) and not _BLOCK_COMMENT_END.search(stripped):
200
+ return False, True
201
+ return False, False
202
+
203
+
204
+ def scan_file_patterns(path: Path, source: str) -> list[dict]:
205
+ findings: list[dict] = []
206
+ lines = source.splitlines()
207
+ in_block_comment = False
208
+
209
+ for compiled, category, severity, detail in _COMPILED_PATTERNS:
210
+ in_block_comment = False # reset per pattern pass
211
+ for lineno, line in enumerate(lines, start=1):
212
+ skip, in_block_comment = _is_commented_out(line, in_block_comment)
213
+ if skip:
214
+ continue
215
+ if not compiled.search(line):
216
+ continue
217
+ actual_severity = severity
218
+ actual_confidence = "likely"
219
+ if not has_sensitive_context(lines, lineno - 1): # lineno-1 → 0-based
220
+ actual_confidence = "needs_review"
221
+ findings.append(
222
+ make_finding(
223
+ category,
224
+ actual_severity,
225
+ detail,
226
+ str(path),
227
+ lineno,
228
+ confidence=actual_confidence,
229
+ )
230
+ )
231
+
232
+ return findings
233
+
234
+
235
+ # ---------------------------------------------------------------------------
236
+ # Async secret suspension detector
237
+ # ---------------------------------------------------------------------------
238
+
239
+
240
+ def scan_async_suspension(path: Path, source: str) -> list[dict]:
241
+ """
242
+ Detect: async fn body where a secret-named local is bound before an .await.
243
+
244
+ Heuristic:
245
+ 1. Find async fn declarations.
246
+ 2. Within each async fn body (between opening { and matching }), find let bindings
247
+ whose variable name matches SENSITIVE_NAME_RE.
248
+ 3. Check whether any .await appears after the binding within the same fn body.
249
+ 4. If so, emit NOT_ON_ALL_PATHS (high).
250
+ """
251
+ findings: list[dict] = []
252
+ lines = source.splitlines()
253
+
254
+ # Find all async fn start lines
255
+ async_fn_re = re.compile(r"\basync\s+fn\s+\w+")
256
+ let_binding_re = re.compile(r"\blet\s+(?:mut\s+)?(\w+)\s*[=:]")
257
+ await_re = re.compile(r"\.await\b")
258
+
259
+ i = 0
260
+ while i < len(lines):
261
+ if async_fn_re.search(lines[i]):
262
+ # Find the body: scan for opening brace
263
+ body_lines: list[tuple[int, str]] = []
264
+ depth = 0
265
+ in_body = False
266
+ for j in range(i, min(i + 500, len(lines))):
267
+ # Count braces, skipping string literals and line comments
268
+ in_str = False
269
+ k = 0
270
+ line_text = lines[j]
271
+ while k < len(line_text):
272
+ ch = line_text[k]
273
+ if in_str:
274
+ if ch == "\\" and k + 1 < len(line_text):
275
+ k += 2 # skip escape sequence
276
+ continue
277
+ elif ch == '"':
278
+ in_str = False
279
+ else:
280
+ if ch == '"':
281
+ in_str = True
282
+ elif ch == "/" and k + 1 < len(line_text) and line_text[k + 1] == "/":
283
+ break # rest of line is a comment
284
+ elif ch == "{":
285
+ depth += 1
286
+ in_body = True
287
+ elif ch == "}":
288
+ depth -= 1
289
+ k += 1
290
+ if in_body:
291
+ body_lines.append((j + 1, lines[j])) # 1-based line number
292
+ if in_body and depth == 0:
293
+ i = j + 1
294
+ break
295
+ else:
296
+ i += 1
297
+ continue
298
+
299
+ # Within body, find secret-named bindings followed by .await
300
+ secret_bindings: list[tuple[int, str]] = [] # (lineno, varname)
301
+ for lineno, line in body_lines:
302
+ m = let_binding_re.search(line)
303
+ if m and SENSITIVE_NAME_RE.search(m.group(1)):
304
+ secret_bindings.append((lineno, m.group(1)))
305
+
306
+ for bind_line, varname in secret_bindings:
307
+ # Check if .await appears after this binding in the fn body
308
+ for lineno, line in body_lines:
309
+ if lineno > bind_line and await_re.search(line):
310
+ findings.append(
311
+ make_finding(
312
+ "NOT_ON_ALL_PATHS",
313
+ "high",
314
+ f"Secret local '{varname}' is live across an .await suspension "
315
+ "point in an async fn — stored in the heap-allocated Future state "
316
+ "machine; ZeroizeOnDrop covers stack variables only",
317
+ str(path),
318
+ bind_line,
319
+ )
320
+ )
321
+ break # one finding per binding is enough
322
+ continue
323
+ i += 1
324
+
325
+ return findings
326
+
327
+
328
+ # ---------------------------------------------------------------------------
329
+ # Main scanner
330
+ # ---------------------------------------------------------------------------
331
+
332
+
333
+ def scan_directory(src_dir: Path) -> list[dict]:
334
+ findings: list[dict] = []
335
+ for rs_file in sorted(src_dir.rglob("*.rs")):
336
+ try:
337
+ source = rs_file.read_text(encoding="utf-8", errors="replace")
338
+ except OSError as e:
339
+ print(f"find_dangerous_apis.py: warning: cannot read {rs_file}: {e}", file=sys.stderr)
340
+ continue
341
+ findings.extend(scan_file_patterns(rs_file, source))
342
+ findings.extend(scan_async_suspension(rs_file, source))
343
+ return findings
344
+
345
+
346
+ # ---------------------------------------------------------------------------
347
+ # CLI
348
+ # ---------------------------------------------------------------------------
349
+
350
+
351
+ def main() -> int:
352
+ parser = argparse.ArgumentParser(
353
+ description="Token/grep-based scanner for dangerous Rust API patterns"
354
+ )
355
+ parser.add_argument("--src", required=True, help="Source directory to scan (.rs files)")
356
+ parser.add_argument("--out", required=True, help="Output findings JSON path")
357
+ args = parser.parse_args()
358
+
359
+ src_dir = Path(args.src)
360
+ if not src_dir.is_dir():
361
+ print(f"find_dangerous_apis.py: source directory not found: {src_dir}", file=sys.stderr)
362
+ return 1
363
+
364
+ findings = scan_directory(src_dir)
365
+
366
+ out_path = Path(args.out)
367
+ out_path.parent.mkdir(parents=True, exist_ok=True)
368
+ out_path.write_text(json.dumps(findings, indent=2), encoding="utf-8")
369
+
370
+ print(f"find_dangerous_apis.py: {len(findings)} finding(s) written to {out_path}")
371
+ return 0
372
+
373
+
374
+ if __name__ == "__main__":
375
+ sys.exit(main())