@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,424 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = []
5
+ # ///
6
+ """
7
+ check_rust_asm.py — Rust assembly analysis dispatcher for STACK_RETENTION and REGISTER_SPILL.
8
+
9
+ Detects the assembly architecture and delegates to the appropriate backend:
10
+ x86-64 → check_rust_asm_x86.py (production-ready)
11
+ AArch64 → check_rust_asm_aarch64.py (EXPERIMENTAL — findings require manual review)
12
+
13
+ Usage:
14
+ uv run check_rust_asm.py --asm <hash>.O2.s \\
15
+ --secrets sensitive-objects.json \\
16
+ --out asm-findings.json
17
+ """
18
+
19
+ import argparse
20
+ import importlib.util
21
+ import json
22
+ import re
23
+ import subprocess
24
+ import sys
25
+ from collections import defaultdict
26
+ from pathlib import Path
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Architecture detection
30
+ # ---------------------------------------------------------------------------
31
+
32
+
33
+ def detect_architecture(asm_text: str) -> str:
34
+ """
35
+ Heuristic architecture detection from assembly text.
36
+
37
+ x86-64: AT&T percent-prefix 64-bit register names (%rsp, %rax, …)
38
+ AArch64: distinctive ARM GNU syntax patterns (stp x29, str xzr, movi v#.*)
39
+ """
40
+ # x86-64: AT&T percent-prefix 64-bit register names
41
+ if re.search(r"%r(?:sp|bp|ax|bx|cx|dx|si|di)\b", asm_text):
42
+ return "x86_64"
43
+ # AArch64: distinctive prologue / zero-register / SIMD instructions (ARM GNU syntax)
44
+ if re.search(r"stp\s+x29|str\s+xzr|stp\s+xzr|movi\s+v\d+\.\w+", asm_text):
45
+ return "aarch64"
46
+ # Broad AArch64 fallback: bare xN registers used as instruction operands
47
+ if re.search(r"\b(?:x1[0-9]|x2[0-9]|x[0-9]),", asm_text):
48
+ return "aarch64"
49
+ return "unknown"
50
+
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # Symbol demangling (shared)
54
+ # ---------------------------------------------------------------------------
55
+
56
+
57
+ def demangle_symbols(asm_text: str) -> str:
58
+ """Demangle all Rust symbols using rustfilt if available."""
59
+ try:
60
+ result = subprocess.run(
61
+ ["rustfilt"],
62
+ input=asm_text,
63
+ capture_output=True,
64
+ text=True,
65
+ timeout=30,
66
+ )
67
+ if result.returncode == 0:
68
+ return result.stdout
69
+ except (FileNotFoundError, subprocess.TimeoutExpired, OSError) as e:
70
+ msg = f"rustfilt unavailable ({type(e).__name__})"
71
+ print(
72
+ f"[check_rust_asm] WARNING: {msg}, using regex demangling",
73
+ file=sys.stderr,
74
+ )
75
+
76
+ # Fallback: partial demangle via regex (strips hash suffix).
77
+ # NOTE: The pattern _ZN[A-Za-z0-9_$]+E matches any Itanium-mangled symbol
78
+ # (C++ included); it may garble non-Rust symbols into odd-looking paths.
79
+ # This is cosmetic — the demangled text is only used for display purposes.
80
+ # e.g. _ZN7example9SecretKey4wipe17h1a2b3c4d5e6f7g8hE -> example::SecretKey::wipe
81
+ def _partial(m: re.Match) -> str:
82
+ sym = m.group(0)
83
+ inner = re.sub(r"17h[0-9a-f]{16}E$", "", sym)
84
+ inner = re.sub(r"^_ZN", "", inner)
85
+ parts = []
86
+ while inner:
87
+ num = re.match(r"^(\d+)", inner)
88
+ if not num:
89
+ break
90
+ n = int(num.group(1))
91
+ inner = inner[len(num.group(1)) :]
92
+ parts.append(inner[:n])
93
+ inner = inner[n:]
94
+ return "::".join(parts) if parts else sym
95
+
96
+ return re.sub(r"_ZN[A-Za-z0-9_$]+E", _partial, asm_text)
97
+
98
+
99
+ # ---------------------------------------------------------------------------
100
+ # Assembly parsing (shared)
101
+ # ---------------------------------------------------------------------------
102
+
103
+ RE_FUNC_TYPE = re.compile(r"\.type\s+(\S+),\s*@function")
104
+ RE_GLOBL = re.compile(r"\.globl\s+(\S+)")
105
+ RE_LABEL = re.compile(r"^([A-Za-z_\$][A-Za-z0-9_\$@.]*):")
106
+ # Internal compiler-generated labels: Ltmp0, LBB0_1, .Ltmp0, etc.
107
+ RE_INTERNAL_LABEL = re.compile(r"^\.?L[A-Z_]")
108
+
109
+
110
+ def parse_functions(asm_lines: list[str]) -> dict[str, list[tuple[int, str]]]:
111
+ """
112
+ Split assembly into per-function sections.
113
+ Returns {function_name: [(line_no, line_text), ...]}
114
+
115
+ Supports both ELF (`.type sym,@function`) and Mach-O (`.globl sym`)
116
+ object formats. When no `.type` directives are found (macOS), falls back
117
+ to `.globl` symbols. Internal compiler labels (LBB0_1, Ltmp0, .Ltmp0)
118
+ are always excluded from function-start candidates.
119
+ """
120
+ functions: dict[str, list[tuple[int, str]]] = {}
121
+ current: str | None = None
122
+ current_lines: list[tuple[int, str]] = []
123
+
124
+ func_names: set[str] = set()
125
+ for line in asm_lines:
126
+ m = RE_FUNC_TYPE.search(line)
127
+ if m:
128
+ func_names.add(m.group(1))
129
+
130
+ # Mach-O fallback: if no ELF .type directives found, use .globl symbols
131
+ if not func_names:
132
+ for line in asm_lines:
133
+ m = RE_GLOBL.search(line)
134
+ if m:
135
+ func_names.add(m.group(1))
136
+
137
+ for lineno, line in enumerate(asm_lines, 1):
138
+ stripped = line.strip()
139
+ m = RE_LABEL.match(stripped)
140
+ if m:
141
+ label = m.group(1)
142
+ # Always skip internal compiler-generated labels regardless of func_names
143
+ if RE_INTERNAL_LABEL.match(label):
144
+ if current is not None:
145
+ current_lines.append((lineno, line))
146
+ continue
147
+ if not func_names or label in func_names:
148
+ if current is not None:
149
+ functions[current] = current_lines
150
+ current = label
151
+ current_lines = [(lineno, line)]
152
+ continue
153
+ if current is not None:
154
+ current_lines.append((lineno, line))
155
+
156
+ if current is not None:
157
+ functions[current] = current_lines
158
+
159
+ return functions
160
+
161
+
162
+ # ---------------------------------------------------------------------------
163
+ # Sensitive object matching (shared)
164
+ # ---------------------------------------------------------------------------
165
+
166
+
167
+ def load_secrets(secrets_path: str) -> list[str] | None:
168
+ """Return sensitive type/symbol names from sensitive-objects.json.
169
+
170
+ Returns an empty list when the file is absent (no secrets configured is
171
+ valid), or None when the file exists but contains corrupt JSON (signals an
172
+ error to the caller so analysis is not silently skipped).
173
+ """
174
+ try:
175
+ with open(secrets_path, encoding="utf-8") as f:
176
+ objects = json.load(f)
177
+ names = []
178
+ for obj in objects:
179
+ if obj.get("language") == "rust":
180
+ names.append(obj.get("name", ""))
181
+ return [n for n in names if n]
182
+ except FileNotFoundError:
183
+ return []
184
+ except json.JSONDecodeError as e:
185
+ print(
186
+ f"[check_rust_asm] ERROR: corrupt secrets JSON at {secrets_path!r}: {e}",
187
+ file=sys.stderr,
188
+ )
189
+ return None
190
+
191
+
192
+ def is_sensitive_function(func_name: str, sensitive_names: list[str]) -> bool:
193
+ """True if the demangled function name relates to a sensitive type."""
194
+ lower = func_name.lower()
195
+ if "drop_in_place" in lower:
196
+ return any(name.lower() in lower for name in sensitive_names)
197
+ return any(name.lower() in lower for name in sensitive_names)
198
+
199
+
200
+ # ---------------------------------------------------------------------------
201
+ # Drop glue check (shared — covers both x86-64 `call` and AArch64 `bl`)
202
+ # ---------------------------------------------------------------------------
203
+
204
+ # Matches both x86-64 `call` and AArch64 `bl` to zeroize/memset routines
205
+ RE_WIPE_CALL = re.compile(r"(?:call|bl)\s+.*(?:memset|volatile_set_memory|zeroize)")
206
+
207
+
208
+ def check_drop_glue(
209
+ func_name: str,
210
+ func_lines: list[tuple[int, str]],
211
+ ) -> dict | None:
212
+ """
213
+ For drop_in_place::<SensitiveType> functions, check for zeroize calls.
214
+ If absent, emit MISSING_SOURCE_ZEROIZE (medium) as corroboration.
215
+ Works for both x86-64 and AArch64 assembly.
216
+ """
217
+ if "drop_in_place" not in func_name.lower():
218
+ return None
219
+
220
+ has_zeroize = any(
221
+ RE_WIPE_CALL.search(line) or "zeroize" in line.lower() for _, line in func_lines
222
+ )
223
+ if not has_zeroize:
224
+ return {
225
+ "category": "MISSING_SOURCE_ZEROIZE",
226
+ "severity": "medium",
227
+ "symbol": func_name,
228
+ "detail": (
229
+ f"drop_in_place for '{func_name}' has no zeroize/volatile-store calls "
230
+ f"— sensitive type may not be wiped on drop"
231
+ ),
232
+ "evidence_detail": (
233
+ f"No zeroize call found in {func_name} drop glue ({len(func_lines)} lines)"
234
+ ),
235
+ }
236
+ return None
237
+
238
+
239
+ # ---------------------------------------------------------------------------
240
+ # Arch module loader
241
+ # ---------------------------------------------------------------------------
242
+
243
+
244
+ def _load_arch_module(name: str):
245
+ """Load an arch backend module from the same directory as this script."""
246
+ script_dir = Path(__file__).parent
247
+ module_path = script_dir / f"{name}.py"
248
+ spec = importlib.util.spec_from_file_location(name, module_path)
249
+ if spec is None or spec.loader is None:
250
+ raise ImportError(
251
+ f"Cannot load arch module {name!r} from {module_path} — "
252
+ "file not found or not a valid Python module"
253
+ )
254
+ module = importlib.util.module_from_spec(spec)
255
+ spec.loader.exec_module(module)
256
+ return module
257
+
258
+
259
+ # ---------------------------------------------------------------------------
260
+ # Main
261
+ # ---------------------------------------------------------------------------
262
+
263
+
264
+ def main() -> int:
265
+ parser = argparse.ArgumentParser(
266
+ description="Analyze Rust assembly for STACK_RETENTION and REGISTER_SPILL"
267
+ )
268
+ parser.add_argument("--asm", required=True, help="Path to .s assembly file")
269
+ parser.add_argument("--secrets", required=True, help="Path to sensitive-objects.json")
270
+ parser.add_argument("--out", required=True, help="Output JSON path")
271
+ args = parser.parse_args()
272
+
273
+ out_path = Path(args.out)
274
+
275
+ def _write_empty_and_return(code: int, message: str = "") -> int:
276
+ out_path.parent.mkdir(parents=True, exist_ok=True)
277
+ if code != 0 and message:
278
+ error_output = [
279
+ {
280
+ "id": "F-RUST-ASM-ERROR",
281
+ "category": "ANALYSIS_ERROR",
282
+ "severity": "info",
283
+ "detail": message,
284
+ "location": {"file": str(asm_path), "line": 0},
285
+ }
286
+ ]
287
+ out_path.write_text(json.dumps(error_output, indent=2), encoding="utf-8")
288
+ else:
289
+ out_path.write_text("[]", encoding="utf-8")
290
+ return code
291
+
292
+ asm_path = Path(args.asm)
293
+ if not asm_path.exists():
294
+ print(f"[check_rust_asm] ERROR: assembly file not found: {asm_path}", file=sys.stderr)
295
+ return _write_empty_and_return(1, f"Assembly file not found: {asm_path}")
296
+
297
+ try:
298
+ asm_text = asm_path.read_text(encoding="utf-8", errors="replace")
299
+ except OSError as e:
300
+ print(f"[check_rust_asm] ERROR: cannot read assembly file: {e}", file=sys.stderr)
301
+ return _write_empty_and_return(1, f"Cannot read assembly file: {e}")
302
+
303
+ arch = detect_architecture(asm_text)
304
+
305
+ if arch == "x86_64":
306
+ try:
307
+ arch_module = _load_arch_module("check_rust_asm_x86")
308
+ except ImportError as e:
309
+ print(f"[check_rust_asm] ERROR: cannot load x86 backend: {e}", file=sys.stderr)
310
+ return _write_empty_and_return(1, f"Cannot load x86 backend: {e}")
311
+ elif arch == "aarch64":
312
+ print(
313
+ "[check_rust_asm] NOTE: AArch64 support is EXPERIMENTAL. "
314
+ "Findings require manual verification before inclusion in a report.",
315
+ file=sys.stderr,
316
+ )
317
+ try:
318
+ arch_module = _load_arch_module("check_rust_asm_aarch64")
319
+ except ImportError as e:
320
+ print(f"[check_rust_asm] ERROR: cannot load AArch64 backend: {e}", file=sys.stderr)
321
+ return _write_empty_and_return(1, f"Cannot load AArch64 backend: {e}")
322
+ else:
323
+ print(
324
+ f"[check_rust_asm] WARNING: unsupported assembly architecture '{arch}'. "
325
+ "Writing skipped finding.",
326
+ file=sys.stderr,
327
+ )
328
+ output = [
329
+ {
330
+ "id": "F-RUST-ASM-SKIP-0001",
331
+ "category": "ANALYSIS_SKIPPED",
332
+ "severity": "info",
333
+ "confidence": "confirmed",
334
+ "detail": f"Unsupported assembly architecture '{arch}' -- no analysis performed",
335
+ "location": {"file": str(asm_path), "line": 0},
336
+ }
337
+ ]
338
+ out_path.parent.mkdir(parents=True, exist_ok=True)
339
+ out_path.write_text(json.dumps(output, indent=2), encoding="utf-8")
340
+ return 0
341
+
342
+ asm_demangled = demangle_symbols(asm_text)
343
+ asm_lines = asm_demangled.splitlines(keepends=True)
344
+
345
+ sensitive_names = load_secrets(args.secrets)
346
+ if sensitive_names is None:
347
+ print("[check_rust_asm] ERROR: aborting due to corrupt secrets file", file=sys.stderr)
348
+ return _write_empty_and_return(1, "Aborting due to corrupt secrets file")
349
+ if not sensitive_names:
350
+ print(
351
+ "[check_rust_asm] WARNING: no Rust sensitive objects found in secrets file",
352
+ file=sys.stderr,
353
+ )
354
+
355
+ functions = parse_functions([line.rstrip("\n") for line in asm_lines])
356
+
357
+ # Deduplicate: collapse monomorphized instances of the same generic function.
358
+ seen_findings: dict[tuple, dict] = {}
359
+ instance_counts: dict[tuple, int] = defaultdict(int)
360
+ raw_findings: list[dict] = []
361
+
362
+ def _dedup_key(finding: dict, base_name: str) -> tuple:
363
+ if finding["category"] == "REGISTER_SPILL":
364
+ return (finding["category"], base_name, finding.get("evidence_detail", ""))
365
+ return (finding["category"], base_name)
366
+
367
+ def _record(finding: dict, base_name: str) -> None:
368
+ key = _dedup_key(finding, base_name)
369
+ instance_counts[key] += 1
370
+ if key not in seen_findings:
371
+ seen_findings[key] = finding
372
+ # Store base_name so the output phase can reconstruct the dedup key
373
+ # without recomputing it from finding["symbol"] (which may differ due
374
+ # to monomorphization hash stripping vs. type-param stripping).
375
+ finding["_base_name"] = base_name
376
+ raw_findings.append(finding)
377
+
378
+ for func_name, func_lines in functions.items():
379
+ if not is_sensitive_function(func_name, sensitive_names):
380
+ continue
381
+
382
+ # Derive base name: strip monomorphization hash and type params
383
+ base_name = re.sub(r"::h[0-9a-f]{16}$", "", func_name)
384
+ base_name = re.sub(r"::<[^>]+>", "", base_name)
385
+
386
+ # Arch-specific findings (STACK_RETENTION, REGISTER_SPILL, red zone)
387
+ for finding in arch_module.analyze_function(func_name, func_lines):
388
+ _record(finding, base_name)
389
+
390
+ # Drop glue check (shared — works for both x86-64 and AArch64)
391
+ finding = check_drop_glue(func_name, func_lines)
392
+ if finding:
393
+ _record(finding, base_name)
394
+
395
+ # Assign IDs and build final output
396
+ output = []
397
+ for idx, finding in enumerate(raw_findings, 1):
398
+ base_name = finding.pop("_base_name", finding["symbol"])
399
+ key = _dedup_key(finding, base_name)
400
+ count = instance_counts.get(key, 1)
401
+ evidence_detail = finding.pop("evidence_detail", "")
402
+ if count > 1:
403
+ evidence_detail += f" (seen in {count} monomorphized instances)"
404
+ output.append(
405
+ {
406
+ "id": f"F-RUST-ASM-{idx:04d}",
407
+ "language": "rust",
408
+ "category": finding["category"],
409
+ "severity": finding["severity"],
410
+ "symbol": finding["symbol"],
411
+ "detail": finding["detail"],
412
+ "evidence": [{"source": "asm", "detail": evidence_detail}],
413
+ "evidence_files": [str(asm_path)],
414
+ }
415
+ )
416
+
417
+ out_path.parent.mkdir(parents=True, exist_ok=True)
418
+ out_path.write_text(json.dumps(output, indent=2), encoding="utf-8")
419
+ print(f"[check_rust_asm] {len(output)} finding(s) written to {args.out}", file=sys.stderr)
420
+ return 0
421
+
422
+
423
+ if __name__ == "__main__":
424
+ sys.exit(main())