flawed 0.0.1__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 (66) hide show
  1. flawed-0.0.1/.gitignore +12 -0
  2. flawed-0.0.1/PKG-INFO +4 -0
  3. flawed-0.0.1/README.md +25 -0
  4. flawed-0.0.1/docs/architecture-overview.md +131 -0
  5. flawed-0.0.1/docs/architecture.md +330 -0
  6. flawed-0.0.1/docs/boundaries.md +294 -0
  7. flawed-0.0.1/docs/code-index.md +708 -0
  8. flawed-0.0.1/docs/principles.md +274 -0
  9. flawed-0.0.1/docs/rule-api.md +351 -0
  10. flawed-0.0.1/docs/semantic-layer.md +653 -0
  11. flawed-0.0.1/docs/tooling-survey.md +674 -0
  12. flawed-0.0.1/examples/cve/README.md +8 -0
  13. flawed-0.0.1/examples/oop_style/README.md +123 -0
  14. flawed-0.0.1/examples/oop_style/base.py +359 -0
  15. flawed-0.0.1/examples/oop_style/rules.py +296 -0
  16. flawed-0.0.1/examples/oop_style/showcase.py +247 -0
  17. flawed-0.0.1/examples/ucl/README.md +8 -0
  18. flawed-0.0.1/npm/index.js +4 -0
  19. flawed-0.0.1/npm/package.json +12 -0
  20. flawed-0.0.1/pyproject.toml +42 -0
  21. flawed-0.0.1/src/flawed/__init__.py +43 -0
  22. flawed-0.0.1/src/flawed/calls.py +202 -0
  23. flawed-0.0.1/src/flawed/checks.py +166 -0
  24. flawed-0.0.1/src/flawed/collections.py +317 -0
  25. flawed-0.0.1/src/flawed/conditions.py +120 -0
  26. flawed-0.0.1/src/flawed/core.py +127 -0
  27. flawed-0.0.1/src/flawed/detector.py +52 -0
  28. flawed-0.0.1/src/flawed/effects.py +258 -0
  29. flawed-0.0.1/src/flawed/evidence.py +125 -0
  30. flawed-0.0.1/src/flawed/flow.py +148 -0
  31. flawed-0.0.1/src/flawed/function.py +224 -0
  32. flawed-0.0.1/src/flawed/inputs.py +305 -0
  33. flawed-0.0.1/src/flawed/repo.py +92 -0
  34. flawed-0.0.1/src/flawed/route.py +203 -0
  35. flawed-0.0.1/src/flawed/scopes.py +204 -0
  36. flawed-0.0.1/tests/__init__.py +0 -0
  37. flawed-0.0.1/tests/fixtures/apps/classes/models.py +43 -0
  38. flawed-0.0.1/tests/fixtures/apps/decorators/app.py +53 -0
  39. flawed-0.0.1/tests/fixtures/apps/flask_basic/app.py +47 -0
  40. flawed-0.0.1/tests/fixtures/apps/functions/helpers.py +11 -0
  41. flawed-0.0.1/tests/fixtures/apps/functions/main.py +39 -0
  42. flawed-0.0.1/tests/fixtures/apps/imports/__init__.py +1 -0
  43. flawed-0.0.1/tests/fixtures/apps/imports/helpers.py +13 -0
  44. flawed-0.0.1/tests/fixtures/apps/imports/main.py +16 -0
  45. flawed-0.0.1/tests/fixtures/apps/minimal/app.py +8 -0
  46. flawed-0.0.1/tests/specs/__init__.py +0 -0
  47. flawed-0.0.1/tests/specs/basics/__init__.py +0 -0
  48. flawed-0.0.1/tests/specs/basics/test_call_graph.py +71 -0
  49. flawed-0.0.1/tests/specs/basics/test_cfg.py +50 -0
  50. flawed-0.0.1/tests/specs/basics/test_class_discovery.py +65 -0
  51. flawed-0.0.1/tests/specs/basics/test_decorator_discovery.py +57 -0
  52. flawed-0.0.1/tests/specs/basics/test_function_discovery.py +184 -0
  53. flawed-0.0.1/tests/specs/basics/test_imports_resolution.py +36 -0
  54. flawed-0.0.1/tests/specs/basics/test_scoped_queries.py +88 -0
  55. flawed-0.0.1/tests/specs/basics/test_value_flow.py +78 -0
  56. flawed-0.0.1/tests/specs/conftest.py +8 -0
  57. flawed-0.0.1/tests/specs/detection/__init__.py +0 -0
  58. flawed-0.0.1/tests/specs/exploration/__init__.py +0 -0
  59. flawed-0.0.1/tests/unit/__init__.py +0 -0
  60. flawed-0.0.1/tests/unit/test_detector.py +24 -0
  61. flawed-0.0.1/tests/unit/test_enums.py +68 -0
  62. flawed-0.0.1/tests/unit/test_evidence.py +58 -0
  63. flawed-0.0.1/tests/unit/test_imports.py +187 -0
  64. flawed-0.0.1/tests/unit/test_instantiation.py +175 -0
  65. flawed-0.0.1/tests/unit/test_selectors.py +228 -0
  66. flawed-0.0.1/tests/unit/test_type_relationships.py +102 -0
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .mypy_cache/
8
+ .ruff_cache/
9
+ .pytest_cache/
10
+ *.egg
11
+ .venv/
12
+ venv/
flawed-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,4 @@
1
+ Metadata-Version: 2.4
2
+ Name: flawed
3
+ Version: 0.0.1
4
+ Requires-Python: >=3.11
flawed-0.0.1/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # flawed
2
+
3
+ Static analysis engine for confusion vulnerability research in Python web applications.
4
+
5
+ ```python
6
+ from flawed import open_repo, detector
7
+ from flawed.inputs import Query, Form, Json
8
+ from flawed.effects import Mutation, Session
9
+ from flawed.checks import Crypto, Token
10
+ from flawed.route import Route, POST, accepting
11
+ ```
12
+
13
+ ## Status
14
+
15
+ Early development. API contracts are defined; engine runtime is not yet implemented.
16
+
17
+ ## Structure
18
+
19
+ - `src/flawed/` — Rule API (Layer 3) — the primary user-facing surface
20
+ - `src/flawed/semantic/` — Semantic Layer (Layer 2) — framework interpretation (planned)
21
+ - `src/flawed/index/` — Code Index (Layer 1) — structural extraction (planned)
22
+
23
+ ## Design
24
+
25
+ See `docs/` for architecture and design documents.
@@ -0,0 +1,131 @@
1
+ # Confusion Analysis Engine
2
+
3
+ Specification for the standalone analysis engine for researching confusion
4
+ vulnerabilities in Python web applications.
5
+
6
+ ## Problem Statement
7
+
8
+ > Given a local repository snapshot, compile source code into stable
9
+ > structural facts, interpretable framework-specific domain objects, and
10
+ > composable detection-rule primitives for confusion vulnerability research.
11
+
12
+ ## Architecture
13
+
14
+ Three layers. Each owns a distinct concern. Dependencies flow in one
15
+ direction: Layer 3 -> Layer 2 -> Layer 1. No skipping, no reverse.
16
+
17
+ ```
18
+ repository snapshot
19
+ |
20
+ v
21
+ Layer 1: Code Index
22
+ Python-generic structural extraction.
23
+ Runs once per repo snapshot. Persists artifacts.
24
+ Exposes: functions, classes, call graph, CFGs, value-flow,
25
+ symbols, decorators, imports, class hierarchy.
26
+ |
27
+ | CodeIndex API (frozen Python objects)
28
+ v
29
+ Layer 2: Semantic Layer
30
+ Framework/library/convention interpretation.
31
+ Runs per detection. Extensible by users.
32
+ Exposes: routes, request reads, gates, effects,
33
+ lifecycle hooks, on-demand flow traces.
34
+ |
35
+ | WebApp API (frozen domain objects)
36
+ v
37
+ Layer 3: Rule API
38
+ Typed domain navigation, scoped queries,
39
+ evidence building, detector framework, dev tools.
40
+ Consumers: rule authors, notebooks, batch detection.
41
+ ```
42
+
43
+ ## Directory Structure
44
+
45
+ ```
46
+ L6/
47
+ README.md <- You are here
48
+ principles.md <- Vision, non-negotiable principles, API design principles
49
+ architecture.md <- Three-layer overview, data flow, execution model
50
+ boundaries.md <- Normative boundary spec: what belongs where
51
+
52
+ API-L1-Code-Index/ <- Layer 1: Python-generic structural extraction
53
+ README.md Extraction pipeline, CodeIndex API, data types
54
+
55
+ API-L2-Semantic-Layer/ <- Layer 2: Framework/library interpretation
56
+ README.md Interpreter architecture, WebApp API, flow tracing
57
+
58
+ API-L3-Rule-API/ <- Layer 3: Rule authoring and exploration surface
59
+ README.md Domain objects, queries, evidence, detector framework
60
+ ```
61
+
62
+ ## Reading Order
63
+
64
+ ### For rule authors (start here)
65
+
66
+ 1. `principles.md` sections 1-3 -- vision and API design principles
67
+ 2. `API-L3-Rule-API/README.md` -- domain objects, queries, examples
68
+ 3. Start writing rules
69
+
70
+ ### For framework adapter authors
71
+
72
+ 1. `principles.md` -- non-negotiable design principles
73
+ 2. `boundaries.md` -- what belongs in which layer
74
+ 3. `API-L2-Semantic-Layer/README.md` -- interpreter protocol, extension model
75
+
76
+ ### For pipeline developers
77
+
78
+ 1. `principles.md` -- full document
79
+ 2. `architecture.md` -- three-layer overview, data flow, execution model
80
+ 3. `boundaries.md` -- normative boundary spec
81
+ 4. `API-L1-Code-Index/README.md` -- extraction pipeline, CodeIndex API
82
+ 5. `API-L2-Semantic-Layer/README.md` -- interpreter architecture, WebApp API
83
+ 6. `API-L3-Rule-API/README.md` -- rule authoring surface
84
+
85
+ ## Key Design Decisions
86
+
87
+ 1. **Framework knowledge is never in the run-once pipeline.** The Code
88
+ Index knows Python the language. Flask, SQLAlchemy, marshmallow, and
89
+ every other framework live exclusively in the Semantic Layer.
90
+
91
+ 2. **Extensibility without re-extraction.** Adding a new framework,
92
+ fixing a route-detection pattern, or recognizing a new ORM call
93
+ requires zero re-extraction. User extensions have full parity with
94
+ built-in defaults.
95
+
96
+ 3. **On-demand flow tracing replaces pre-computed taint.** Semgrep's
97
+ taint signatures are removed from the internal pipeline. Data-flow
98
+ tracing is a runtime Semantic Layer service that accepts arbitrary
99
+ sources, not just `request`.
100
+
101
+ 4. **Decorators split: syntax in Layer 1, meaning in Layer 2.** The Code
102
+ Index records decorator name, arguments, and location. The Semantic
103
+ Layer decides whether a decorator is an auth gate, a route
104
+ registration, or a CSRF exemption.
105
+
106
+ 5. **Strict unidirectional layering.** Layer 3 -> Layer 2 -> Layer 1.
107
+ No skipping. No reverse dependencies. No exceptions.
108
+
109
+ ## Supersession
110
+
111
+ This directory supersedes `wip/round2/L5/confusion-analysis/`.
112
+
113
+ What changed from L5 to L6:
114
+
115
+ | L5 | L6 | Why |
116
+ |----|-----|-----|
117
+ | 7-stage monolithic pipeline | 3-layer architecture | Clear responsibility boundaries, strict layering |
118
+ | Flask-specific extraction in run-once pipeline | All framework logic in Semantic Layer | Extensibility without re-extraction |
119
+ | No middle layer | Semantic Layer (Layer 2) | Framework interpretation must be dynamic and extensible |
120
+ | Pre-computed taint via Semgrep rule | On-demand flow tracing at Layer 2 | Arbitrary sources, not just `request`; no Semgrep dependency |
121
+ | Extension via model libraries only | Full interpreter replacement/extension | First-class framework adapter protocol |
122
+ | Mixed generic/Flask components per stage | Pure generic pipeline + pure framework layer | Each layer testable in isolation |
123
+
124
+ ## Relationship to Other Documentation
125
+
126
+ | Document | Role |
127
+ |----------|------|
128
+ | `wip/round2/L5/confusion-analysis/` | Previous iteration (superseded by this directory) |
129
+ | `wip/round2/L4/confusion-analysis-pipeline.md` | Historical: original monolithic spec |
130
+ | `docs/analysis-pipeline/` | Empirical research: Semgrep capabilities, tool evaluations |
131
+ | `.claude/scratch/semgrep-pro-investigation/` | Research artifacts: extraction outputs, wave test results |
@@ -0,0 +1,330 @@
1
+ # Architecture
2
+
3
+ Three layers. Each owns a distinct concern. Dependencies flow in one
4
+ direction: Layer 3 → Layer 2 → Layer 1. No skipping, no reverse references.
5
+
6
+ ```
7
+ repository snapshot
8
+
9
+
10
+ ┌─────────────────────────────────────────────┐
11
+ │ Layer 1: Code Index │
12
+ │ Python-generic structural extraction. │
13
+ │ Runs once per repo snapshot. │
14
+ │ Persists artifacts to disk. │
15
+ │ Exposes a typed Python API over the │
16
+ │ pre-computed code structure. │
17
+ │ │
18
+ │ Produces: AST entities, call graph, CFGs, │
19
+ │ value-flow, symbols, class hierarchy, │
20
+ │ imports, decorators, attribute accesses. │
21
+ └────────────────────┬────────────────────────┘
22
+ │ CodeIndex API (Python objects, frozen)
23
+
24
+ ┌─────────────────────────────────────────────┐
25
+ │ Layer 2: Semantic Layer │
26
+ │ Framework/library/convention-specific │
27
+ │ interpretation. Runs per detection. │
28
+ │ Extensible by rule authors. │
29
+ │ │
30
+ │ Produces: Routes, RequestReads, Gates, │
31
+ │ Effects, Lifecycle, method branches, │
32
+ │ classified mutations, enhanced dispatch, │
33
+ │ on-demand data-flow traces. │
34
+ └────────────────────┬────────────────────────┘
35
+ │ WebApp API (domain objects, frozen)
36
+
37
+ ┌─────────────────────────────────────────────┐
38
+ │ Layer 3: Rule API │
39
+ │ Typed domain navigation, scoped queries, │
40
+ │ collection API, evidence building, │
41
+ │ detector framework, dev tools. │
42
+ │ │
43
+ │ Consumers: rule authors, notebooks, │
44
+ │ interactive exploration, batch detection. │
45
+ └─────────────────────────────────────────────┘
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Layer 1: Code Index
51
+
52
+ ### Responsibility
53
+
54
+ Given a local Python repository snapshot, extract structural facts that are
55
+ true of the code itself, independent of any web framework, library, or
56
+ coding convention. Store those facts. Expose them through a typed Python API
57
+ that hides file layout, serialization format, and internal ID schemes.
58
+
59
+ ### What it produces
60
+
61
+ | Fact kind | Source | Notes |
62
+ |-----------|--------|-------|
63
+ | Functions | LibCST + astroid | name, FQN, params, location, kind (top-level/method/nested/lambda) |
64
+ | Classes | LibCST + astroid | name, FQN, bases, location, MRO, hierarchy |
65
+ | Imports | LibCST + Semgrep symbols | resolved names, aliases, re-exports |
66
+ | Decorators | LibCST + astroid | name, resolved FQN, args, target function, location — no semantic interpretation |
67
+ | Call edges | Semgrep call graph + AST | merged cross-file, per-file, with resolution status; includes unresolved dispatch patterns (getattr, dict dispatch) as edges |
68
+ | CFGs | LibCST + NetworkX | per-function blocks, edges, dominance, branch structure |
69
+ | Value-flow edges | LibCST + astroid + Semgrep svalues | assignments, arguments, returns, aliases, unpacking |
70
+ | Symbol resolution | LibCST + astroid + Semgrep symbols | FQNs, resolved names, import chains |
71
+ | Class hierarchy | LibCST + astroid | MRO, base classes, method resolution — stored on Class objects |
72
+ | Attribute accesses | LibCST + astroid | reads and writes on any object; includes dict operations, list/set mutations, del statements |
73
+ | Source locations | all passes | file, line, column for every fact |
74
+ | Errors and gaps | all passes | per-file parse failures, per-function CFG failures, unresolved symbols |
75
+
76
+ ### What it does NOT produce
77
+
78
+ - Routes or endpoint registrations.
79
+ - Request input classifications (`Query`, `Form`, `Json`).
80
+ - Security gate or effect observations.
81
+ - Lifecycle hooks or framework state access.
82
+ - Taint summaries or request-data flow traces.
83
+ - Semantic tags (`auth_gate`, `db_write`, `lifecycle_state`).
84
+ - Anything that requires knowing which framework the code uses.
85
+
86
+ ### API shape
87
+
88
+ Layer 1 exposes a `CodeIndex` object. All access goes through it. No
89
+ consumer reads JSONL files, raw Semgrep output, or internal indexes
90
+ directly.
91
+
92
+ Built on mandatory dependencies: LibCST (parsing, scope analysis), astroid
93
+ (inference, MRO), Semgrep Pro (cross-file call graph, symbols), basedpyright
94
+ (type oracle), and NetworkX (graph algorithms).
95
+
96
+ ```python
97
+ idx = CodeIndex.open("/path/to/analysis-store/repos/slug/snapshot")
98
+
99
+ idx.functions # FunctionCollection — filter, iterate, lookup by FQN
100
+ idx.classes # ClassCollection — includes hierarchy (MRO, subclasses)
101
+ idx.imports # ImportCollection
102
+ idx.decorators # DecoratorCollection — syntactic facts with resolved FQNs
103
+ idx.call_graph # CallGraph — nodes, edges, reachability; includes
104
+ # unresolved dispatch edges (getattr, dict dispatch)
105
+ idx.cfg(fn_fqn) # per-function CFG with dominance
106
+ idx.value_flow # ValueFlowGraph — assignment chains, argument binding
107
+ idx.symbols # SymbolIndex — FQN resolution, import chains
108
+ idx.attributes # AttributeAccessCollection — reads/writes/mutations on any object
109
+ idx.source(location) # source text for a location
110
+ idx.errors # extraction errors and gaps
111
+ ```
112
+
113
+ Every object returned is frozen. Layer 2 cannot mutate Layer 1 data.
114
+
115
+ ### Execution
116
+
117
+ Runs once per repository snapshot via CLI. Produces a deterministic artifact
118
+ tree in the analysis store. Idempotent: re-running with the same source and
119
+ tool versions produces identical output.
120
+
121
+ ---
122
+
123
+ ## Layer 2: Semantic Layer
124
+
125
+ ### Responsibility
126
+
127
+ Interpret Layer 1's Python-structural facts through the lens of a specific
128
+ web framework, set of libraries, and project conventions. Produce typed
129
+ domain objects that encode what the code *means* in a web application
130
+ security context.
131
+
132
+ This is where all framework-specific knowledge lives. Flask route
133
+ registration patterns, request container mappings, ORM write detection,
134
+ session and context state identification, lifecycle hooks — all of it.
135
+
136
+ ### What it produces
137
+
138
+ | Domain concept | Built from (Layer 1 facts) | Framework knowledge required |
139
+ |----------------|---------------------------|------------------------------|
140
+ | Routes | decorators + functions + class hierarchy | Route registration patterns (`@app.route`, MethodView, etc.) |
141
+ | Request reads | call sites + symbol resolution + attribute accesses | Request container API (`request.args.get`, marshmallow schema, etc.) |
142
+ | Context accesses | attribute accesses + import resolution | Context proxies (`g`, `session`, `current_user`) |
143
+ | Gates | decorators + call sites + conditionals | Auth decorator patterns, permission check functions |
144
+ | Effects | call sites + attribute writes + symbol resolution | ORM write patterns, external request patterns |
145
+ | Lifecycle hooks | decorators + functions + import resolution | Hook registration (`before_request`, `after_request`) |
146
+ | Method branches | CFG blocks + conditionals + value-flow | Method dispatch patterns (`request.method == "POST"`) |
147
+ | Classified mutations | attribute accesses + symbol resolution | Framework-specific state mutation patterns |
148
+ | Enhanced dispatch | call graph (unresolved edges) + class hierarchy | Framework-specific dispatch (MethodView, signals) |
149
+ | On-demand flow traces | call graph + value-flow + CFGs | Source/sink definitions, propagation rules |
150
+
151
+ ### Extension model
152
+
153
+ Layer 2 is composed of **interpreters** — ordinary Python modules that
154
+ encode reusable framework/library knowledge:
155
+
156
+ - **Route interpreters**: recognize registration patterns, produce Route objects.
157
+ - **Input interpreters**: map code expressions to typed request-read observations.
158
+ - **Gate interpreters**: classify decorators, calls, conditionals as security gates.
159
+ - **Effect interpreters**: classify calls and writes as security-relevant effects.
160
+ - **State interpreters**: identify framework-specific context access patterns.
161
+ - **Flow interpreters**: define source/sink/propagator rules for data-flow tracing.
162
+
163
+ Users extend or replace any interpreter. A project using Flask-RESTful adds
164
+ an interpreter that recognizes `Resource` subclasses as route handlers. A
165
+ project with custom auth adds an interpreter that recognizes its
166
+ `@requires_permission` decorator as a gate. These extensions have full
167
+ parity with the built-in Flask interpreters — the built-ins are just
168
+ default interpreter modules, not privileged internal code.
169
+
170
+ ### API shape
171
+
172
+ Layer 2 exposes a `WebApp` object built from a `CodeIndex` plus a set of
173
+ active interpreters:
174
+
175
+ ```python
176
+ app = WebApp.from_index(idx, interpreters=flask_defaults())
177
+
178
+ app.routes # RouteCollection — framework-specific
179
+ app.request_reads # RequestReadCollection
180
+ app.gates # GateCollection
181
+ app.effects # EffectCollection
182
+ app.hooks # HookCollection
183
+ app.context_accesses # ContextAccessCollection
184
+ app.classified_mutations # ClassifiedMutationCollection
185
+
186
+ app.trace_flow(source, sink) # on-demand data-flow traversal
187
+ app.reachable_from(function) # transitive closure with framework edges
188
+ app.enhanced_call_graph # call graph with framework-injected edges
189
+ ```
190
+
191
+ Every object returned is frozen. Layer 3 cannot mutate Layer 2 data.
192
+
193
+ ### Execution
194
+
195
+ Runs per detection invocation. Layer 2 code executes each time a rule runs,
196
+ so interpreter changes take effect immediately without re-running the
197
+ internal pipeline.
198
+
199
+ ---
200
+
201
+ ## Layer 3: Rule API
202
+
203
+ ### Responsibility
204
+
205
+ Provide rule authors with typed, composable, notebook-friendly tools for
206
+ navigating analyzed codebases, expressing security invariants, building
207
+ evidence chains, and producing vulnerability candidates.
208
+
209
+ ### What it provides
210
+
211
+ - **Domain navigation**: `Route`, `Function`, `CodeScope` with fluent queries.
212
+ - **Scoped queries**: `.reads()`, `.effects()`, `.gates()`, `.calls()` within
213
+ a code scope (route body, reachable code, function body).
214
+ - **Control flow**: `.cfg.dominates()`, `.cfg.precedes()`, `.cfg.ordered()`.
215
+ - **Value flow**: `.value.flows_to()`, `.value_bindings.to()`.
216
+ - **Collections**: filtering, grouping, chaining over domain objects.
217
+ - **Evidence building**: `CandidateBuilder` with evidence, missing evidence,
218
+ analysis gaps, severity, tags.
219
+ - **Detector framework**: `@detector`, `@invariant`, `@for_routes`, config.
220
+ - **Dev tools**: `trace_detector()`, `dry_run()`, `explain()`.
221
+
222
+ ### Consumers
223
+
224
+ - Security researchers writing detection rules.
225
+ - Notebook-driven interactive code exploration.
226
+ - Batch detection across a repository corpus.
227
+ - Hypothesis testing and rule prototyping.
228
+
229
+ Layer 3 consumers never import Layer 1 types, never read raw files, and
230
+ never need to understand static analysis internals.
231
+
232
+ ---
233
+
234
+ ## Data Flow
235
+
236
+ ```
237
+ Source files
238
+
239
+
240
+ Layer 1: Semgrep extraction → normalization → AST passes → merge → index
241
+
242
+ │ Persisted artifacts: analysis-store/repos/<slug>/<snapshot>/
243
+ │ - raw/semgrep/ (call graph, symbols, svalues)
244
+ │ - normalized/ (entities, calls, cfgs, hierarchy, symbols,
245
+ │ value_flow, attributes, decorators)
246
+ │ - projections/ (merged_call_graph, indexes)
247
+ │ - manifest.json (version hashes, step status)
248
+ │ - errors.jsonl (extraction failures, gaps)
249
+
250
+
251
+ Layer 2: Framework interpreters → domain construction → flow tracing
252
+
253
+ │ Runtime objects (not persisted by default):
254
+ │ - Route, RequestRead, Gate, Effect, Hook, ContextAccess
255
+ │ - Enhanced call graph (with framework-specific edges)
256
+ │ - Classified mutations, method branches
257
+ │ - On-demand flow traces
258
+
259
+
260
+ Layer 3: Rule execution → candidate emission
261
+
262
+ │ Output: candidates.jsonl, reports, evidence packs
263
+
264
+
265
+ Triage and reporting
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Execution Model
271
+
272
+ | Concern | When it runs | Persisted? | Invalidated by |
273
+ |---------|-------------|------------|----------------|
274
+ | Layer 1 extraction | Once per repo snapshot | Yes — artifact tree | Source change, tool version change |
275
+ | Layer 2 interpretation | Per detection run | No (optionally cached) | Interpreter change, Layer 1 change |
276
+ | Layer 3 detection | Per rule invocation | Candidates persisted | Rule change, config change, Layer 2 change |
277
+
278
+ Layer 1 is the expensive step. Layer 2 and Layer 3 are cheap and
279
+ re-run freely. This split means that adding support for a new
280
+ framework, fixing a gate recognition pattern, or adjusting effect
281
+ classification requires zero re-extraction.
282
+
283
+ ---
284
+
285
+ ## Extension Points
286
+
287
+ | Extension | Layer | Mechanism | Requires re-extraction? |
288
+ |-----------|-------|-----------|------------------------|
289
+ | New route registration pattern | 2 | Route interpreter module | No |
290
+ | New request parser library | 2 | Input interpreter module | No |
291
+ | New auth decorator pattern | 2 | Gate interpreter module | No |
292
+ | New ORM write pattern | 2 | Effect interpreter module | No |
293
+ | New context proxy | 2 | State interpreter module | No |
294
+ | Custom flow source/sink | 2 | Flow interpreter module | No |
295
+ | New detection rule | 3 | Detector module | No |
296
+ | New model library | 3 | Ordinary Python module | No |
297
+ | New extraction pass | 1 | Fact builder module | Yes |
298
+
299
+ Extension through Layer 2 interpreters is the expected path. Layer 1
300
+ extraction changes only when the structural fact base is missing a
301
+ primitive observation that no interpreter can recover from existing
302
+ facts.
303
+
304
+ ---
305
+
306
+ ## Dependency Rules
307
+
308
+ 1. **Layer 3 depends only on Layer 2.** Rule code imports Layer 2's domain
309
+ types and API. It never imports Layer 1 types, never reads analysis
310
+ store files, never instantiates code index objects.
311
+
312
+ 2. **Layer 2 depends only on Layer 1.** Interpreter code imports Layer 1's
313
+ `CodeIndex` API. It never reads raw Semgrep output, never parses JSONL
314
+ files, never accesses internal storage paths.
315
+
316
+ 3. **Layer 1 depends on nothing above.** It has no knowledge of frameworks,
317
+ routes, security semantics, or vulnerability patterns.
318
+
319
+ 4. **No skipping.** Layer 3 cannot reach into Layer 1 for "just this one
320
+ query." If Layer 3 needs something, Layer 2 must expose it. If
321
+ Layer 2 needs something, Layer 1 must expose it.
322
+
323
+ 5. **Boundary immutability.** Objects crossing layer boundaries are frozen.
324
+ The receiving layer cannot modify what the producing layer tracks.
325
+
326
+ 6. **Vocabulary matches the consumer.** Layer 1 speaks Python structural
327
+ vocabulary (functions, calls, CFG blocks). Layer 2 speaks web
328
+ application security vocabulary (routes, inputs, gates, effects).
329
+ Layer 3 speaks vulnerability research vocabulary (invariants,
330
+ confusion, candidates, evidence).