frapast 0.1.0__py3-none-any.whl

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 (141) hide show
  1. frapast-0.1.0.dist-info/METADATA +402 -0
  2. frapast-0.1.0.dist-info/RECORD +141 -0
  3. frapast-0.1.0.dist-info/WHEEL +5 -0
  4. frapast-0.1.0.dist-info/entry_points.txt +5 -0
  5. frapast-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. frapast-0.1.0.dist-info/top_level.txt +1 -0
  7. scanner/__init__.py +23 -0
  8. scanner/__main__.py +7 -0
  9. scanner/autofix/__init__.py +5 -0
  10. scanner/autofix/engine.py +313 -0
  11. scanner/autofix/models.py +21 -0
  12. scanner/bench_commands.py +85 -0
  13. scanner/callgraph/__init__.py +4 -0
  14. scanner/callgraph/builder.py +139 -0
  15. scanner/callgraph/models.py +74 -0
  16. scanner/cli.py +1213 -0
  17. scanner/config.py +69 -0
  18. scanner/fp/__init__.py +11 -0
  19. scanner/fp/engine.py +98 -0
  20. scanner/fp/models.py +30 -0
  21. scanner/fp_analyzer.py +69 -0
  22. scanner/hooks/__init__.py +12 -0
  23. scanner/hooks/engine.py +111 -0
  24. scanner/hooks/models.py +56 -0
  25. scanner/ledger_io.py +188 -0
  26. scanner/ledger_schema.py +33 -0
  27. scanner/logger.py +23 -0
  28. scanner/proof/__init__.py +22 -0
  29. scanner/proof/bench_runner.py +541 -0
  30. scanner/proof/http_client.py +347 -0
  31. scanner/proof/http_synthesis.py +641 -0
  32. scanner/proof/models.py +31 -0
  33. scanner/proof/orchestrator.py +826 -0
  34. scanner/proof/validate_reproducer_markers.py +112 -0
  35. scanner/python/__init__.py +43 -0
  36. scanner/python/engine.py +1172 -0
  37. scanner/python/models.py +305 -0
  38. scanner/regression/__init__.py +7 -0
  39. scanner/regression/generator.py +114 -0
  40. scanner/reporting/__init__.py +3 -0
  41. scanner/reporting/engine.py +144 -0
  42. scanner/reporting/formatters.py +8 -0
  43. scanner/reporting/sarif.py +83 -0
  44. scanner/rules/FR-CORR-001.yaml +7 -0
  45. scanner/rules/FR-CORR-002.yaml +7 -0
  46. scanner/rules/FR-CSRF-001.yaml +12 -0
  47. scanner/rules/FR-DATA-001.yaml +7 -0
  48. scanner/rules/FR-HOOK-001.yaml +7 -0
  49. scanner/rules/FR-HOOK-002.yaml +7 -0
  50. scanner/rules/FR-HOOK-003.yaml +7 -0
  51. scanner/rules/FR-HOOK-004.yaml +7 -0
  52. scanner/rules/FR-HOOK-005.yaml +7 -0
  53. scanner/rules/FR-HOOK-006.yaml +7 -0
  54. scanner/rules/FR-HOOK-007.yaml +7 -0
  55. scanner/rules/FR-I18N-001.yaml +7 -0
  56. scanner/rules/FR-INJ-001.yaml +7 -0
  57. scanner/rules/FR-INJ-002.yaml +7 -0
  58. scanner/rules/FR-INJ-005.yaml +11 -0
  59. scanner/rules/FR-PATH-001.yaml +7 -0
  60. scanner/rules/FR-PERF-001.yaml +7 -0
  61. scanner/rules/FR-PERM-001.yaml +7 -0
  62. scanner/rules/FR-PERM-002.yaml +7 -0
  63. scanner/rules/FR-PERM-003.yaml +7 -0
  64. scanner/rules/FR-PERM-004.yaml +7 -0
  65. scanner/rules/FR-PERM-005.yaml +7 -0
  66. scanner/rules/FR-PERM-006.yaml +7 -0
  67. scanner/rules/FR-SQLI-001.yaml +7 -0
  68. scanner/rules/FR-SQLI-002.yaml +7 -0
  69. scanner/rules/FR-SQLI-003.yaml +7 -0
  70. scanner/rules/FR-SQLI-004.yaml +7 -0
  71. scanner/rules/FR-SSRF-001.yaml +11 -0
  72. scanner/rules/FR-WKFL-001.yaml +7 -0
  73. scanner/rules/FR-WKFL-002.yaml +7 -0
  74. scanner/rules/FR-WKFL-003.yaml +7 -0
  75. scanner/rules/FR-WKFL-004.yaml +7 -0
  76. scanner/rules/FR-XSS-001.yaml +11 -0
  77. scanner/rules/__init__.py +3 -0
  78. scanner/rules/engine.py +1232 -0
  79. scanner/rules/schema.yaml +8 -0
  80. scanner/schema/__init__.py +19 -0
  81. scanner/schema/engine.py +148 -0
  82. scanner/schema/models.py +94 -0
  83. scanner/severity/__init__.py +4 -0
  84. scanner/severity/engine.py +199 -0
  85. scanner/severity/models.py +42 -0
  86. scanner/severity/taxonomy.py +8 -0
  87. scanner/shared/__init__.py +3 -0
  88. scanner/shared/records.py +31 -0
  89. scanner/suppression.py +274 -0
  90. scanner/taxonomy/FR-DATA-001.yaml +9 -0
  91. scanner/taxonomy/FR-DATA-002.yaml +9 -0
  92. scanner/taxonomy/FR-DATA-003.yaml +9 -0
  93. scanner/taxonomy/FR-HOOK-001.yaml +9 -0
  94. scanner/taxonomy/FR-HOOK-002.yaml +9 -0
  95. scanner/taxonomy/FR-HOOK-003.yaml +9 -0
  96. scanner/taxonomy/FR-HOOK-004.yaml +9 -0
  97. scanner/taxonomy/FR-HOOK-005.yaml +9 -0
  98. scanner/taxonomy/FR-INJ-001.yaml +9 -0
  99. scanner/taxonomy/FR-INJ-002.yaml +9 -0
  100. scanner/taxonomy/FR-INJ-003.yaml +9 -0
  101. scanner/taxonomy/FR-INJ-004.yaml +9 -0
  102. scanner/taxonomy/FR-PERM-001.yaml +10 -0
  103. scanner/taxonomy/FR-PERM-002.yaml +9 -0
  104. scanner/taxonomy/FR-PERM-003.yaml +9 -0
  105. scanner/taxonomy/FR-PERM-004.yaml +9 -0
  106. scanner/taxonomy/FR-PERM-005.yaml +9 -0
  107. scanner/taxonomy/FR-PERM-006.yaml +9 -0
  108. scanner/taxonomy/FR-SQLI-001.yaml +10 -0
  109. scanner/taxonomy/FR-SQLI-002.yaml +9 -0
  110. scanner/taxonomy/FR-SQLI-003.yaml +9 -0
  111. scanner/taxonomy/FR-SQLI-004.yaml +9 -0
  112. scanner/taxonomy/FR-WKFL-001.yaml +9 -0
  113. scanner/taxonomy/FR-WKFL-002.yaml +9 -0
  114. scanner/taxonomy/FR-WKFL-003.yaml +9 -0
  115. scanner/taxonomy/FR-WKFL-004.yaml +9 -0
  116. scanner/taxonomy/taxonomy_registry.yaml +75 -0
  117. scanner/ui/__init__.py +27 -0
  118. scanner/ui/banner.py +88 -0
  119. scanner/ui/flow.py +457 -0
  120. scanner/ui/menus.py +245 -0
  121. scanner/ui/progress.py +70 -0
  122. scanner/ui/results.py +330 -0
  123. scanner/ui/shell.py +446 -0
  124. scanner/ui/theme.py +73 -0
  125. scanner/validate/__init__.py +3 -0
  126. scanner/validate/engine.py +72 -0
  127. scanner/validate_ledger.py +37 -0
  128. scanner/validate_taxonomy.py +74 -0
  129. scanner/verify_ledger_integrity.py +69 -0
  130. scanner/web/__init__.py +1 -0
  131. scanner/web/db.py +351 -0
  132. scanner/web/server.py +1252 -0
  133. scanner/web/static/app.js +1790 -0
  134. scanner/web/static/index.html +468 -0
  135. scanner/web/static/logo-dark-mark.png +0 -0
  136. scanner/web/static/logo-light-mark.png +0 -0
  137. scanner/web/static/logo-light.png +0 -0
  138. scanner/web/static/logo-light.svg +6 -0
  139. scanner/web/static/logo.png +0 -0
  140. scanner/web/static/logo.svg +5 -0
  141. scanner/web/static/style.css +2194 -0
@@ -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).
@@ -0,0 +1,141 @@
1
+ frapast-0.1.0.dist-info/licenses/LICENSE,sha256=EZ122ZWAhHmbYLthqzf6pdhYCKOBTlLQGg2uengKzT0,1093
2
+ scanner/__init__.py,sha256=wG2fR912n3vnMJCJjAwZohmGSDtWnq6A11ZdUtslR94,480
3
+ scanner/__main__.py,sha256=1VjVH6HhgkNukO0F7tkM5YrWFEA3MZOZ1iFHi9TFm0o,154
4
+ scanner/bench_commands.py,sha256=4B3RpL7Ggg7RpgXseAPKJtsxBTUkDcZPDqYroRLxB5Y,3138
5
+ scanner/cli.py,sha256=NZjaJmjTTHc4Fr0trA9eEJokfjnsBGh33XjgEKYTRa8,46557
6
+ scanner/config.py,sha256=wv69UhgAMeFV00ML_Tkn2gYSiL0U2q479CQSnux05as,1945
7
+ scanner/fp_analyzer.py,sha256=6tFXheWBfG29BLK0dZUxBWQPHf6x1J94AWc-LbG49Fs,2398
8
+ scanner/ledger_io.py,sha256=1RYpWk-qWBm_Whzu3HujbAlVG-H75_-6UP7t38LPnOc,6554
9
+ scanner/ledger_schema.py,sha256=2o6PoXq91_aWf0YvvRzBblug6JEin38VIuKYgMLSdVA,1284
10
+ scanner/logger.py,sha256=PS-n_9cob3XdFIy3dW1AXRKVCXB5aq3i73LguyEzU-M,618
11
+ scanner/suppression.py,sha256=_Y13D7EdkxqmRuJIfY37VfXk8TNA33WtmITBMUQZlEk,9561
12
+ scanner/validate_ledger.py,sha256=jLBqbzuhmVwfdHOxvyz6shRQhy5knrQ3buApW0BcQTc,975
13
+ scanner/validate_taxonomy.py,sha256=6i51KkTXCXkKp4M4deMUYmFRps7WOjMxsXxz42TFs7k,2799
14
+ scanner/verify_ledger_integrity.py,sha256=nKnqUaC8BsLU5bc-Q6nNlyAwe8-IF9ofpR2c_oz5Jw4,2006
15
+ scanner/autofix/__init__.py,sha256=FszLp1CINiPSyI75R6VSIDRgGyfIlJP17IVZ-B-jsLI,157
16
+ scanner/autofix/engine.py,sha256=86UfhNYMohR6YV0SqAd8drcqR4y-Ndo2iojgp8gIUz0,11133
17
+ scanner/autofix/models.py,sha256=aEBE96PXkXKWq2VjlKPXA3oVGOKYrPvObchUMO2HcGQ,468
18
+ scanner/callgraph/__init__.py,sha256=saN1uW6CFpp5KOIh3mYtoTfG9rl_dfD5DzFo5y63ZPw,191
19
+ scanner/callgraph/builder.py,sha256=afHjsZysJwrvPxHYCuEbBYz0yEEaXZWpn7DxVgKbsJc,5617
20
+ scanner/callgraph/models.py,sha256=LKTAKxtehxhymoJIFifdb03nKCoLPVRoa0SNPj3gAUk,2492
21
+ scanner/fp/__init__.py,sha256=nUTZw9Z6Y67Evow5Uwr9uZPP3-I02gK5qp8CDSofGos,331
22
+ scanner/fp/engine.py,sha256=a2ZEQGGOsLrgwAjL-JP04awg7S2lhvBBZzhXTT4Mbug,3511
23
+ scanner/fp/models.py,sha256=7GAoWBS-jLyEL1tGJ9a-7aVWrNxYUXi6Gj4zPuLt_Fc,514
24
+ scanner/hooks/__init__.py,sha256=sZ_UXsY3ni4k3swMFszGdTj43jjCEQuuXGVjShvlze8,326
25
+ scanner/hooks/engine.py,sha256=S9H3cPzZEJvt5KYxAD_mWszMWXVrJuREaQJg_isAh8Q,3884
26
+ scanner/hooks/models.py,sha256=S6gPhQeLGwiwEyK6XXpPSuuD_RGSyxhOoATjrw37jTk,1560
27
+ scanner/proof/__init__.py,sha256=GnyNLMopiH-6LGWJGyGMTP8vWZP5PE8VhhyQl3HWh1k,531
28
+ scanner/proof/bench_runner.py,sha256=mqod21dxLIjQsFMykm-g64fDQ-7R_PUNa7Iyeeldjk0,23807
29
+ scanner/proof/http_client.py,sha256=YCLP9b5FJPNCRAJ70IU21JhKmirGGQ2Y48YV_WEPWZY,13032
30
+ scanner/proof/http_synthesis.py,sha256=blwhd9BE-rmJTRuU0iTjlISNJIVYc4qFtK0du0dqN8s,26203
31
+ scanner/proof/models.py,sha256=HnkJp8UqoW5SHaEfmIMQrCEISJxgWVNEBJ98mAisT6Q,661
32
+ scanner/proof/orchestrator.py,sha256=jDzy6U1WHfkAgE8ZyWG-2akryFX4dgdPY5GG5jPH_qA,29919
33
+ scanner/proof/validate_reproducer_markers.py,sha256=fIZoWZ0uCtve623gfpE7Tz5YhBUyYf9n-DHm6FvGaN8,3792
34
+ scanner/python/__init__.py,sha256=aczlaB_mP7-Oefsavt_-C8H1MW-WKFdGb_LyA21NyUE,875
35
+ scanner/python/engine.py,sha256=0EhEMTHqQNfIHQo-PqGL8NdqkCmlfqi2O8KjeIF7IXU,47296
36
+ scanner/python/models.py,sha256=Xy7Q1DEfVc4zxzCCOKTPpL94w49pd-B9jwvP0TTfbT4,6411
37
+ scanner/regression/__init__.py,sha256=m-rhcdBlv3g4lvqiwuQdivMlUfYSPM9b7BWQCSQmTXQ,240
38
+ scanner/regression/generator.py,sha256=NXJfq26HPFKtd_eZpqV74d7rlStdQPTIClSIiLJq9Ak,3692
39
+ scanner/reporting/__init__.py,sha256=K82WGCTVneKzIXwDYHtvUwaEPxrdTa1n1RPD7O26VmU,92
40
+ scanner/reporting/engine.py,sha256=tjER7tM7TwCGUaIvAzLSi6bQQLCLE48CweiphjlLrVU,4938
41
+ scanner/reporting/formatters.py,sha256=KMM8YNw9rTZcfBTBcSfrDpT8d-udPuYtF9Fi5nEAJZ0,153
42
+ scanner/reporting/sarif.py,sha256=Iu2PfpmpnK8KEtLokzBumOU2ugx6gNDYTjUO4pcsSZ8,2123
43
+ scanner/rules/FR-CORR-001.yaml,sha256=IZMbAo-kygIqaDdX0hGvCDuXKHre-JBPIU8u5REv9NY,270
44
+ scanner/rules/FR-CORR-002.yaml,sha256=oiyVgf4stg_2-dHA23Zcbg6zcP9BTBZ-veiGn3sMe-s,260
45
+ scanner/rules/FR-CSRF-001.yaml,sha256=O8V5OGaDu0YWp5Ba8cJAU1GLkS9_UW8XSFuIeq3O0Zs,517
46
+ scanner/rules/FR-DATA-001.yaml,sha256=tjVIvgdxOGlGh972qgGcxBLpjQzy4zkJIEtisZjFdmo,278
47
+ scanner/rules/FR-HOOK-001.yaml,sha256=P4g1KylupXfHN8fHybegwlz5f9mR4gYKsLGePgNSafw,308
48
+ scanner/rules/FR-HOOK-002.yaml,sha256=4w0KNxypZ8Z9OBFyAjMESqJ1zcOW3pAE5g9Xum7LFz8,285
49
+ scanner/rules/FR-HOOK-003.yaml,sha256=nF-A0PHp6M1CzS7GMHu4jydYBh2mybr5EeOVWDftOlk,340
50
+ scanner/rules/FR-HOOK-004.yaml,sha256=nknVcDIwEj3M7sFkXIfnB1FlQIZbmGcFOqEwTuJ0nVI,300
51
+ scanner/rules/FR-HOOK-005.yaml,sha256=t_wTzznX3BrhWzQosK85O5wXlvk7r4zk5HNgs6xn9EA,307
52
+ scanner/rules/FR-HOOK-006.yaml,sha256=DyWIJKfRk0s03QIajQ7vBt8dmnhVxnew6jGfpa-rhfM,270
53
+ scanner/rules/FR-HOOK-007.yaml,sha256=R8rTW4hCTctzqSzsSL1_3VpE6QnGc0P8n-00J3-mj38,260
54
+ scanner/rules/FR-I18N-001.yaml,sha256=5_5GLeEQP5OzYnUNInHCN2CGF65YEfnd4ITQ0cZGvWY,297
55
+ scanner/rules/FR-INJ-001.yaml,sha256=1uWXLBVLxeaazGIQGbRSNE7-cJfw4XLo2VoJRC1u9sA,342
56
+ scanner/rules/FR-INJ-002.yaml,sha256=lXQ6IH_iXVTMvIva0w5EOJ7-lW-y7ssPKs3mVblT9gM,342
57
+ scanner/rules/FR-INJ-005.yaml,sha256=9wbhnTxA8puCYJcmVW6rpBWONWlTNRoqqnZ_tl1W6Tc,443
58
+ scanner/rules/FR-PATH-001.yaml,sha256=CwZAKea4daa7hkA09xtT-lJvs0ZeWnAb6QaJ8Pq5DVM,402
59
+ scanner/rules/FR-PERF-001.yaml,sha256=H1dgh9pt3oQAGhegrey_e2JUd03tGM6-xFjD6b0SKgU,284
60
+ scanner/rules/FR-PERM-001.yaml,sha256=6iMELJtCHj_CAUWiNqYGQTL7bGKSrXpKbJJuEcU9QKs,340
61
+ scanner/rules/FR-PERM-002.yaml,sha256=8kt3rfBzV63xUVqjROL6A9y-t0Ekng7hdvA8IKxSJyo,345
62
+ scanner/rules/FR-PERM-003.yaml,sha256=5uTSpcjLVXA2HXnFMiGwlbnuSJK7FwKJx5QXGVz_LHI,335
63
+ scanner/rules/FR-PERM-004.yaml,sha256=_ojgIqKmuEdS8D33V2KrWmg3E7ABT2y7Uzv0IyQzFl4,338
64
+ scanner/rules/FR-PERM-005.yaml,sha256=DKHMszdCyYs25KMfW1toHcM4JPaUdrNjIip5aJukL3I,355
65
+ scanner/rules/FR-PERM-006.yaml,sha256=Ffe7Hphkvg85owuUfCj9xRCZgCXGWUrSkDMRoZ9Igoo,339
66
+ scanner/rules/FR-SQLI-001.yaml,sha256=gEdjvclHIzkbWexv4kRPC6G-ju0NWIUax8nIsN6WgYM,295
67
+ scanner/rules/FR-SQLI-002.yaml,sha256=T58NGHfwkedL0I0NE_ZbhiX8_94G0EGsIinvSlYsPvY,334
68
+ scanner/rules/FR-SQLI-003.yaml,sha256=JK0RN9gPEdxR--fhiMt7E448bDJ-sFegIvFwZv7IsU4,348
69
+ scanner/rules/FR-SQLI-004.yaml,sha256=ahEjp9lD9Ws2GgeJ985ip7I-l1sx5Z-UdO2BmouTuT8,331
70
+ scanner/rules/FR-SSRF-001.yaml,sha256=RQkJUgaV_ZApjmckcLRlGsAjBmS9C_nMl9YHcMGNzDA,429
71
+ scanner/rules/FR-WKFL-001.yaml,sha256=VbuR-zmx8MHOcnhJHFFIcWWuwIoa2aeKWPro2RTgJGY,363
72
+ scanner/rules/FR-WKFL-002.yaml,sha256=x3R0u5wC-eRyOsXvr4bAFsfudyMLWBf-ztKD4iadKm0,342
73
+ scanner/rules/FR-WKFL-003.yaml,sha256=Rn9XKJ3zuLbvB1v58dOgjwYXhLIxbTZ2GNu-Zn6DrA0,306
74
+ scanner/rules/FR-WKFL-004.yaml,sha256=fLxhtor3Z39TLRwrSGi2MJiG8wuvPIQhyLwC6j-_YMY,330
75
+ scanner/rules/FR-XSS-001.yaml,sha256=_lRngJmMOT-uea1U2f54RM2fktF6lRPMKw552xAa-I8,443
76
+ scanner/rules/__init__.py,sha256=SvKySOBVbPTN7l3CB9odAtSDt6HyDE6siQ8yK3cr0rA,164
77
+ scanner/rules/engine.py,sha256=Z0NLDmfTPgCR-x9Bbp2Y8V-DTG_bQYOqmxXlwVLnsyc,47233
78
+ scanner/rules/schema.yaml,sha256=qhqfBehZjCgkaQdFmxaTHhV4cSlulub-bX1obEDeim8,127
79
+ scanner/schema/__init__.py,sha256=26gGVHyqmnrnGGWUJiIkP2uTAy6B0kUN8hG73FqoiZM,367
80
+ scanner/schema/engine.py,sha256=JIsisBkuC7X-lf4_RrdY8A6jSeuUKTUeSafMXBwyJhE,4943
81
+ scanner/schema/models.py,sha256=ldE_AUUEwoNTw0_gmMuTNew0dZTP0-2qEz1kEdhmj5w,2853
82
+ scanner/severity/__init__.py,sha256=cVzTzbmi1xZI7g7B33zvMOvRUXR7D-5U9GRqdQksNYs,188
83
+ scanner/severity/engine.py,sha256=mLbXIU8iDXbf9qE6NaxocIy0bw6jbIuBa4st1lu4uR4,7465
84
+ scanner/severity/models.py,sha256=V_1Tc3zBAyOinpb6un2pNQMN7B30ckbOuArW3yaAiJA,685
85
+ scanner/severity/taxonomy.py,sha256=5n9ZaR-4VElyhpzeNnd6Mh83dqeYsC98vHfkGLiZlj8,296
86
+ scanner/shared/__init__.py,sha256=GEC3zaWczhz1J9wCxDsxIaHOCmWfMQzCBK8ud1uf2cA,126
87
+ scanner/shared/records.py,sha256=P52uPlOt_kPd5aNHhEWjK931z8vPpl3rKolitJ0D8ik,580
88
+ scanner/taxonomy/FR-DATA-001.yaml,sha256=HMWsoV4KtF5aUuclZWy0BQe07KBQxN0farQStTITstw,283
89
+ scanner/taxonomy/FR-DATA-002.yaml,sha256=PGbwKf3_Vq4XXW8ry8i1HTlnqyvZOvuRmHosA224e9E,248
90
+ scanner/taxonomy/FR-DATA-003.yaml,sha256=YalgCiKAukvDZ57XeKBOtmfcItj4LS85blXFiYWAqMU,243
91
+ scanner/taxonomy/FR-HOOK-001.yaml,sha256=US-HBCVJS8SQxP3YF0NCRrxbLYuogFZ6d_Ms_VvFO5M,250
92
+ scanner/taxonomy/FR-HOOK-002.yaml,sha256=s8KUagTXlbxJbekvXs8-rMyjqhNmTNi5O5aflh5jfaU,273
93
+ scanner/taxonomy/FR-HOOK-003.yaml,sha256=M-iGlCyW8G1IULoI1klWl953VViXX0_bsFFGL9UBz_s,273
94
+ scanner/taxonomy/FR-HOOK-004.yaml,sha256=IdSvkoTEmspo5bBPGITrS5XqiN8DB5ZR0XoaqoXc_2Y,275
95
+ scanner/taxonomy/FR-HOOK-005.yaml,sha256=hnh9OHlJ_p7q177mX-I8yAiihEKMReIS9cblnIwBHWA,273
96
+ scanner/taxonomy/FR-INJ-001.yaml,sha256=QY1LlNH3Y9PLZ-LwIxf0u4vdazOEvJi_bO-oIyQ5wWU,266
97
+ scanner/taxonomy/FR-INJ-002.yaml,sha256=1GCTuZndQLxk8aPZDvw75tT5BSusPO2aZZ93GcudSUc,255
98
+ scanner/taxonomy/FR-INJ-003.yaml,sha256=rEih6DiHS0yU5lGjlHlXmc5-_Ylfkl2iwkDHucJSCEc,240
99
+ scanner/taxonomy/FR-INJ-004.yaml,sha256=jaSMirbUa2jv5sVr0Sd43Z7i_DOHFjXUU7BHFBZIDcA,269
100
+ scanner/taxonomy/FR-PERM-001.yaml,sha256=XBKv7Ic-9TA6Kg6MsIgk5AqYAwt2xNiJEvg2QB5halk,362
101
+ scanner/taxonomy/FR-PERM-002.yaml,sha256=qGYJnIuP4gOMJGsBNTLvoxA57wI1mG052-4s0kodq4M,307
102
+ scanner/taxonomy/FR-PERM-003.yaml,sha256=QYEaD5cTwC141njnkrGDmJDlyjQPkJwF2wEFuu46Jlk,283
103
+ scanner/taxonomy/FR-PERM-004.yaml,sha256=jX4WFUudQryffjQpry6KvEjVFJlw_0iRNVRDL6VfUGs,257
104
+ scanner/taxonomy/FR-PERM-005.yaml,sha256=wsm6fcVkb5El8iZNuH3weca8MReB0WHp8cSUgmhHniA,252
105
+ scanner/taxonomy/FR-PERM-006.yaml,sha256=XzT1hvGs3Zq4fJDTDrxCHzusojs5W1LZ0yw25D8o-Co,283
106
+ scanner/taxonomy/FR-SQLI-001.yaml,sha256=aOuO3z1dpernX8fskzcYCrFGQ5httxiBJPeiE4nzZ6w,341
107
+ scanner/taxonomy/FR-SQLI-002.yaml,sha256=L9x3SG55w-zq_fgtNTVHnVU-gQievVV3x75Cb5uX0KY,271
108
+ scanner/taxonomy/FR-SQLI-003.yaml,sha256=X9gXTBdTiRtz-UzeaPS5c9YC49h2wWP4jBACynPYsWs,273
109
+ scanner/taxonomy/FR-SQLI-004.yaml,sha256=zDAm_ISJqDlAGjg0f1SmKq89AtsUIvhu7KmaAb3Pk3o,244
110
+ scanner/taxonomy/FR-WKFL-001.yaml,sha256=pAtjPoDXL7U0EEztWcp5DZZx6fle4BBAFf8e4DBh5FA,253
111
+ scanner/taxonomy/FR-WKFL-002.yaml,sha256=6-p_tUOMnlJ28yEtMyEdmb-pzf9E5gWZnsr8F5EsQSs,261
112
+ scanner/taxonomy/FR-WKFL-003.yaml,sha256=qvAex5EZyja88JllxGIftHUKzjOsH8K9qy-VoiyUg24,265
113
+ scanner/taxonomy/FR-WKFL-004.yaml,sha256=pbUBntwkhWEZYTOhSE_cuAGN-LgL1DTxONpZ2hAowX4,252
114
+ scanner/taxonomy/taxonomy_registry.yaml,sha256=p6u7tvRZBIvnxHJ53T-zSLSdOG-dCx0W7bckvvTAZHo,3418
115
+ scanner/ui/__init__.py,sha256=HhvKaE82x7dT7cP15P_-T7VbqN6xUYqylSvSu6p68Ps,623
116
+ scanner/ui/banner.py,sha256=1iYdnin9xh1yXsRXIHjNHuK7VSMoUn6MJ-Zi-7opHO8,2658
117
+ scanner/ui/flow.py,sha256=4NKkCy6FNC4nftFwzYEvWBm63O5LYnsKtep93mpvocY,16075
118
+ scanner/ui/menus.py,sha256=VqYZlkt8L_eXtZhFw_SPTw3BdFfbF1kCOEEg-s1V8g0,7616
119
+ scanner/ui/progress.py,sha256=GHVlmpocK9yuUXZeojv3Zdpg_kcc23ZkXaGL-VhQRj0,2071
120
+ scanner/ui/results.py,sha256=BOwbjZYyyLEM59Lo65jKsfDYfbJVnV8lLvsXxyt_B5o,12793
121
+ scanner/ui/shell.py,sha256=2jt_P690ToO3KJN1DSiyImi1nupxGsfVl1mHuoIRHOo,15548
122
+ scanner/ui/theme.py,sha256=0DSTOGQ12vwjTl3OiIdiquw75qnOrhz691fElU7I6zc,2144
123
+ scanner/validate/__init__.py,sha256=mLvo2IH-hS-Z2jgeiNiRl9F0bq4FAJZPgPqg802LYk4,89
124
+ scanner/validate/engine.py,sha256=1-WHJia1DV0psOi2eDgbRhDl5zP2fk8mGqQs_wVsyyo,2855
125
+ scanner/web/__init__.py,sha256=jwz7uT8QRMBLLfzUEe9EFTJcWcHysTbu0oU5a-vi1Mw,30
126
+ scanner/web/db.py,sha256=BW4Ed-qD62E_h48DVHwEhAfVZFgmmFVhvrlXTkwzfX0,12220
127
+ scanner/web/server.py,sha256=sDOHFZ2BYkFOdXn_RF5v3JY0POTjgm4g39SSMjJL-mo,50476
128
+ scanner/web/static/app.js,sha256=qztv4VXfrP98UoQ1bkJ3f0S2GQqvdj2BHt5eSFqvGaw,68450
129
+ scanner/web/static/index.html,sha256=k-XU0oL3tpXyxHRz5DYSwpVyRrWt-2uqY6GBcYKdt14,27528
130
+ scanner/web/static/logo-dark-mark.png,sha256=7h9ttJfSHW7-wjWXO3v2VMlTx5uqVN9kqrqmtyp2tCI,7859
131
+ scanner/web/static/logo-light-mark.png,sha256=MgWAB9GdWqQNCwsl3vvYpxoFUTONsA6vZfu7kmtRWkY,7864
132
+ scanner/web/static/logo-light.png,sha256=B4ibn91RnHKCCsAvMY_u2CO1l50CUcFZIVGs-Y98kyk,7965
133
+ scanner/web/static/logo-light.svg,sha256=zT6hNBOjk5nNLsuDBmn7XCsRH8kdgwPBAeedO6vnB5Q,10989
134
+ scanner/web/static/logo.png,sha256=i89aaoMgBTjI2R_l0qzICjzZb1gY6v_3m7JiXZyMt2k,24072
135
+ scanner/web/static/logo.svg,sha256=B8l4-FBLzOAFdMXtaJrftJ5ppwjyTLdmPJ1VSmS8igY,32353
136
+ scanner/web/static/style.css,sha256=U4VxmuG1tsOHOARm3xab9c789nzTFMpzNAcQqTixMNk,50523
137
+ frapast-0.1.0.dist-info/METADATA,sha256=s3gfN7WtvjFGNyEa4Jab5tWVKy0Kg-i-Gr0nmP5eOcg,21231
138
+ frapast-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
139
+ frapast-0.1.0.dist-info/entry_points.txt,sha256=MOYj6MVv49N55bpKDSZ3rTjbqo7Y9YBGQWw_KJB7SoU,106
140
+ frapast-0.1.0.dist-info/top_level.txt,sha256=uVdh7ZIJC9rjZQex7pba18nBuRRxFhjkFu4vHPiWTcw,8
141
+ frapast-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ frapast = scanner.cli:main
3
+
4
+ [frappe.commands]
5
+ frapast = scanner.bench_commands:commands
@@ -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.
@@ -0,0 +1 @@
1
+ scanner
scanner/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ try:
2
+ from importlib.metadata import version
3
+ __version__ = version("frapast")
4
+ except Exception:
5
+ __version__ = "0.1.0"
6
+
7
+ from scanner.cli import scan, scan_multi
8
+ from scanner.config import RepoConfig, ScanConfig, load_config
9
+ from scanner.logger import logger
10
+ from scanner.rules import Candidate, clear_rule_caches, execute_rules
11
+
12
+ __all__ = [
13
+ "Candidate",
14
+ "RepoConfig",
15
+ "ScanConfig",
16
+ "clear_rule_caches",
17
+ "execute_rules",
18
+ "load_config",
19
+ "logger",
20
+ "scan",
21
+ "scan_multi",
22
+ ]
23
+
scanner/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Main package entrypoint for running via `python -m scanner`."""
2
+ import sys
3
+
4
+ from scanner.cli import main
5
+
6
+ if __name__ == "__main__":
7
+ sys.exit(main())
@@ -0,0 +1,5 @@
1
+ """scanner/autofix package."""
2
+ from scanner.autofix.engine import FixEngine
3
+ from scanner.autofix.models import FixPatch
4
+
5
+ __all__ = ["FixEngine", "FixPatch"]