@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,381 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = []
5
+ # ///
6
+ """
7
+ Control-Flow Graph analyzer for zeroization path coverage.
8
+
9
+ This tool builds CFGs from source code or LLVM IR to verify that:
10
+ - Zeroization occurs on ALL execution paths
11
+ - Early returns don't skip cleanup
12
+ - Error paths include proper cleanup
13
+ - Wipes dominate all function exits
14
+ """
15
+
16
+ import argparse
17
+ import json
18
+ import re
19
+ import sys
20
+ from dataclasses import dataclass, field
21
+ from pathlib import Path
22
+
23
+
24
+ @dataclass
25
+ class CFGNode:
26
+ """Node in control flow graph."""
27
+
28
+ id: str
29
+ type: str # 'entry', 'exit', 'statement', 'branch', 'return'
30
+ line_num: int | None = None
31
+ statement: str | None = None
32
+ successors: list[str] = field(default_factory=list)
33
+ predecessors: list[str] = field(default_factory=list)
34
+ has_wipe: bool = False
35
+ has_sensitive_var: bool = False
36
+
37
+
38
+ class CFGBuilder:
39
+ """Build control flow graph from source or IR."""
40
+
41
+ def __init__(self, source_file: Path, sensitive_patterns: list[str], wipe_patterns: list[str]):
42
+ self.source_file = source_file
43
+ self.sensitive_patterns = sensitive_patterns
44
+ self.wipe_patterns = wipe_patterns
45
+ self.nodes: dict[str, CFGNode] = {}
46
+ self.entry_node: str | None = None
47
+ self.exit_nodes: set[str] = set()
48
+ self.node_counter = 0
49
+
50
+ def create_node(
51
+ self, node_type: str, line_num: int | None = None, statement: str | None = None
52
+ ) -> str:
53
+ """Create a new CFG node."""
54
+ node_id = f"node_{self.node_counter}"
55
+ self.node_counter += 1
56
+
57
+ node = CFGNode(id=node_id, type=node_type, line_num=line_num, statement=statement)
58
+
59
+ # Check if this node has sensitive variable
60
+ if statement:
61
+ for pattern in self.sensitive_patterns:
62
+ if re.search(pattern, statement, re.IGNORECASE):
63
+ node.has_sensitive_var = True
64
+ break
65
+
66
+ # Check if this node has wipe
67
+ for pattern in self.wipe_patterns:
68
+ if re.search(pattern, statement):
69
+ node.has_wipe = True
70
+ break
71
+
72
+ self.nodes[node_id] = node
73
+ return node_id
74
+
75
+ def add_edge(self, from_id: str, to_id: str) -> None:
76
+ """Add directed edge in CFG."""
77
+ if from_id in self.nodes and to_id in self.nodes:
78
+ self.nodes[from_id].successors.append(to_id)
79
+ self.nodes[to_id].predecessors.append(from_id)
80
+
81
+ def build_from_source(self) -> None:
82
+ """Build CFG from source code (simplified C/C++ parser)."""
83
+ with open(self.source_file) as f:
84
+ lines = f.readlines()
85
+
86
+ self.entry_node = self.create_node("entry")
87
+ current_node = self.entry_node
88
+
89
+ in_function = False
90
+ brace_depth = 0
91
+ branch_stack = [] # Stack of (condition_node, merge_node) pairs
92
+
93
+ for line_num, line in enumerate(lines, 1):
94
+ stripped = line.strip()
95
+
96
+ # Skip comments and empty lines
97
+ if not stripped or stripped.startswith("//") or stripped.startswith("/*"):
98
+ continue
99
+
100
+ # Function start
101
+ if "{" in line and not in_function:
102
+ in_function = True
103
+ brace_depth = line.count("{")
104
+ continue
105
+
106
+ if not in_function:
107
+ continue
108
+
109
+ # Track brace depth
110
+ brace_depth += line.count("{") - line.count("}")
111
+
112
+ # Function end
113
+ if brace_depth == 0:
114
+ in_function = False
115
+ # Connect to exit
116
+ exit_node = self.create_node("exit", line_num)
117
+ self.add_edge(current_node, exit_node)
118
+ self.exit_nodes.add(exit_node)
119
+ continue
120
+
121
+ # Return statement
122
+ if re.match(r"\s*return\b", stripped):
123
+ return_node = self.create_node("return", line_num, stripped)
124
+ self.add_edge(current_node, return_node)
125
+ exit_node = self.create_node("exit", line_num)
126
+ self.add_edge(return_node, exit_node)
127
+ self.exit_nodes.add(exit_node)
128
+ # Reset current for next statement (in case there's dead code)
129
+ current_node = return_node
130
+ continue
131
+
132
+ # If statement
133
+ if re.match(r"\s*if\s*\(", stripped):
134
+ branch_node = self.create_node("branch", line_num, stripped)
135
+ self.add_edge(current_node, branch_node)
136
+
137
+ # Create merge point for later
138
+ merge_node = self.create_node("statement", line_num, "// merge point")
139
+ branch_stack.append((branch_node, merge_node))
140
+
141
+ # True branch starts after condition
142
+ true_node = self.create_node("statement", line_num, "// true branch")
143
+ self.add_edge(branch_node, true_node)
144
+ current_node = true_node
145
+ continue
146
+
147
+ # Else statement
148
+ if re.match(r"\s*else\b", stripped):
149
+ if branch_stack:
150
+ branch_node, merge_node = branch_stack[-1]
151
+ # False branch
152
+ false_node = self.create_node("statement", line_num, "// false branch")
153
+ self.add_edge(branch_node, false_node)
154
+ # Connect previous path to merge
155
+ self.add_edge(current_node, merge_node)
156
+ current_node = false_node
157
+ continue
158
+
159
+ # End of branch (closing brace)
160
+ if stripped == "}" and branch_stack:
161
+ branch_node, merge_node = branch_stack.pop()
162
+ self.add_edge(current_node, merge_node)
163
+ current_node = merge_node
164
+ continue
165
+
166
+ # Regular statement
167
+ stmt_node = self.create_node("statement", line_num, stripped)
168
+ self.add_edge(current_node, stmt_node)
169
+ current_node = stmt_node
170
+
171
+ # Ensure we have at least one exit node
172
+ if not self.exit_nodes:
173
+ exit_node = self.create_node("exit")
174
+ self.add_edge(current_node, exit_node)
175
+ self.exit_nodes.add(exit_node)
176
+
177
+ def find_all_paths_to_exit(self) -> list[list[str]]:
178
+ """Find all paths from entry to any exit node."""
179
+ if not self.entry_node:
180
+ return []
181
+
182
+ all_paths = []
183
+
184
+ def dfs(node_id: str, path: list[str], visited: set[str]) -> None:
185
+ if node_id in visited:
186
+ return # Avoid cycles
187
+
188
+ visited.add(node_id)
189
+ path.append(node_id)
190
+
191
+ node = self.nodes[node_id]
192
+
193
+ # If this is an exit node, save the path
194
+ if node_id in self.exit_nodes:
195
+ all_paths.append(path.copy())
196
+ else:
197
+ # Continue to successors
198
+ for succ_id in node.successors:
199
+ dfs(succ_id, path, visited.copy())
200
+
201
+ path.pop()
202
+
203
+ dfs(self.entry_node, [], set())
204
+ return all_paths
205
+
206
+ def check_path_has_wipe(self, path: list[str]) -> tuple[bool, str | None]:
207
+ """Check if a path contains a wipe operation."""
208
+ for node_id in path:
209
+ if self.nodes[node_id].has_wipe:
210
+ return True, node_id
211
+ return False, None
212
+
213
+ def check_path_has_sensitive_var(self, path: list[str]) -> bool:
214
+ """Check if a path uses sensitive variables."""
215
+ return any(self.nodes[node_id].has_sensitive_var for node_id in path)
216
+
217
+ def compute_dominators(self) -> dict[str, set[str]]:
218
+ """Compute dominator sets for all nodes."""
219
+ if not self.entry_node:
220
+ return {}
221
+
222
+ # Initialize
223
+ dominators = {}
224
+ all_nodes = set(self.nodes.keys())
225
+
226
+ dominators[self.entry_node] = {self.entry_node}
227
+
228
+ for node_id in all_nodes:
229
+ if node_id != self.entry_node:
230
+ dominators[node_id] = all_nodes.copy()
231
+
232
+ # Iterate until fixpoint
233
+ changed = True
234
+ while changed:
235
+ changed = False
236
+ for node_id in all_nodes:
237
+ if node_id == self.entry_node:
238
+ continue
239
+
240
+ # Dom(n) = {n} ∪ (∩ Dom(p) for all predecessors p)
241
+ new_dom = {node_id}
242
+ if self.nodes[node_id].predecessors:
243
+ pred_doms = [dominators[pred] for pred in self.nodes[node_id].predecessors]
244
+ if pred_doms:
245
+ new_dom = new_dom.union(set.intersection(*pred_doms))
246
+
247
+ if new_dom != dominators[node_id]:
248
+ dominators[node_id] = new_dom
249
+ changed = True
250
+
251
+ return dominators
252
+
253
+ def verify_wipe_dominates_exits(self) -> dict:
254
+ """Verify that wipe operations dominate all exit nodes."""
255
+ dominators = self.compute_dominators()
256
+
257
+ # Find all wipe nodes
258
+ wipe_nodes = [node_id for node_id, node in self.nodes.items() if node.has_wipe]
259
+
260
+ results = {
261
+ "wipe_dominates_all_exits": True,
262
+ "wipe_nodes": wipe_nodes,
263
+ "problematic_exits": [],
264
+ }
265
+
266
+ for exit_id in self.exit_nodes:
267
+ exit_doms = dominators.get(exit_id, set())
268
+
269
+ # Check if any wipe node dominates this exit
270
+ has_dominating_wipe = any(wipe_id in exit_doms for wipe_id in wipe_nodes)
271
+
272
+ if not has_dominating_wipe:
273
+ results["wipe_dominates_all_exits"] = False
274
+ results["problematic_exits"].append(
275
+ {
276
+ "exit_node": exit_id,
277
+ "line": self.nodes[exit_id].line_num,
278
+ "dominators": list(exit_doms),
279
+ }
280
+ )
281
+
282
+ return results
283
+
284
+ def analyze(self) -> dict:
285
+ """Perform comprehensive CFG analysis."""
286
+ # Find all paths
287
+ all_paths = self.find_all_paths_to_exit()
288
+
289
+ # Check each path
290
+ paths_with_wipe = 0
291
+ paths_without_wipe = []
292
+ paths_with_sensitive_vars = 0
293
+
294
+ for i, path in enumerate(all_paths):
295
+ has_wipe, wipe_node = self.check_path_has_wipe(path)
296
+ has_sensitive = self.check_path_has_sensitive_var(path)
297
+
298
+ if has_wipe:
299
+ paths_with_wipe += 1
300
+ elif has_sensitive:
301
+ # Sensitive path without wipe
302
+ paths_without_wipe.append(
303
+ {
304
+ "path_id": i,
305
+ "length": len(path),
306
+ "nodes": [
307
+ {
308
+ "id": node_id,
309
+ "line": self.nodes[node_id].line_num,
310
+ "statement": self.nodes[node_id].statement,
311
+ }
312
+ for node_id in path
313
+ ],
314
+ }
315
+ )
316
+
317
+ if has_sensitive:
318
+ paths_with_sensitive_vars += 1
319
+
320
+ # Dominator analysis
321
+ dominator_results = self.verify_wipe_dominates_exits()
322
+
323
+ return {
324
+ "cfg_stats": {
325
+ "total_nodes": len(self.nodes),
326
+ "total_paths": len(all_paths),
327
+ "exit_nodes": len(self.exit_nodes),
328
+ },
329
+ "wipe_coverage": {
330
+ "paths_with_wipe": paths_with_wipe,
331
+ "paths_without_wipe": len(paths_without_wipe),
332
+ "paths_with_sensitive_vars": paths_with_sensitive_vars,
333
+ "coverage_percentage": (paths_with_wipe / len(all_paths) * 100) if all_paths else 0,
334
+ },
335
+ "problematic_paths": paths_without_wipe,
336
+ "dominator_analysis": dominator_results,
337
+ }
338
+
339
+
340
+ def main():
341
+ parser = argparse.ArgumentParser(description="Control-flow graph analyzer")
342
+ parser.add_argument("--src", required=True, help="Source file to analyze")
343
+ parser.add_argument("--out", required=True, help="Output JSON file")
344
+
345
+ args = parser.parse_args()
346
+
347
+ # Default patterns
348
+ sensitive_patterns = [
349
+ r"\b(secret|key|seed|priv|private|sk|shared_secret|nonce|token|pwd|pass)\b"
350
+ ]
351
+ wipe_patterns = [
352
+ r"\bexplicit_bzero\s*\(",
353
+ r"\bmemset_s\s*\(",
354
+ r"\bOPENSSL_cleanse\s*\(",
355
+ r"\bsodium_memzero\s*\(",
356
+ r"\bzeroize\s*\(",
357
+ ]
358
+
359
+ # Build CFG
360
+ builder = CFGBuilder(Path(args.src), sensitive_patterns, wipe_patterns)
361
+ try:
362
+ builder.build_from_source()
363
+ except OSError as e:
364
+ print(f"Error: cannot read source file {args.src}: {e}", file=sys.stderr)
365
+ sys.exit(1)
366
+
367
+ # Analyze
368
+ results = {"source_file": args.src, "analysis": builder.analyze()}
369
+
370
+ # Write output
371
+ output_path = Path(args.out)
372
+ output_path.parent.mkdir(parents=True, exist_ok=True)
373
+
374
+ with open(output_path, "w") as f:
375
+ json.dump(results, f, indent=2)
376
+
377
+ print(f"OK: CFG analysis written to {args.out}")
378
+
379
+
380
+ if __name__ == "__main__":
381
+ main()
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Analyze heap allocations for security issues with sensitive data.
5
+ #
6
+ # Usage:
7
+ # analyze_heap.sh --src path/to/file.c --config config.yaml --out /tmp/heap_analysis.json
8
+ #
9
+ # Detects:
10
+ # - malloc/calloc/realloc for sensitive variables (should use secure allocators)
11
+ # - Missing mlock/madvise for sensitive heaps
12
+ # - Secure allocator usage (approved patterns)
13
+
14
+ usage() {
15
+ echo "Usage: $0 --src <file> --out <analysis.json> [--config <config.yaml>]" >&2
16
+ }
17
+
18
+ json_escape() {
19
+ local s="$1"
20
+ s="${s//\\/\\\\}"
21
+ s="${s//\"/\\\"}"
22
+ s="${s//$'\n'/\\n}"
23
+ s="${s//$'\t'/\\t}"
24
+ printf '%s' "$s"
25
+ }
26
+
27
+ SRC=""
28
+ CONFIG=""
29
+ OUT=""
30
+
31
+ while [[ $# -gt 0 ]]; do
32
+ case "$1" in
33
+ --src)
34
+ SRC="$2"
35
+ shift 2
36
+ ;;
37
+ --config)
38
+ CONFIG="$2"
39
+ shift 2
40
+ ;;
41
+ --out)
42
+ OUT="$2"
43
+ shift 2
44
+ ;;
45
+ *)
46
+ echo "Unknown arg: $1" >&2
47
+ usage
48
+ exit 2
49
+ ;;
50
+ esac
51
+ done
52
+
53
+ if [[ -z "$SRC" || -z "$OUT" ]]; then
54
+ usage
55
+ exit 2
56
+ fi
57
+
58
+ if [[ ! -f "$SRC" ]]; then
59
+ echo "Source file not found: $SRC" >&2
60
+ exit 2
61
+ fi
62
+
63
+ # Load patterns from config
64
+ SENSITIVE_PATTERN="(secret|key|seed|priv|private|sk|shared_secret|nonce|token|pwd|pass)"
65
+ SECURE_ALLOC_FUNCS="(OPENSSL_secure_malloc|OPENSSL_secure_zalloc|sodium_malloc|sodium_allocarray|SecureAlloc)"
66
+
67
+ if [[ -n "$CONFIG" ]] && [[ -f "$CONFIG" ]]; then
68
+ # Extract patterns from YAML (POSIX-compatible, no grep -P)
69
+ SENS_PAT=$(grep -A 20 "^sensitive_name_regex:" "$CONFIG" | sed -n 's/.*"\([^"]*\)".*/\1/p' | head -1 || echo "")
70
+ if [[ -n "$SENS_PAT" ]]; then
71
+ SENSITIVE_PATTERN="$SENS_PAT"
72
+ fi
73
+
74
+ SEC_FUNCS=$(grep -A 20 "^secure_heap_alloc_funcs:" "$CONFIG" | sed -n 's/.*- "\([^"]*\)".*/\1/p' | tr '\n' '|' | sed 's/|$//')
75
+ if [[ -n "$SEC_FUNCS" ]]; then
76
+ SECURE_ALLOC_FUNCS="($SEC_FUNCS)"
77
+ elif [[ -z "$SENS_PAT" ]]; then
78
+ echo "WARNING: config file provided but no patterns extracted from $CONFIG" >&2
79
+ fi
80
+ fi
81
+
82
+ # Arrays to collect findings
83
+ INSECURE_ALLOCS=()
84
+ SECURE_ALLOCS=()
85
+ MISSING_MLOCK=()
86
+ MISSING_MADVISE=()
87
+ MADVISE_RE='madvise[[:space:]]*\(([a-zA-Z_][a-zA-Z0-9_]*)[^)]*MADV_(DONTDUMP|DONTFORK|WIPEONFORK)'
88
+
89
+ # Track allocated pointers to check for mlock/madvise
90
+ declare -A ALLOCATED_PTRS
91
+
92
+ LINE_NUM=0
93
+
94
+ while IFS= read -r line; do
95
+ ((LINE_NUM++))
96
+
97
+ # Skip comments
98
+ [[ "$line" =~ ^[[:space:]]*// ]] && continue
99
+ [[ "$line" =~ ^[[:space:]]*\* ]] && continue
100
+
101
+ # Detect insecure allocations
102
+ if [[ "$line" =~ ([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*(malloc|calloc|realloc)[[:space:]]*\( ]]; then
103
+ PTR="${BASH_REMATCH[1]}"
104
+ ALLOC_FUNC="${BASH_REMATCH[2]}"
105
+
106
+ if [[ "$PTR" =~ $SENSITIVE_PATTERN ]]; then
107
+ INSECURE_ALLOCS+=("{\"line\": $LINE_NUM, \"pointer\": \"$PTR\", \"allocator\": \"$ALLOC_FUNC\", \"severity\": \"high\", \"context\": \"$(json_escape "$line")\"}")
108
+ ALLOCATED_PTRS["$PTR"]="insecure:$LINE_NUM"
109
+ fi
110
+ fi
111
+
112
+ # Detect secure allocations
113
+ if [[ "$line" =~ ([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*($SECURE_ALLOC_FUNCS)[[:space:]]*\( ]]; then
114
+ PTR="${BASH_REMATCH[1]}"
115
+ ALLOC_FUNC="${BASH_REMATCH[2]}"
116
+
117
+ SECURE_ALLOCS+=("{\"line\": $LINE_NUM, \"pointer\": \"$PTR\", \"allocator\": \"$ALLOC_FUNC\", \"context\": \"$(json_escape "$line")\"}")
118
+ ALLOCATED_PTRS["$PTR"]="secure:$LINE_NUM"
119
+ fi
120
+
121
+ # Detect mlock usage
122
+ if [[ "$line" =~ mlock[2]?[[:space:]]*\(([a-zA-Z_][a-zA-Z0-9_]*) ]]; then
123
+ PTR="${BASH_REMATCH[1]}"
124
+ if [[ -n "${ALLOCATED_PTRS[$PTR]:-}" ]]; then
125
+ ALLOCATED_PTRS["$PTR"]="${ALLOCATED_PTRS[$PTR]}:mlocked"
126
+ fi
127
+ fi
128
+
129
+ # Detect madvise usage
130
+ if [[ "$line" =~ $MADVISE_RE ]]; then
131
+ PTR="${BASH_REMATCH[1]}"
132
+ if [[ -n "${ALLOCATED_PTRS[$PTR]:-}" ]]; then
133
+ ALLOCATED_PTRS["$PTR"]="${ALLOCATED_PTRS[$PTR]}:madvised"
134
+ fi
135
+ fi
136
+
137
+ done <"$SRC"
138
+
139
+ # Check for missing protections
140
+ for PTR in "${!ALLOCATED_PTRS[@]}"; do
141
+ INFO="${ALLOCATED_PTRS[$PTR]}"
142
+
143
+ if [[ "$INFO" =~ ^insecure: ]]; then
144
+ LINE="${INFO#insecure:}"
145
+ LINE="${LINE%%:*}"
146
+
147
+ if [[ ! "$INFO" =~ mlocked ]]; then
148
+ MISSING_MLOCK+=("{\"line\": $LINE, \"pointer\": \"$PTR\", \"recommendation\": \"Add mlock() to prevent swapping to disk\"}")
149
+ fi
150
+
151
+ if [[ ! "$INFO" =~ madvised ]]; then
152
+ MISSING_MADVISE+=("{\"line\": $LINE, \"pointer\": \"$PTR\", \"recommendation\": \"Add madvise(MADV_DONTDUMP) to exclude from core dumps\"}")
153
+ fi
154
+ fi
155
+ done
156
+
157
+ # Generate JSON report
158
+ mkdir -p "$(dirname "$OUT")"
159
+
160
+ cat >"$OUT" <<EOF
161
+ {
162
+ "source_file": "$SRC",
163
+ "findings": {
164
+ "insecure_allocations": [
165
+ $(
166
+ IFS=,
167
+ echo "${INSECURE_ALLOCS[*]}"
168
+ )
169
+ ],
170
+ "secure_allocations": [
171
+ $(
172
+ IFS=,
173
+ echo "${SECURE_ALLOCS[*]}"
174
+ )
175
+ ],
176
+ "missing_mlock": [
177
+ $(
178
+ IFS=,
179
+ echo "${MISSING_MLOCK[*]}"
180
+ )
181
+ ],
182
+ "missing_madvise": [
183
+ $(
184
+ IFS=,
185
+ echo "${MISSING_MADVISE[*]}"
186
+ )
187
+ ]
188
+ },
189
+ "summary": {
190
+ "insecure_alloc_count": ${#INSECURE_ALLOCS[@]},
191
+ "secure_alloc_count": ${#SECURE_ALLOCS[@]},
192
+ "missing_protection_count": $((${#MISSING_MLOCK[@]} + ${#MISSING_MADVISE[@]}))
193
+ },
194
+ "recommendations": [
195
+ "Replace malloc/calloc/realloc with OPENSSL_secure_malloc/sodium_malloc for sensitive data",
196
+ "Use mlock() to prevent sensitive memory from being swapped to disk",
197
+ "Use madvise(MADV_DONTDUMP) to exclude sensitive memory from core dumps",
198
+ "Use madvise(MADV_WIPEONFORK) to zero memory in child processes after fork"
199
+ ]
200
+ }
201
+ EOF
202
+
203
+ # Validate JSON output
204
+ if command -v jq &>/dev/null; then
205
+ if ! jq empty "$OUT" 2>/dev/null; then
206
+ echo "ERROR: generated JSON is malformed: $OUT" >&2
207
+ exit 1
208
+ fi
209
+ fi
210
+
211
+ echo "OK: heap analysis written to $OUT"