frapast 0.1.0__tar.gz

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 (172) hide show
  1. frapast-0.1.0/LICENSE +21 -0
  2. frapast-0.1.0/PKG-INFO +402 -0
  3. frapast-0.1.0/README.md +362 -0
  4. frapast-0.1.0/frapast.egg-info/PKG-INFO +402 -0
  5. frapast-0.1.0/frapast.egg-info/SOURCES.txt +170 -0
  6. frapast-0.1.0/frapast.egg-info/dependency_links.txt +1 -0
  7. frapast-0.1.0/frapast.egg-info/entry_points.txt +5 -0
  8. frapast-0.1.0/frapast.egg-info/requires.txt +17 -0
  9. frapast-0.1.0/frapast.egg-info/top_level.txt +1 -0
  10. frapast-0.1.0/pyproject.toml +98 -0
  11. frapast-0.1.0/scanner/__init__.py +23 -0
  12. frapast-0.1.0/scanner/__main__.py +7 -0
  13. frapast-0.1.0/scanner/autofix/__init__.py +5 -0
  14. frapast-0.1.0/scanner/autofix/engine.py +313 -0
  15. frapast-0.1.0/scanner/autofix/models.py +21 -0
  16. frapast-0.1.0/scanner/bench_commands.py +85 -0
  17. frapast-0.1.0/scanner/callgraph/__init__.py +4 -0
  18. frapast-0.1.0/scanner/callgraph/builder.py +139 -0
  19. frapast-0.1.0/scanner/callgraph/models.py +74 -0
  20. frapast-0.1.0/scanner/cli.py +1213 -0
  21. frapast-0.1.0/scanner/config.py +69 -0
  22. frapast-0.1.0/scanner/fp/__init__.py +11 -0
  23. frapast-0.1.0/scanner/fp/engine.py +98 -0
  24. frapast-0.1.0/scanner/fp/models.py +30 -0
  25. frapast-0.1.0/scanner/fp_analyzer.py +69 -0
  26. frapast-0.1.0/scanner/hooks/__init__.py +12 -0
  27. frapast-0.1.0/scanner/hooks/engine.py +111 -0
  28. frapast-0.1.0/scanner/hooks/models.py +56 -0
  29. frapast-0.1.0/scanner/ledger_io.py +188 -0
  30. frapast-0.1.0/scanner/ledger_schema.py +33 -0
  31. frapast-0.1.0/scanner/logger.py +23 -0
  32. frapast-0.1.0/scanner/proof/__init__.py +22 -0
  33. frapast-0.1.0/scanner/proof/bench_runner.py +541 -0
  34. frapast-0.1.0/scanner/proof/http_client.py +347 -0
  35. frapast-0.1.0/scanner/proof/http_synthesis.py +641 -0
  36. frapast-0.1.0/scanner/proof/models.py +31 -0
  37. frapast-0.1.0/scanner/proof/orchestrator.py +826 -0
  38. frapast-0.1.0/scanner/proof/validate_reproducer_markers.py +112 -0
  39. frapast-0.1.0/scanner/python/__init__.py +43 -0
  40. frapast-0.1.0/scanner/python/engine.py +1172 -0
  41. frapast-0.1.0/scanner/python/models.py +305 -0
  42. frapast-0.1.0/scanner/regression/__init__.py +7 -0
  43. frapast-0.1.0/scanner/regression/generator.py +114 -0
  44. frapast-0.1.0/scanner/reporting/__init__.py +3 -0
  45. frapast-0.1.0/scanner/reporting/engine.py +144 -0
  46. frapast-0.1.0/scanner/reporting/formatters.py +8 -0
  47. frapast-0.1.0/scanner/reporting/sarif.py +83 -0
  48. frapast-0.1.0/scanner/rules/FR-CORR-001.yaml +7 -0
  49. frapast-0.1.0/scanner/rules/FR-CORR-002.yaml +7 -0
  50. frapast-0.1.0/scanner/rules/FR-CSRF-001.yaml +12 -0
  51. frapast-0.1.0/scanner/rules/FR-DATA-001.yaml +7 -0
  52. frapast-0.1.0/scanner/rules/FR-HOOK-001.yaml +7 -0
  53. frapast-0.1.0/scanner/rules/FR-HOOK-002.yaml +7 -0
  54. frapast-0.1.0/scanner/rules/FR-HOOK-003.yaml +7 -0
  55. frapast-0.1.0/scanner/rules/FR-HOOK-004.yaml +7 -0
  56. frapast-0.1.0/scanner/rules/FR-HOOK-005.yaml +7 -0
  57. frapast-0.1.0/scanner/rules/FR-HOOK-006.yaml +7 -0
  58. frapast-0.1.0/scanner/rules/FR-HOOK-007.yaml +7 -0
  59. frapast-0.1.0/scanner/rules/FR-I18N-001.yaml +7 -0
  60. frapast-0.1.0/scanner/rules/FR-INJ-001.yaml +7 -0
  61. frapast-0.1.0/scanner/rules/FR-INJ-002.yaml +7 -0
  62. frapast-0.1.0/scanner/rules/FR-INJ-005.yaml +11 -0
  63. frapast-0.1.0/scanner/rules/FR-PATH-001.yaml +7 -0
  64. frapast-0.1.0/scanner/rules/FR-PERF-001.yaml +7 -0
  65. frapast-0.1.0/scanner/rules/FR-PERM-001.yaml +7 -0
  66. frapast-0.1.0/scanner/rules/FR-PERM-002.yaml +7 -0
  67. frapast-0.1.0/scanner/rules/FR-PERM-003.yaml +7 -0
  68. frapast-0.1.0/scanner/rules/FR-PERM-004.yaml +7 -0
  69. frapast-0.1.0/scanner/rules/FR-PERM-005.yaml +7 -0
  70. frapast-0.1.0/scanner/rules/FR-PERM-006.yaml +7 -0
  71. frapast-0.1.0/scanner/rules/FR-SQLI-001.yaml +7 -0
  72. frapast-0.1.0/scanner/rules/FR-SQLI-002.yaml +7 -0
  73. frapast-0.1.0/scanner/rules/FR-SQLI-003.yaml +7 -0
  74. frapast-0.1.0/scanner/rules/FR-SQLI-004.yaml +7 -0
  75. frapast-0.1.0/scanner/rules/FR-SSRF-001.yaml +11 -0
  76. frapast-0.1.0/scanner/rules/FR-WKFL-001.yaml +7 -0
  77. frapast-0.1.0/scanner/rules/FR-WKFL-002.yaml +7 -0
  78. frapast-0.1.0/scanner/rules/FR-WKFL-003.yaml +7 -0
  79. frapast-0.1.0/scanner/rules/FR-WKFL-004.yaml +7 -0
  80. frapast-0.1.0/scanner/rules/FR-XSS-001.yaml +11 -0
  81. frapast-0.1.0/scanner/rules/__init__.py +3 -0
  82. frapast-0.1.0/scanner/rules/engine.py +1232 -0
  83. frapast-0.1.0/scanner/rules/schema.yaml +8 -0
  84. frapast-0.1.0/scanner/schema/__init__.py +19 -0
  85. frapast-0.1.0/scanner/schema/engine.py +148 -0
  86. frapast-0.1.0/scanner/schema/models.py +94 -0
  87. frapast-0.1.0/scanner/severity/__init__.py +4 -0
  88. frapast-0.1.0/scanner/severity/engine.py +199 -0
  89. frapast-0.1.0/scanner/severity/models.py +42 -0
  90. frapast-0.1.0/scanner/severity/taxonomy.py +8 -0
  91. frapast-0.1.0/scanner/shared/__init__.py +3 -0
  92. frapast-0.1.0/scanner/shared/records.py +31 -0
  93. frapast-0.1.0/scanner/suppression.py +274 -0
  94. frapast-0.1.0/scanner/taxonomy/FR-DATA-001.yaml +9 -0
  95. frapast-0.1.0/scanner/taxonomy/FR-DATA-002.yaml +9 -0
  96. frapast-0.1.0/scanner/taxonomy/FR-DATA-003.yaml +9 -0
  97. frapast-0.1.0/scanner/taxonomy/FR-HOOK-001.yaml +9 -0
  98. frapast-0.1.0/scanner/taxonomy/FR-HOOK-002.yaml +9 -0
  99. frapast-0.1.0/scanner/taxonomy/FR-HOOK-003.yaml +9 -0
  100. frapast-0.1.0/scanner/taxonomy/FR-HOOK-004.yaml +9 -0
  101. frapast-0.1.0/scanner/taxonomy/FR-HOOK-005.yaml +9 -0
  102. frapast-0.1.0/scanner/taxonomy/FR-INJ-001.yaml +9 -0
  103. frapast-0.1.0/scanner/taxonomy/FR-INJ-002.yaml +9 -0
  104. frapast-0.1.0/scanner/taxonomy/FR-INJ-003.yaml +9 -0
  105. frapast-0.1.0/scanner/taxonomy/FR-INJ-004.yaml +9 -0
  106. frapast-0.1.0/scanner/taxonomy/FR-PERM-001.yaml +10 -0
  107. frapast-0.1.0/scanner/taxonomy/FR-PERM-002.yaml +9 -0
  108. frapast-0.1.0/scanner/taxonomy/FR-PERM-003.yaml +9 -0
  109. frapast-0.1.0/scanner/taxonomy/FR-PERM-004.yaml +9 -0
  110. frapast-0.1.0/scanner/taxonomy/FR-PERM-005.yaml +9 -0
  111. frapast-0.1.0/scanner/taxonomy/FR-PERM-006.yaml +9 -0
  112. frapast-0.1.0/scanner/taxonomy/FR-SQLI-001.yaml +10 -0
  113. frapast-0.1.0/scanner/taxonomy/FR-SQLI-002.yaml +9 -0
  114. frapast-0.1.0/scanner/taxonomy/FR-SQLI-003.yaml +9 -0
  115. frapast-0.1.0/scanner/taxonomy/FR-SQLI-004.yaml +9 -0
  116. frapast-0.1.0/scanner/taxonomy/FR-WKFL-001.yaml +9 -0
  117. frapast-0.1.0/scanner/taxonomy/FR-WKFL-002.yaml +9 -0
  118. frapast-0.1.0/scanner/taxonomy/FR-WKFL-003.yaml +9 -0
  119. frapast-0.1.0/scanner/taxonomy/FR-WKFL-004.yaml +9 -0
  120. frapast-0.1.0/scanner/taxonomy/taxonomy_registry.yaml +75 -0
  121. frapast-0.1.0/scanner/ui/__init__.py +27 -0
  122. frapast-0.1.0/scanner/ui/banner.py +88 -0
  123. frapast-0.1.0/scanner/ui/flow.py +457 -0
  124. frapast-0.1.0/scanner/ui/menus.py +245 -0
  125. frapast-0.1.0/scanner/ui/progress.py +70 -0
  126. frapast-0.1.0/scanner/ui/results.py +330 -0
  127. frapast-0.1.0/scanner/ui/shell.py +446 -0
  128. frapast-0.1.0/scanner/ui/theme.py +73 -0
  129. frapast-0.1.0/scanner/validate/__init__.py +3 -0
  130. frapast-0.1.0/scanner/validate/engine.py +72 -0
  131. frapast-0.1.0/scanner/validate_ledger.py +37 -0
  132. frapast-0.1.0/scanner/validate_taxonomy.py +74 -0
  133. frapast-0.1.0/scanner/verify_ledger_integrity.py +69 -0
  134. frapast-0.1.0/scanner/web/__init__.py +1 -0
  135. frapast-0.1.0/scanner/web/db.py +351 -0
  136. frapast-0.1.0/scanner/web/server.py +1252 -0
  137. frapast-0.1.0/scanner/web/static/app.js +1790 -0
  138. frapast-0.1.0/scanner/web/static/index.html +468 -0
  139. frapast-0.1.0/scanner/web/static/logo-dark-mark.png +0 -0
  140. frapast-0.1.0/scanner/web/static/logo-light-mark.png +0 -0
  141. frapast-0.1.0/scanner/web/static/logo-light.png +0 -0
  142. frapast-0.1.0/scanner/web/static/logo-light.svg +6 -0
  143. frapast-0.1.0/scanner/web/static/logo.png +0 -0
  144. frapast-0.1.0/scanner/web/static/logo.svg +5 -0
  145. frapast-0.1.0/scanner/web/static/style.css +2194 -0
  146. frapast-0.1.0/setup.cfg +4 -0
  147. frapast-0.1.0/tests/test_all_rules.py +90 -0
  148. frapast-0.1.0/tests/test_ast_rule_coverage.py +427 -0
  149. frapast-0.1.0/tests/test_audit_fixes.py +122 -0
  150. frapast-0.1.0/tests/test_autofix.py +130 -0
  151. frapast-0.1.0/tests/test_callgraph_dispatch.py +124 -0
  152. frapast-0.1.0/tests/test_cli_exit_codes.py +102 -0
  153. frapast-0.1.0/tests/test_config.py +25 -0
  154. frapast-0.1.0/tests/test_cve_gaps_fix.py +259 -0
  155. frapast-0.1.0/tests/test_developer_workflow.py +50 -0
  156. frapast-0.1.0/tests/test_fr_data_001_fix.py +403 -0
  157. frapast-0.1.0/tests/test_fr_perm_perf_fix.py +251 -0
  158. frapast-0.1.0/tests/test_fresh_install.py +110 -0
  159. frapast-0.1.0/tests/test_hardening.py +72 -0
  160. frapast-0.1.0/tests/test_hardening_v2.py +112 -0
  161. frapast-0.1.0/tests/test_phase0.py +34 -0
  162. frapast-0.1.0/tests/test_phase1.py +179 -0
  163. frapast-0.1.0/tests/test_phase2.py +20 -0
  164. frapast-0.1.0/tests/test_phase3.py +31 -0
  165. frapast-0.1.0/tests/test_precision_benchmark.py +443 -0
  166. frapast-0.1.0/tests/test_python_indexer.py +62 -0
  167. frapast-0.1.0/tests/test_security_hardening_regression.py +333 -0
  168. frapast-0.1.0/tests/test_server_security.py +55 -0
  169. frapast-0.1.0/tests/test_suppression.py +194 -0
  170. frapast-0.1.0/tests/test_suppression_adversarial.py +228 -0
  171. frapast-0.1.0/tests/test_tier1_reproducers.py +119 -0
  172. frapast-0.1.0/tests/test_tier2_http.py +492 -0
frapast-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frappe Security Scanner Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
frapast-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,402 @@
1
+ Metadata-Version: 2.4
2
+ Name: frapast
3
+ Version: 0.1.0
4
+ Summary: Runtime-Proven Static Security Analysis Engine for Frappe and ERPNext
5
+ Author-email: Pratheep S <pratheeps2024@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pratheep-bit/frapast
8
+ Project-URL: Repository, https://github.com/pratheep-bit/frapast
9
+ Project-URL: Issues, https://github.com/pratheep-bit/frapast/issues
10
+ Project-URL: Security, https://github.com/pratheep-bit/frapast/security/advisories
11
+ Project-URL: Changelog, https://github.com/pratheep-bit/frapast/releases
12
+ Keywords: frappe,erpnext,security,sast,static-analysis,vulnerability-scanner
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Security
16
+ Classifier: Topic :: Software Development :: Quality Assurance
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Operating System :: OS Independent
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: pyyaml>=6.0
27
+ Requires-Dist: rich>=13.0.0
28
+ Requires-Dist: libcst>=1.0.0
29
+ Requires-Dist: tomli>=2.0.0; python_version < "3.11"
30
+ Provides-Extra: shell
31
+ Requires-Dist: prompt_toolkit>=3.0; extra == "shell"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Requires-Dist: build>=1.0.0; extra == "dev"
37
+ Requires-Dist: pip-audit>=2.7.0; extra == "dev"
38
+ Requires-Dist: libcst>=1.0.0; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ <div align="center">
42
+
43
+ <picture>
44
+ <source media="(prefers-color-scheme: dark)" srcset="scanner/web/static/logo.svg">
45
+ <img src="scanner/web/static/logo-light.svg" alt="frapAST Logo" width="96" height="96">
46
+ </picture>
47
+
48
+ # frapAST
49
+
50
+ ### Runtime-Proven Static Security and Performance Engine for Frappe and ERPNext
51
+
52
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.1.0-blue.svg)](https://pypi.org/project/frapast/)
53
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
54
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
55
+ [![Tests](https://img.shields.io/badge/tests-287%20passed-green.svg)](#test-suite-and-quality-assurance)
56
+ [![Rules](https://img.shields.io/badge/detectors-28%20active-blue.svg)](#rule-taxonomy)
57
+ [![Benchmark Speed](https://img.shields.io/badge/speed-16%2C700%2B%20files%2Fsec-green.svg)](#industry-benchmarks-and-performance)
58
+ [![Privacy](https://img.shields.io/badge/privacy-100%25%20Local%20and%20Airgapped-blue.svg)](#data-privacy-and-air-gapped-execution)
59
+
60
+ Find the vulnerability. Synthesize the reproducer. Prove it live. Ship the autofix.
61
+
62
+ [Quickstart](#quickstart) | [Industry Benchmarks](#industry-benchmarks-and-performance) | [Architecture Comparison](#architectural-comparison) | [Developer Experience](#developer-experience-dx) | [Rule Taxonomy](#rule-taxonomy) | [Engine Architecture](#how-it-works) | [Web Dashboard](#interactive-web-dashboard)
63
+
64
+ </div>
65
+
66
+ ---
67
+
68
+ ## Overview
69
+
70
+ Generic SAST tools analyze Frappe applications as generic Python scripts. Consequently, they fail to recognize that `@frappe.whitelist()` exposes a public HTTP endpoint, cannot resolve dynamic DocType schema permissions, miss string-literal dispatches such as `frappe.call("dotted.path")`, and generate significant false-positive noise.
71
+
72
+ frapAST is designed from the Frappe framework layer outward. It indexes DocType JSON schemas, parses `hooks.py`, walks module AST structures, and constructs an interprocedural static call graph modeling direct calls, string-based RPC dispatches, lifecycle hooks, dynamic document methods, and report execution entry points.
73
+
74
+ Candidate findings are scored across a multi-dimensional risk matrix and can be escalated through an Active Two-Tier Proof Engine that synthesizes standalone HTTP/RPC reproducers to verify exploitability against a running Frappe bench.
75
+
76
+ ---
77
+
78
+ ## Industry Benchmarks and Performance
79
+
80
+ Evaluated against open-source enterprise Frappe codebases on macOS (Apple Silicon) and Linux (x86_64) using Python 3.10 through 3.14:
81
+
82
+ ### 1. Throughput and Scalability
83
+ | Target Repository | Total Files | Code Volume | Scan Time | Indexing Throughput | Active Candidates |
84
+ |---|:---:|:---:|:---:|:---:|:---:|
85
+ | Synthetic Scale Corpus | 5,000 files | ~250,000 LOC | 0.30s | 16,728 files/sec | 0 |
86
+ | Frappe HRMS (`hrms`) | 670 files | ~155,000 LOC | 0.95s | 705 files/sec | 150 |
87
+ | ERPNext Core (`erpnext`) | 3,842 files | ~980,000 LOC | 5.82s | 660 files/sec | 684 |
88
+
89
+ ### 2. Empirical Detection Precision (Audited on Frappe HRMS)
90
+ | Rule Family | Detection Target | Verified TP | Audited Precision | Ground-Truth Outcome |
91
+ |---|---|:---:|:---:|---|
92
+ | `FR-PERM-001` | Mutating whitelisted RPC lacking permission checks | 19 / 19 | 100.0% | Identified unauthenticated write operations (mutating tier) |
93
+ | `FR-HOOK-001` | Controller `on_submit` without `on_cancel` | 6 / 7 | 85.7% | Identified uncancelled ledger and allocation records |
94
+ | `FR-HOOK-004` | Un-deduplicated `frappe.enqueue()` background jobs | 11 / 11 | 100.0% | Prevented duplicate background job queue storms |
95
+ | `FR-HOOK-006` | Bare `except:` statements swallowing framework signals | 3 / 3 | 100.0% | Prevented silent database transaction aborts in patches |
96
+ | `FR-DATA-001` | Non-existent DocType schema field access | 3 / 3 | 100.0% | Identified invalid field references in controllers |
97
+ | `FR-WKFL-003` | DocType `status` mutation without `docstatus` update | 4 / 4 | 100.0% | Identified state synchronization desyncs on submittables |
98
+ | `FR-INJ-001` | Single-dict parameter injection into `frappe.get_doc` | 5 / 5 | 100.0% | Identified mass-assignment risks in batch endpoints |
99
+ | `FR-PERF-001` | Database queries inside loops (N+1 query pattern) | 10 / 10 | 100.0% | Sampled batch loop database bottlenecks ($n=10$, seed=42) |
100
+ | `FR-PATH-001` | User-controlled file path traversal | Synthetic | Verified | Validated against synthetic `TALOS-2020-1091` reproductions |
101
+ | `FR-SQLI-001` | Script Report multi-hop dynamic SQL injection | Synthetic | Verified | Validated against synthetic `GHSA-745c-5q8r-vgj2` reproductions |
102
+
103
+ ---
104
+
105
+ ## Live-Bench Active Verification Case Study
106
+
107
+ frapAST includes live integration testing against local and staging Frappe benches. The Tier 2 proof engine generates and executes direct HTTP RPC reproducers to verify whether a static candidate is exploitable over the wire:
108
+
109
+ * **Unprotected RPC Endpoint (`frappe.client.get_time_zone`)**:
110
+ ```text
111
+ GET /api/method/frappe.client.get_time_zone HTTP/1.1 -> HTTP 200 OK
112
+ {"message":{"time_zone":"Asia/Kolkata"}}
113
+ [PROVEN]: Endpoint permits unauthenticated guest execution.
114
+ ```
115
+
116
+ * **Protected RPC Endpoint (`frappe.auth.get_logged_user`)**:
117
+ ```text
118
+ GET /api/method/frappe.auth.get_logged_user HTTP/1.1 -> HTTP 403 Forbidden
119
+ {"exc_type":"PermissionError","message":"You are not permitted to access this resource"}
120
+ [REFUTED]: Endpoint enforces authentication guard.
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Architectural Comparison
126
+
127
+ | Capability | Generic SAST (Bandit / SonarQube) | Cloud Static Scanners | frapAST |
128
+ |---|:---:|:---:|:---:|
129
+ | Frappe Framework Modeling | None (generic Python only) | Partial regex matching | Full (DocType JSONs, `hooks.py`, ORM, DocEvents, Reports) |
130
+ | Active Proof Verification | None (static alerts only) | None (static alerts only) | Two-Tier Active Proof (synthesizes live HTTP reproducers) |
131
+ | Data Privacy and Sovereignty | Depends on deployment | Source code sent to third-party cloud | 100% Local and Air-Gapped (runs on localhost, zero data egress) |
132
+ | Automated Remediation | None | Manual refactoring | CLI AST Autofix (`frapast fix` with diff previews) |
133
+ | Native Bench Integration | None | None | Native `bench frapast` CLI command group |
134
+ | CI/CD Pipeline Support | Generic exit codes | Proprietary webhooks | OASIS SARIF 2.1.0 and Reusable GitHub Composite Action |
135
+ | Indexing Speed | 100-300 files/sec | Queue-dependent | 16,700+ files/sec (sub-second local execution) |
136
+
137
+ ---
138
+
139
+ ## Data Privacy and Air-Gapped Execution
140
+
141
+ frapAST is engineered with strict data confidentiality:
142
+
143
+ 1. **Zero Data Egress**: All parsing, AST traversal, call graph generation, and proof execution run locally on `127.0.0.1`. No telemetry or source code is transmitted externally.
144
+ 2. **Confidentiality by Design**: No company names, client data, or proprietary identifiers are stored or required. All references in reports adhere strictly to open-source repository paths.
145
+ 3. **Local Origin Gating**: The web dashboard is bound exclusively to localhost and enforces origin headers against unauthorized cross-origin requests.
146
+
147
+ ---
148
+
149
+ ## Quickstart
150
+
151
+ ### Installation
152
+
153
+ ```bash
154
+ # Install via pip
155
+ pip install frapast
156
+
157
+ # Or install in editable mode for development
158
+ pip install -e .
159
+ ```
160
+
161
+ ### 1. Launch the Visual Dashboard
162
+ ```bash
163
+ frapast
164
+ # Automatically starts local server and opens http://localhost:7777
165
+ ```
166
+
167
+ ### 2. Run a CLI Security and Performance Audit
168
+ ```bash
169
+ # Perform static scan
170
+ frapast scan /path/to/frappe-app
171
+
172
+ # Perform scan with active bench verification
173
+ frapast scan /path/to/frappe-app --prove --bench-url http://localhost:8000
174
+
175
+ # Export SARIF 2.1.0 for GitHub Code Scanning
176
+ frapast scan /path/to/frappe-app --format sarif > results.sarif
177
+ ```
178
+
179
+ ### 3. Apply Automated Fixes via CLI
180
+ > **Note**: Automated code remediation is provided via the command-line interface (`frapast fix`) and the Python library. It is intentionally decoupled from the web dashboard REST API.
181
+
182
+ ```bash
183
+ # Preview AST modifications as unified diffs (dry-run)
184
+ frapast fix /path/to/frappe-app
185
+
186
+ # Apply modifications directly to source files
187
+ frapast fix /path/to/frappe-app --apply
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Developer Experience (DX)
193
+
194
+ ### 1. Native Frappe Bench CLI (`bench frapast`)
195
+ frapAST registers directly into Frappe's native `bench` CLI:
196
+
197
+ ```bash
198
+ # Run security and performance audit on an app
199
+ bench frapast audit my_custom_app
200
+
201
+ # Apply automated security patches
202
+ bench frapast fix my_custom_app --apply
203
+
204
+ # Verify live HTTP exploitability on an active bench site
205
+ bench frapast prove my_custom_app --site dev.local
206
+
207
+ # Diagnose bench connectivity and port availability
208
+ bench frapast check --port 8000
209
+ ```
210
+
211
+ ### 2. Reusable GitHub Action
212
+ Add `.github/workflows/frapast.yml` to your repository for automated PR security checks and SARIF code scanning annotations:
213
+
214
+ ```yaml
215
+ name: frapAST Security Audit
216
+
217
+ on:
218
+ push:
219
+ branches: [ main, develop ]
220
+ pull_request:
221
+ branches: [ main, develop ]
222
+
223
+ jobs:
224
+ security-audit:
225
+ runs-on: ubuntu-latest
226
+ steps:
227
+ - uses: actions/checkout@v4
228
+
229
+ - name: Run frapAST Audit
230
+ uses: pratheep-bit/frapast@main
231
+ with:
232
+ repo-path: '.'
233
+ fail-on: 'critical'
234
+ upload-sarif: 'true'
235
+ ```
236
+
237
+ ### 3. Automated Autofix Engine (`frapast fix`)
238
+ Automated AST patch generation handles routine security boilerplate:
239
+ - `FR-HOOK-001`: Automatically generates symmetrical `on_cancel(self)` methods for DocTypes implementing `on_submit`.
240
+ - `FR-HOOK-004`: Injects `deduplicate=True` into `frappe.enqueue()` calls.
241
+ - `FR-HOOK-006`: Replaces bare `except:` blocks with `except Exception:` to preserve framework execution signals.
242
+ - `FR-PERM-001`: Injects `frappe.only_for("System Manager")` permission checks into unguarded mutating endpoints.
243
+
244
+ ---
245
+
246
+ ## Rule Taxonomy
247
+
248
+ frapAST includes 28 active rule detectors and 2 explicitly disabled rules across core security and operational areas:
249
+
250
+ ```
251
+ Proof Basis:
252
+ Tier 2 : Live HTTP/RPC verification against a running Frappe bench
253
+ Tier 1 : Standalone local AST reproducer (independent of bench)
254
+ Static : Structural code pattern and dataflow verification
255
+ ```
256
+
257
+ ### Injection and Access Control
258
+ | Rule ID | Severity | Description | Proof Basis | Status |
259
+ |---|:---:|---|:---:|:---:|
260
+ | `FR-SQLI-001` | Critical | Dynamic `frappe.db.sql()` query with f-string or string concatenation lacking parameter bindings | Tier 2 | Validated (Synthetic GHSA-745c-5q8r-vgj2) |
261
+ | `FR-SQLI-002` | Critical | Raw SQL query referencing a submittable DocType table without a `docstatus` filter | Static | Validated |
262
+ | `FR-SQLI-003` | High | `frappe.db.set_value` invoked from whitelisted RPC bypassing controller `validate()` and `before_save()` hooks | Tier 2 | Validated |
263
+ | `FR-SQLI-004` | High | `frappe.qb.DocType()` or `frappe.qb.from_()` using request-controlled dynamic table identifiers | Tier 2 | Validated |
264
+ | `FR-INJ-001` | Critical | Request parameters unpacked directly into `frappe.get_doc(kwargs)` (mass assignment risk) | Tier 2 | Validated |
265
+ | `FR-INJ-002` | Critical | `eval()` or `exec()` called with request-controlled input reachable from whitelisted RPC | Tier 2 | Validated |
266
+ | `FR-INJ-005` | Disabled | `frappe.msgprint()` or `frappe.throw()` format strings | Static | Disabled (requires interprocedural taint analysis) |
267
+ | `FR-PATH-001` | High | User-controlled file path passed to file I/O operations without directory containment checks | Tier 2 | Validated (Synthetic TALOS-2020-1091) |
268
+ | `FR-SSRF-001` | High | User-controlled URL passed to outbound HTTP requests (`requests.get`, `urlopen`) with no allowlist | Tier 2 | Validated |
269
+ | `FR-CSRF-001` | High | Guest-accessible (`allow_guest=True`) endpoint performing state-changing database modifications | Tier 2 | Validated |
270
+
271
+ ### Authorization and Permission Enforcement
272
+ | Rule ID | Severity | Description | Proof Basis | Status |
273
+ |---|:---:|---|:---:|:---:|
274
+ | `FR-PERM-001` | High / Critical | `@frappe.whitelist()` endpoint lacking explicit permission validation (`has_permission`, `only_for`) | Tier 2 | Validated (100% on Mutating Tier) |
275
+ | `FR-PERM-002` | High | `ignore_permissions=True` reachable within one hop of an unguarded public whitelisted endpoint | Tier 2 | Validated |
276
+ | `FR-PERM-003` | High | `frappe.db.set_value` on an `if_owner`-scoped DocType bypassing owner permission enforcement | Tier 2 | Validated |
277
+ | `FR-PERM-004` | Medium | Report query bypassing DocType `permission_query_conditions` hooks | Static | Validated |
278
+ | `FR-PERM-005` | Medium | Internal SQL query bypassing DocType `has_permission` row-level security hooks | Static | Validated |
279
+ | `FR-PERM-006` | High | `frappe.db.set_value` on a child table DocType (`istable=1`) leaving parent document totals uncalculated | Static | Validated |
280
+
281
+ ### Framework Lifecycle and Workflow Integrity
282
+ | Rule ID | Severity | Description | Proof Basis | Status |
283
+ |---|:---:|---|:---:|:---:|
284
+ | `FR-HOOK-001` | Medium | Controller class defines `on_submit` but not `on_cancel` (missing reversal logic) | Tier 1 | Validated |
285
+ | `FR-HOOK-002` | Medium | Multiple applications registering conflicting handlers on the same `(doctype, event)` hook | Static | Validated |
286
+ | `FR-HOOK-003` | Medium | Whitelisted fast-path writing fields directly without validating lifecycle state transitions | Static | Validated |
287
+ | `FR-HOOK-004` | Medium | `frappe.enqueue()` invoked without deduplication keys, risking duplicate queue execution | Tier 1 | Validated |
288
+ | `FR-HOOK-005` | Low | `frappe.db.commit()` called within a lifecycle hook, breaking atomic transaction rollbacks | Tier 1 | Validated |
289
+ | `FR-WKFL-001` | Medium | `frappe.db.set_value` on submittable DocType without validating document draft status (`docstatus == 0`) | Static | Validated |
290
+ | `FR-WKFL-002` | Medium | Direct database write to `workflow_state` bypassing the Frappe workflow transition engine | Static | Validated |
291
+ | `FR-WKFL-003` | Medium | `status` updated without updating `docstatus` on submittable DocTypes | Tier 1 | Validated |
292
+ | `FR-WKFL-004` | Disabled | Amendment chain field leakage | Static | Disabled (natively handled by Frappe `no_copy=1`) |
293
+
294
+ ### Performance, Correctness and Reliability
295
+ | Rule ID | Severity | Description | Proof Basis | Status |
296
+ |---|:---:|---|:---:|:---:|
297
+ | `FR-PERF-001` | Low | `frappe.get_doc()` called inside a loop over query results (N+1 query bottleneck) | Tier 1 | Validated |
298
+ | `FR-HOOK-006` | Low | Bare `except:` block swallowing framework execution signals and exceptions | Tier 1 | Validated |
299
+ | `FR-HOOK-007` | Low | Mutable default argument (`[]`, `{}`) in function definition signature | Tier 1 | Validated |
300
+ | `FR-DATA-001` | Low | DocType field reference accessing a non-existent schema fieldname | Tier 1 | Validated |
301
+ | `FR-DATA-002` | Low | Missing `db.commit()` after asynchronous background processing state writes | Tier 1 | Validated |
302
+ | `FR-DATA-003` | Low | Raw database delete on parent document leaving orphan child table rows | Static | Validated |
303
+ | `FR-I18N-001` | Low | Hardcoded user-facing message string in `msgprint` or `throw` without `frappe._()` | Tier 1 | Validated |
304
+
305
+ ---
306
+
307
+ ## How It Works
308
+
309
+ ```
310
+ +-----------------+ +-----------------+ +-----------------+
311
+ | Schema Index | | Hook Index | | Python AST |
312
+ | (DocType JSON) | | (hooks.py AST) | | (Source Files) |
313
+ +--------+--------+ +--------+--------+ +--------+--------+
314
+ | | |
315
+ +---------------------+---------------------+
316
+ |
317
+ +--------v--------+
318
+ | Call Graph | 5 Edge Types:
319
+ | | - direct_call
320
+ | | - string_dispatch (frappe.call)
321
+ | | - hook_dispatch (doc_events)
322
+ | | - dynamic_method (get_doc.method)
323
+ | | - report_entry (report.execute)
324
+ +--------+--------+
325
+ |
326
+ +--------v--------+
327
+ | Rule Engine | 28 Active Detectors
328
+ +--------+--------+
329
+ |
330
+ +--------v--------+
331
+ | Severity Matrix | Multi-Dimensional Composite Scoring
332
+ +--------+--------+
333
+ |
334
+ +------------------+------------------+
335
+ | |
336
+ +--------v-------+ +--------v-------+
337
+ | CLI / SARIF | | Web Dashboard |
338
+ +--------+-------+ +--------+-------+
339
+ | |
340
+ +------------------+------------------+
341
+ |
342
+ +--------v--------+
343
+ | Proof Engine | Tier 0 (Static) -> Tier 1 (AST) -> Tier 2 (HTTP/RPC)
344
+ +--------+--------+
345
+ |
346
+ +--------v--------+
347
+ | Autofix Engine | CLI AST Code Patch Generator and Diff Viewer
348
+ +-----------------+
349
+ ```
350
+
351
+ 1. **Schema and Hook Indexing**: Parses all DocType JSON definitions (`fields`, `permissions`, `is_submittable`, `istable`) and `hooks.py` dispatch trees.
352
+ 2. **AST Parsing and Call Graph Construction**: Analyzes AST structures to extract whitelisted endpoints, database calls, parameters, string-literal dispatches (`frappe.call("dotted.path")`), and script report entry points (`execute(filters)`).
353
+ 3. **Composite Severity Matrix**: Computes severity using required privileges (guest vs authenticated user), impact classification, and blast radius.
354
+ 4. **Active Proof Verification**:
355
+ - **Tier 1 (AST Proof)**: Standalone verification programs executed locally.
356
+ - **Tier 2 (HTTP/RPC Proof)**: Authenticated HTTP requests executed by `FrappeHTTPClient` against a Frappe bench.
357
+ 5. **Autofix Engine**: Automatically generates and applies AST modifications via the CLI to eliminate manual boilerplate updates.
358
+
359
+ ---
360
+
361
+ ## Interactive Web Dashboard
362
+
363
+ Launch the local web dashboard:
364
+ ```bash
365
+ frapast
366
+ ```
367
+
368
+ Features provided in the web interface:
369
+ - **Real-Time Scan Streaming**: Server-Sent Events (SSE) update the dashboard as files are processed.
370
+ - **Visual Proof Drawer**: Inspect synthesized reproducers and view live verification outcomes.
371
+ - **Bench Connectivity Diagnostics**: Test connection to local Frappe bench instances with diagnostic reporting.
372
+ - **Persistent Storage**: SQLite with WAL mode preserves scan and proof history across application restarts.
373
+
374
+ ---
375
+
376
+ ## Test Suite and Quality Assurance
377
+
378
+ The codebase includes an automated test suite:
379
+
380
+ ```bash
381
+ pytest -v
382
+ ```
383
+
384
+ - **287 passed tests** covering AST visitors, call graph resolution, rule detectors, server security, reproducer synthesis, adversarial suppression handling, and autofix patches.
385
+ - **Security hardening**: Hardened shell script generation (`shlex.quote`), path traversal directory containment, and localhost origin gating.
386
+
387
+ ---
388
+
389
+ ## Contributing
390
+
391
+ Contributions are welcome. To report an architectural pattern or false-positive edge case:
392
+
393
+ 1. Fork the repository
394
+ 2. Create your feature branch (`git checkout -b feature/new-rule`)
395
+ 3. Run tests (`pytest`)
396
+ 4. Commit your changes and submit a Pull Request
397
+
398
+ ---
399
+
400
+ ## License
401
+
402
+ MIT (c) 2026 Frappe Security Scanner Contributors - see [LICENSE](LICENSE).