@zivis/mcp 0.1.0-alpha.21 → 0.1.0-alpha.23

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 (223) hide show
  1. package/dist/matcher/ast-eval.d.ts +27 -0
  2. package/dist/matcher/ast-eval.js +132 -0
  3. package/dist/matcher/ast-eval.js.map +1 -0
  4. package/dist/matcher/finding.d.ts +76 -0
  5. package/dist/matcher/finding.js +14 -0
  6. package/dist/matcher/finding.js.map +1 -0
  7. package/dist/matcher/index.d.ts +30 -0
  8. package/dist/matcher/index.js +255 -0
  9. package/dist/matcher/index.js.map +1 -0
  10. package/dist/matcher/inference-candidates.d.ts +54 -0
  11. package/dist/matcher/inference-candidates.js +106 -0
  12. package/dist/matcher/inference-candidates.js.map +1 -0
  13. package/dist/matcher/walker.d.ts +14 -0
  14. package/dist/matcher/walker.js +79 -0
  15. package/dist/matcher/walker.js.map +1 -0
  16. package/dist/pattern-pack/index.d.ts +154 -0
  17. package/dist/pattern-pack/index.js +224 -0
  18. package/dist/pattern-pack/index.js.map +1 -0
  19. package/dist/pattern-packs/zivis-public-0.1.0/capsules/agents/privilege-separation.yaml +224 -0
  20. package/dist/pattern-packs/zivis-public-0.1.0/capsules/prompting/self-consistency-no-isolation.yaml +237 -0
  21. package/dist/pattern-packs/zivis-public-0.1.0/capsules/security/late-org-filter.yaml +266 -0
  22. package/dist/pattern-packs/zivis-public-0.1.0/controls/external_validator_on_facts.yaml +37 -0
  23. package/dist/pattern-packs/zivis-public-0.1.0/controls/human_approval_gate.yaml +44 -0
  24. package/dist/pattern-packs/zivis-public-0.1.0/controls/langgraph_state_machine_with_per_node_tools.yaml +35 -0
  25. package/dist/pattern-packs/zivis-public-0.1.0/controls/per_sample_retrieval_diversification.yaml +38 -0
  26. package/dist/pattern-packs/zivis-public-0.1.0/controls/prisma_extension_or_middleware_attaching_org_filter.yaml +31 -0
  27. package/dist/pattern-packs/zivis-public-0.1.0/controls/row_level_security_policy.yaml +37 -0
  28. package/dist/pattern-packs/zivis-public-0.1.0/manifest.json +129 -0
  29. package/dist/pattern-packs/zivis-public-0.1.0/prompts/agents/privilege-separation/identify.md +54 -0
  30. package/dist/pattern-packs/zivis-public-0.1.0/prompts/prompting/self-consistency-no-isolation/identify.md +60 -0
  31. package/dist/pattern-packs/zivis-public-0.1.0/prompts/security/late-org-filter/identify.md +78 -0
  32. package/dist/prompts/index.d.ts +1 -1
  33. package/dist/prompts/index.js +1 -7
  34. package/dist/prompts/index.js.map +1 -1
  35. package/dist/server.js +36 -166
  36. package/dist/server.js.map +1 -1
  37. package/dist/tools/enterprise/get-threat-model-artifact.d.ts +30 -0
  38. package/dist/tools/enterprise/get-threat-model-artifact.js +80 -0
  39. package/dist/tools/enterprise/get-threat-model-artifact.js.map +1 -0
  40. package/dist/tools/enterprise/get-threat-model.d.ts +1 -1
  41. package/dist/tools/enterprise/get-threat-model.js +59 -11
  42. package/dist/tools/enterprise/get-threat-model.js.map +1 -1
  43. package/dist/tools/enterprise/list-threat-model-artifacts.d.ts +67 -0
  44. package/dist/tools/enterprise/list-threat-model-artifacts.js +121 -0
  45. package/dist/tools/enterprise/list-threat-model-artifacts.js.map +1 -0
  46. package/dist/tools/enterprise/manage-actor.d.ts +1 -1
  47. package/dist/tools/enterprise/manage-actor.js +14 -1
  48. package/dist/tools/enterprise/manage-actor.js.map +1 -1
  49. package/dist/tools/enterprise/manage-attack-scenario.d.ts +1 -1
  50. package/dist/tools/enterprise/manage-attack-scenario.js +13 -1
  51. package/dist/tools/enterprise/manage-attack-scenario.js.map +1 -1
  52. package/dist/tools/enterprise/manage-component.d.ts +1 -1
  53. package/dist/tools/enterprise/manage-component.js +20 -1
  54. package/dist/tools/enterprise/manage-component.js.map +1 -1
  55. package/dist/tools/enterprise/manage-data-asset.d.ts +2 -2
  56. package/dist/tools/enterprise/manage-data-asset.js +17 -1
  57. package/dist/tools/enterprise/manage-data-asset.js.map +1 -1
  58. package/dist/tools/enterprise/manage-data-flow.d.ts +2 -2
  59. package/dist/tools/enterprise/manage-data-flow.js +19 -1
  60. package/dist/tools/enterprise/manage-data-flow.js.map +1 -1
  61. package/dist/tools/enterprise/manage-security-control.d.ts +1 -1
  62. package/dist/tools/enterprise/manage-security-control.js +18 -1
  63. package/dist/tools/enterprise/manage-security-control.js.map +1 -1
  64. package/dist/tools/enterprise/manage-test-case.d.ts +2 -2
  65. package/dist/tools/enterprise/manage-test-case.js +8 -0
  66. package/dist/tools/enterprise/manage-test-case.js.map +1 -1
  67. package/dist/tools/get-started.d.ts +1 -1
  68. package/dist/tools/get-started.js +185 -8
  69. package/dist/tools/get-started.js.map +1 -1
  70. package/dist/tools/inspect.d.ts +38 -0
  71. package/dist/tools/inspect.js +252 -0
  72. package/dist/tools/inspect.js.map +1 -0
  73. package/dist/tools/manage-application.d.ts +1 -1
  74. package/dist/tools/security-review.js +67 -50
  75. package/dist/tools/security-review.js.map +1 -1
  76. package/dist/tools/threat-get-capsule.d.ts +25 -0
  77. package/dist/tools/threat-get-capsule.js +64 -0
  78. package/dist/tools/threat-get-capsule.js.map +1 -0
  79. package/dist/tools/threat-get-inference-prompt.d.ts +32 -0
  80. package/dist/tools/threat-get-inference-prompt.js +91 -0
  81. package/dist/tools/threat-get-inference-prompt.js.map +1 -0
  82. package/dist/tools/threat-guide.d.ts +39 -0
  83. package/dist/tools/threat-guide.js +111 -0
  84. package/dist/tools/threat-guide.js.map +1 -0
  85. package/dist/tools/threat-list-relevant-capsules.d.ts +30 -0
  86. package/dist/tools/threat-list-relevant-capsules.js +176 -0
  87. package/dist/tools/threat-list-relevant-capsules.js.map +1 -0
  88. package/dist/tools/threat-run-matcher.d.ts +27 -0
  89. package/dist/tools/threat-run-matcher.js +164 -0
  90. package/dist/tools/threat-run-matcher.js.map +1 -0
  91. package/package.json +13 -3
  92. package/dist/prompts/prep-for-launch.d.ts +0 -9
  93. package/dist/prompts/prep-for-launch.js +0 -62
  94. package/dist/prompts/prep-for-launch.js.map +0 -1
  95. package/dist/prompts/secure-my-app.d.ts +0 -9
  96. package/dist/prompts/secure-my-app.js +0 -70
  97. package/dist/prompts/secure-my-app.js.map +0 -1
  98. package/dist/prompts/weekly-check.d.ts +0 -9
  99. package/dist/prompts/weekly-check.js +0 -48
  100. package/dist/prompts/weekly-check.js.map +0 -1
  101. package/dist/tools/enterprise/capture-evidence.d.ts +0 -54
  102. package/dist/tools/enterprise/capture-evidence.js +0 -72
  103. package/dist/tools/enterprise/capture-evidence.js.map +0 -1
  104. package/dist/tools/enterprise/create-report-ticket.d.ts +0 -34
  105. package/dist/tools/enterprise/create-report-ticket.js +0 -44
  106. package/dist/tools/enterprise/create-report-ticket.js.map +0 -1
  107. package/dist/tools/enterprise/end-pentest-session.d.ts +0 -33
  108. package/dist/tools/enterprise/end-pentest-session.js +0 -42
  109. package/dist/tools/enterprise/end-pentest-session.js.map +0 -1
  110. package/dist/tools/enterprise/generate-report.d.ts +0 -55
  111. package/dist/tools/enterprise/generate-report.js +0 -111
  112. package/dist/tools/enterprise/generate-report.js.map +0 -1
  113. package/dist/tools/enterprise/get-campaign.d.ts +0 -27
  114. package/dist/tools/enterprise/get-campaign.js +0 -31
  115. package/dist/tools/enterprise/get-campaign.js.map +0 -1
  116. package/dist/tools/enterprise/get-report-status.d.ts +0 -27
  117. package/dist/tools/enterprise/get-report-status.js +0 -38
  118. package/dist/tools/enterprise/get-report-status.js.map +0 -1
  119. package/dist/tools/enterprise/list-agents.d.ts +0 -42
  120. package/dist/tools/enterprise/list-agents.js +0 -95
  121. package/dist/tools/enterprise/list-agents.js.map +0 -1
  122. package/dist/tools/enterprise/list-campaigns.d.ts +0 -37
  123. package/dist/tools/enterprise/list-campaigns.js +0 -60
  124. package/dist/tools/enterprise/list-campaigns.js.map +0 -1
  125. package/dist/tools/enterprise/list-report-issues.d.ts +0 -36
  126. package/dist/tools/enterprise/list-report-issues.js +0 -50
  127. package/dist/tools/enterprise/list-report-issues.js.map +0 -1
  128. package/dist/tools/enterprise/manage-campaign-agent-instance.d.ts +0 -42
  129. package/dist/tools/enterprise/manage-campaign-agent-instance.js +0 -68
  130. package/dist/tools/enterprise/manage-campaign-agent-instance.js.map +0 -1
  131. package/dist/tools/enterprise/manage-campaign-target.d.ts +0 -44
  132. package/dist/tools/enterprise/manage-campaign-target.js +0 -70
  133. package/dist/tools/enterprise/manage-campaign-target.js.map +0 -1
  134. package/dist/tools/enterprise/manage-campaign.d.ts +0 -62
  135. package/dist/tools/enterprise/manage-campaign.js +0 -114
  136. package/dist/tools/enterprise/manage-campaign.js.map +0 -1
  137. package/dist/tools/enterprise/manage-finding.d.ts +0 -69
  138. package/dist/tools/enterprise/manage-finding.js +0 -106
  139. package/dist/tools/enterprise/manage-finding.js.map +0 -1
  140. package/dist/tools/enterprise/promote-finding-to-issue.d.ts +0 -36
  141. package/dist/tools/enterprise/promote-finding-to-issue.js +0 -65
  142. package/dist/tools/enterprise/promote-finding-to-issue.js.map +0 -1
  143. package/dist/tools/enterprise/publish-report-version.d.ts +0 -34
  144. package/dist/tools/enterprise/publish-report-version.js +0 -75
  145. package/dist/tools/enterprise/publish-report-version.js.map +0 -1
  146. package/dist/tools/enterprise/publish-test-to-library.d.ts +0 -31
  147. package/dist/tools/enterprise/publish-test-to-library.js +0 -40
  148. package/dist/tools/enterprise/publish-test-to-library.js.map +0 -1
  149. package/dist/tools/enterprise/record-test-result.d.ts +0 -67
  150. package/dist/tools/enterprise/record-test-result.js +0 -75
  151. package/dist/tools/enterprise/record-test-result.js.map +0 -1
  152. package/dist/tools/enterprise/start-pentest-session.d.ts +0 -35
  153. package/dist/tools/enterprise/start-pentest-session.js +0 -50
  154. package/dist/tools/enterprise/start-pentest-session.js.map +0 -1
  155. package/dist/tools/enterprise/sync-report-section.d.ts +0 -41
  156. package/dist/tools/enterprise/sync-report-section.js +0 -79
  157. package/dist/tools/enterprise/sync-report-section.js.map +0 -1
  158. package/dist/tools/enterprise/update-report-issue.d.ts +0 -55
  159. package/dist/tools/enterprise/update-report-issue.js +0 -69
  160. package/dist/tools/enterprise/update-report-issue.js.map +0 -1
  161. package/dist/tools/get-agent-test-results.d.ts +0 -41
  162. package/dist/tools/get-agent-test-results.js +0 -86
  163. package/dist/tools/get-agent-test-results.js.map +0 -1
  164. package/dist/tools/get-application-auth-config.d.ts +0 -27
  165. package/dist/tools/get-application-auth-config.js +0 -44
  166. package/dist/tools/get-application-auth-config.js.map +0 -1
  167. package/dist/tools/get-artifact-content.d.ts +0 -29
  168. package/dist/tools/get-artifact-content.js +0 -86
  169. package/dist/tools/get-artifact-content.js.map +0 -1
  170. package/dist/tools/get-finding-details.d.ts +0 -22
  171. package/dist/tools/get-finding-details.js +0 -61
  172. package/dist/tools/get-finding-details.js.map +0 -1
  173. package/dist/tools/get-findings.d.ts +0 -51
  174. package/dist/tools/get-findings.js +0 -124
  175. package/dist/tools/get-findings.js.map +0 -1
  176. package/dist/tools/get-recon-scan.d.ts +0 -27
  177. package/dist/tools/get-recon-scan.js +0 -46
  178. package/dist/tools/get-recon-scan.js.map +0 -1
  179. package/dist/tools/get-scan-output.d.ts +0 -27
  180. package/dist/tools/get-scan-output.js +0 -64
  181. package/dist/tools/get-scan-output.js.map +0 -1
  182. package/dist/tools/get-scenario-details.d.ts +0 -27
  183. package/dist/tools/get-scenario-details.js +0 -46
  184. package/dist/tools/get-scenario-details.js.map +0 -1
  185. package/dist/tools/get-test-case-results.d.ts +0 -37
  186. package/dist/tools/get-test-case-results.js +0 -66
  187. package/dist/tools/get-test-case-results.js.map +0 -1
  188. package/dist/tools/get-test-details.d.ts +0 -29
  189. package/dist/tools/get-test-details.js +0 -49
  190. package/dist/tools/get-test-details.js.map +0 -1
  191. package/dist/tools/list-agent-test-cases.d.ts +0 -41
  192. package/dist/tools/list-agent-test-cases.js +0 -73
  193. package/dist/tools/list-agent-test-cases.js.map +0 -1
  194. package/dist/tools/list-recon-scans.d.ts +0 -36
  195. package/dist/tools/list-recon-scans.js +0 -74
  196. package/dist/tools/list-recon-scans.js.map +0 -1
  197. package/dist/tools/list-scan-artifacts.d.ts +0 -41
  198. package/dist/tools/list-scan-artifacts.js +0 -63
  199. package/dist/tools/list-scan-artifacts.js.map +0 -1
  200. package/dist/tools/list-scans.d.ts +0 -38
  201. package/dist/tools/list-scans.js +0 -125
  202. package/dist/tools/list-scans.js.map +0 -1
  203. package/dist/tools/list-test-library.d.ts +0 -44
  204. package/dist/tools/list-test-library.js +0 -83
  205. package/dist/tools/list-test-library.js.map +0 -1
  206. package/dist/tools/list-test-scenarios.d.ts +0 -25
  207. package/dist/tools/list-test-scenarios.js +0 -94
  208. package/dist/tools/list-test-scenarios.js.map +0 -1
  209. package/dist/tools/local-scan.d.ts +0 -40
  210. package/dist/tools/local-scan.js +0 -261
  211. package/dist/tools/local-scan.js.map +0 -1
  212. package/dist/tools/run-test-scenario.d.ts +0 -37
  213. package/dist/tools/run-test-scenario.js +0 -70
  214. package/dist/tools/run-test-scenario.js.map +0 -1
  215. package/dist/tools/scan-pr.d.ts +0 -27
  216. package/dist/tools/scan-pr.js +0 -51
  217. package/dist/tools/scan-pr.js.map +0 -1
  218. package/dist/tools/setup-red-team-target.d.ts +0 -73
  219. package/dist/tools/setup-red-team-target.js +0 -461
  220. package/dist/tools/setup-red-team-target.js.map +0 -1
  221. package/dist/tools/triage-finding.d.ts +0 -41
  222. package/dist/tools/triage-finding.js +0 -125
  223. package/dist/tools/triage-finding.js.map +0 -1
@@ -0,0 +1,266 @@
1
+ # Capsule: Late org-filter — cross-tenant leak via presentation-time scoping
2
+ # Spec: docs/specs/PATTERN-MATCHING-LIBRARY-V1.md
3
+ # Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
4
+ # Schema: schemas/pattern-detection-card.schema.json
5
+
6
+ id: security.late-org-filter
7
+ slug: late-org-filter
8
+ version: 0.1.0
9
+ category: security
10
+ title: Late Org Filter
11
+ headline: Tenant Scoping Applied After the Query, Not Inside It
12
+ description: >
13
+ Detects multi-tenant code paths where org/tenant filtering happens at the
14
+ presentation layer (response loop, stream emit, response shaping) instead
15
+ of inside the data query. Any race, error path, or partial response that
16
+ reaches the wire before the filter fires leaks rows across tenants.
17
+
18
+ detection_tier: public
19
+ scoring_tier: proprietary
20
+
21
+ applicable_languages:
22
+ - typescript
23
+ - javascript
24
+ - python
25
+ - go
26
+
27
+ relevance_filter:
28
+ any_of:
29
+ - dependency: express
30
+ direct_only: true
31
+ - dependency: fastify
32
+ direct_only: true
33
+ - dependency: hono
34
+ direct_only: true
35
+ - dependency: "@nestjs/core"
36
+ direct_only: true
37
+ - dependency: fastapi
38
+ direct_only: true
39
+ - dependency: flask
40
+ direct_only: true
41
+ - dependency: "django"
42
+ direct_only: true
43
+ - dependency: "github.com/gin-gonic/gin"
44
+ direct_only: true
45
+ - dependency: "github.com/labstack/echo/v4"
46
+ direct_only: true
47
+ none_of:
48
+ # If the project clearly isn't multi-tenant, skip — no benefit, lots of noise.
49
+ - evidence: "single_tenant_only_marker"
50
+
51
+ strategies:
52
+ - code: AST
53
+ language: typescript
54
+ expression: |
55
+ rule:
56
+ any:
57
+ # Filter applied to a query result then sent — classic shape.
58
+ - pattern: $ROWS.filter($_ => $_.orgId === $$$)
59
+ - pattern: $ROWS.filter($_ => $_.org_id === $$$)
60
+ - pattern: $ROWS.filter($_ => $_.tenantId === $$$)
61
+ # Per-row tenant guard inside a loop — the SSE / iterator variant.
62
+ - pattern: |
63
+ if ($R.orgId === $$$) {
64
+ $$$
65
+ }
66
+ - pattern: |
67
+ if ($R.org_id === $$$) {
68
+ $$$
69
+ }
70
+ evidence_fields: [file_path, line_span, symbol]
71
+ confidence_weight: 0.35
72
+
73
+ - code: AST
74
+ language: python
75
+ expression: |
76
+ rule:
77
+ any:
78
+ - pattern: |
79
+ rows = $QUERY($$$)
80
+ filtered = [r for r in rows if r.org_id == $REQ.org_id]
81
+ - pattern: |
82
+ for row in $QUERY($$$):
83
+ if row.org_id == request.org_id:
84
+ yield row
85
+ evidence_fields: [file_path, line_span, symbol]
86
+ confidence_weight: 0.35
87
+
88
+ - code: GRAPH
89
+ expression: |
90
+ query_call_returns_rows
91
+ AND filter_predicate_references(req_org_id OR session_org_id)
92
+ AND filter_executes_AFTER(query_call) IN response_handler_or_stream_emit
93
+ AND query_call.where_clause DOES_NOT include org_id
94
+ evidence_fields: [graph_path, file_spans, node_ids]
95
+ confidence_weight: 0.30
96
+
97
+ - code: META
98
+ expression:
99
+ none_of_controls:
100
+ - row_level_security_policy
101
+ - prisma_extension_or_middleware_attaching_org_filter
102
+ evidence_fields: [missing_controls]
103
+ confidence_weight: 0.20
104
+
105
+ - code: INFER
106
+ expression: prompt_id:security.late-org-filter.identify
107
+ evidence_fields: [llm_rationale]
108
+ confidence_weight: 0.15
109
+ trusted: false
110
+
111
+ required_evidence_count: 2
112
+
113
+ control_negations:
114
+ - "presence_of(row_level_security_policy)"
115
+ - "presence_of(query_helper_with_implicit_org_scope)"
116
+ - "presence_of(prisma_extension_or_middleware_attaching_org_filter)"
117
+
118
+ threats:
119
+ - title: Cross-tenant data leak via SSE / stream pre-filter window
120
+ stride: [information_disclosure, elevation_of_privilege]
121
+ description: >
122
+ In a streaming or async-iterator response, the underlying query returns
123
+ rows from all tenants and the per-emit filter rejects mismatches. A
124
+ brief window where rows are buffered, logged, traced, or partially
125
+ rendered before the filter executes can leak data across tenants. This
126
+ is the SSE / queue/stream variant of the pattern.
127
+ remediation: >
128
+ Push the org/tenant predicate into the data layer — RLS, query helpers,
129
+ ORM middleware. Make every query mandatorily org-scoped at the boundary;
130
+ treat presentation-layer filters as defense in depth, never the primary
131
+ barrier.
132
+ - title: Cross-tenant leak via error path or partial response
133
+ stride: [information_disclosure]
134
+ description: >
135
+ An exception inside the post-query filter loop returns the unfiltered
136
+ buffer or a serialization mid-state. Other tenants' rows reach the
137
+ wire because the only org check sits past the failure point.
138
+ remediation: >
139
+ Same control — RLS or query-time scoping. Plus: serialize the response
140
+ body only after filtering completes; on filter error, return a
141
+ structured error with no payload.
142
+ - title: Logged or traced rows expose other tenants
143
+ stride: [information_disclosure, repudiation]
144
+ description: >
145
+ Observability hooks (request logger, tracer, metrics) often capture the
146
+ raw query result before the presentation-layer filter runs. Other
147
+ tenants' identifiers, names, or PII land in logs the requesting tenant
148
+ should never have seen.
149
+ remediation: >
150
+ Either log only post-filter results, or — better — make the query
151
+ itself org-scoped so the unfiltered result never exists.
152
+
153
+ fixtures:
154
+ positive:
155
+ - "fixtures/positive/late-org-filter/express_sse_post_filter_typescript"
156
+ - "fixtures/positive/late-org-filter/fastapi_iterator_post_filter_python"
157
+ negative:
158
+ - "fixtures/negative/late-org-filter/prisma_middleware_org_scope"
159
+ - "fixtures/negative/late-org-filter/postgres_rls_enabled"
160
+
161
+ lifecycle: draft
162
+ owner: platform-threat-modeling
163
+ tags:
164
+ - multi-tenant
165
+ - cross-tenant-leak
166
+ - data-scoping
167
+ - sse
168
+ - stream
169
+
170
+ sensitivity_level: customer_safe
171
+ allowed_execution_modes:
172
+ - local_only
173
+ - local_model
174
+ - hybrid_cloud_eval
175
+
176
+ safe_summary: >
177
+ Tenant filtering applied after the query — at the response loop, the SSE
178
+ emit, or the stream iterator — instead of inside the query. Any error,
179
+ race, observability hook, or partial response that fires before the filter
180
+ reaches the wire leaks rows across tenants. The fix is to push org/tenant
181
+ scoping down to the data layer (RLS, query helpers, ORM middleware) so the
182
+ unfiltered set never exists in the first place.
183
+
184
+ detect_when: >
185
+ A handler, stream, or async iterator runs a data query, then filters the
186
+ result by `request.orgId` (or a session org) before responding — and the
187
+ query itself doesn't include the org predicate. Multi-tenant routes only.
188
+
189
+ risk_hints:
190
+ - The bug class behind a meaningful share of cross-tenant SaaS incidents
191
+ - SSE / queue / WebSocket variants are the hardest to spot because the filter looks "inside" the loop
192
+ - Logs and traces often capture the unfiltered set even when the response itself is clean
193
+ - Tests often pass because they only run with a single tenant in the test database
194
+
195
+ architectural_signals:
196
+ - Query call followed by an explicit filter referencing a request/session org id
197
+ - SSE or async iterator that yields events conditional on a tenant match
198
+ - No row-level security policy, no implicit-org-scope helper, no ORM middleware
199
+
200
+ model_task_prompt_ref: prompts/security/late-org-filter/identify.md
201
+
202
+ expected_output_schema:
203
+ type: object
204
+ required: [verdict, scope_location, evidence_spans]
205
+ properties:
206
+ verdict:
207
+ type: string
208
+ enum: [late_filter_present, scoped_at_query, partial_scope, unclear]
209
+ scope_location:
210
+ type: string
211
+ enum: [data_layer, query_layer, presentation_layer, multiple, none]
212
+ rationale:
213
+ type: string
214
+ blast_radius:
215
+ type: string
216
+ enum: [single_endpoint, route_family, service_wide, unclear]
217
+ recommended_controls:
218
+ type: array
219
+ items:
220
+ type: string
221
+ enum: [row_level_security, query_helper, orm_middleware, repository_pattern_with_org_scope, none]
222
+ evidence_spans:
223
+ type: array
224
+ items:
225
+ type: object
226
+ required: [file]
227
+ properties:
228
+ file: { type: string }
229
+ start_line: { type: integer }
230
+ end_line: { type: integer }
231
+ note: { type: string }
232
+
233
+ minimum_context_required:
234
+ lines: 100
235
+ symbols:
236
+ - orgId
237
+ - org_id
238
+ - tenant_id
239
+ - filter
240
+ - SSE
241
+ - stream
242
+
243
+ related_patterns:
244
+ - security.idor-via-direct-id
245
+ - security.broken-object-level-authz
246
+
247
+ repair_contract:
248
+ allowed:
249
+ - Push org/tenant scoping into the query (where clause, RLS, ORM middleware)
250
+ - Introduce a repository wrapper that mandates org_id on every method
251
+ - Add a Postgres row-level security policy
252
+ - Keep the presentation-layer filter as defense in depth IN ADDITION TO the data-layer scope
253
+ forbidden:
254
+ - Removing the presentation-layer filter without first introducing a data-layer scope (regresses safety)
255
+ - Treating the patch as complete after only fixing the immediate route — the bug class is structural
256
+
257
+ validation_contract:
258
+ requires_tests_pass: false
259
+ requires_static_rules:
260
+ - data_layer_org_scope_present_after_patch
261
+ - no_unscoped_query_remains_in_route_handler
262
+
263
+ unsafe_to_expose_fields:
264
+ - internal_rubric
265
+ - golden_transcripts
266
+ - exploit_templates
@@ -0,0 +1,37 @@
1
+ # Control: external_validator_on_facts
2
+ # Referenced by:
3
+ # - prompting.self-consistency-no-isolation (META: none_of_controls)
4
+ #
5
+ # An external (non-LLM) check that the aggregated answer is factually
6
+ # consistent — a SQL query, an API call, a regex against ground truth, etc.
7
+
8
+ id: external_validator_on_facts
9
+ description: >
10
+ An external, non-LLM check applied to the aggregated answer before it's
11
+ acted on. Common shapes: factCheck()/validateFacts() helpers, schema
12
+ validation, lookup against a trusted store.
13
+
14
+ applicable_languages:
15
+ - typescript
16
+ - javascript
17
+ - python
18
+
19
+ detection:
20
+ - language: typescript
21
+ rule:
22
+ any:
23
+ - pattern: factCheck($$$)
24
+ - pattern: validateFacts($$$)
25
+ - pattern: validateAnswer($$$)
26
+ - pattern: $_.factCheck($$$)
27
+ - pattern: $_.validate($$$)
28
+ - language: python
29
+ rule:
30
+ any:
31
+ - pattern: fact_check($$$)
32
+ - pattern: validate_facts($$$)
33
+ - pattern: validate_answer($$$)
34
+ - pattern: $_.validate_against_ground_truth($$$)
35
+
36
+ owner: platform-threat-modeling
37
+ lifecycle: draft
@@ -0,0 +1,44 @@
1
+ # Control: human_approval_gate
2
+ # Referenced by:
3
+ # - prompting.self-consistency-no-isolation (META: none_of_controls)
4
+ # - agents.privilege-separation (potential — high-impact tool dispatch)
5
+ #
6
+ # Defines what "the codebase has a human-in-the-loop approval before
7
+ # committing a high-impact action" looks like. Detection is intentionally
8
+ # fuzzy — we'd rather over-detect approval gates (and miss firing META)
9
+ # than under-detect (and fire false-positive findings).
10
+
11
+ id: human_approval_gate
12
+ description: >
13
+ Detects code that pauses execution to wait for human approval before
14
+ committing a high-impact action. Common shapes: humanInTheLoop helpers,
15
+ LangGraph interrupts, await-on-approval queue reads, request-confirmation
16
+ prompts.
17
+
18
+ applicable_languages:
19
+ - typescript
20
+ - javascript
21
+ - python
22
+
23
+ detection:
24
+ - language: typescript
25
+ rule:
26
+ any:
27
+ - pattern: humanInTheLoop($$$)
28
+ - pattern: humanApproval($$$)
29
+ - pattern: requireApproval($$$)
30
+ - pattern: await $_.waitForApproval($$$)
31
+ - pattern: await $_.awaitApproval($$$)
32
+ - pattern: $_.interrupt($$$)
33
+ - language: python
34
+ rule:
35
+ any:
36
+ - pattern: human_in_the_loop($$$)
37
+ - pattern: human_approval($$$)
38
+ - pattern: require_approval($$$)
39
+ - pattern: wait_for_user($$$)
40
+ - pattern: await_approval($$$)
41
+ - pattern: $_.interrupt($$$)
42
+
43
+ owner: platform-threat-modeling
44
+ lifecycle: draft
@@ -0,0 +1,35 @@
1
+ # Control: langgraph_state_machine_with_per_node_tools
2
+ # Referenced by:
3
+ # - agents.privilege-separation (META: none_of_controls)
4
+ #
5
+ # LangGraph's idiom for privilege separation: a StateGraph with multiple
6
+ # nodes, each binding a different (smaller) tool set. When present, the
7
+ # capsule's anti-pattern (one shared tool list) is mitigated.
8
+
9
+ id: langgraph_state_machine_with_per_node_tools
10
+ description: >
11
+ LangGraph StateGraph with multiple add_node calls — each node typically
12
+ binds its own tools. Even imperfect detection of LangGraph node-graph
13
+ construction is enough to suppress the capsule's META rule, since the
14
+ customer is clearly using the right idiom.
15
+
16
+ applicable_languages:
17
+ - typescript
18
+ - javascript
19
+ - python
20
+
21
+ detection:
22
+ - language: python
23
+ rule:
24
+ any:
25
+ - pattern: $_.add_node($$$)
26
+ - pattern: StateGraph($$$)
27
+ - pattern: builder.add_node($$$)
28
+ - language: typescript
29
+ rule:
30
+ any:
31
+ - pattern: $_.addNode($$$)
32
+ - pattern: new StateGraph($$$)
33
+
34
+ owner: platform-threat-modeling
35
+ lifecycle: draft
@@ -0,0 +1,38 @@
1
+ # Control: per_sample_retrieval_diversification
2
+ # Referenced by:
3
+ # - prompting.self-consistency-no-isolation (META: none_of_controls)
4
+ #
5
+ # Different samples in a self-consistency loop pull from different
6
+ # retrieval queries / different stores / different rerank seeds, instead
7
+ # of all sharing the same RAG context.
8
+
9
+ id: per_sample_retrieval_diversification
10
+ description: >
11
+ When a self-consistency aggregator pulls retrieval per-sample from
12
+ varied queries (i.e. retrieval is INSIDE the per-sample lambda, not
13
+ hoisted above the loop). The shape is a .map over generation function
14
+ that itself contains a similaritySearch / vectorStore call.
15
+
16
+ applicable_languages:
17
+ - typescript
18
+ - javascript
19
+ - python
20
+
21
+ detection:
22
+ - language: typescript
23
+ rule:
24
+ pattern: $_.similaritySearch($$$)
25
+ inside:
26
+ stopBy: end
27
+ any:
28
+ - pattern: $_.map($$$)
29
+ - kind: for_statement
30
+ - language: python
31
+ rule:
32
+ pattern: $_.similarity_search($$$)
33
+ inside:
34
+ stopBy: end
35
+ kind: for_statement
36
+
37
+ owner: platform-threat-modeling
38
+ lifecycle: draft
@@ -0,0 +1,31 @@
1
+ # Control: prisma_extension_or_middleware_attaching_org_filter
2
+ # Referenced by:
3
+ # - security.late-org-filter (META: none_of_controls)
4
+ #
5
+ # A Prisma extension (or legacy $use middleware) that automatically appends
6
+ # a where-clause scoping every query by org/tenant. When present, the
7
+ # late-filter pattern is defense in depth, not the primary control.
8
+
9
+ id: prisma_extension_or_middleware_attaching_org_filter
10
+ description: >
11
+ Prisma client extension (post-5.x) or legacy $use middleware that injects
12
+ org/tenant scoping into every query. Detected by the call shape that
13
+ registers the extension/middleware referencing org_id, orgId, or tenant_id.
14
+
15
+ applicable_languages:
16
+ - typescript
17
+ - javascript
18
+
19
+ detection:
20
+ - language: typescript
21
+ rule:
22
+ any:
23
+ # Modern Prisma client extension intercepting query operations
24
+ - pattern: "$_.$$extends({ query: $$$ })"
25
+ - pattern: "prisma.$$extends($$$)"
26
+ # Legacy Prisma middleware
27
+ - pattern: "$_.$$use($$$)"
28
+ - pattern: "prisma.$$use($$$)"
29
+
30
+ owner: platform-threat-modeling
31
+ lifecycle: draft
@@ -0,0 +1,37 @@
1
+ # Control: row_level_security_policy
2
+ # Referenced by:
3
+ # - security.late-org-filter (META: none_of_controls)
4
+ #
5
+ # Detection of Postgres RLS policies in the codebase — either authored as
6
+ # SQL migration text inside a string literal, or invoked via session GUC
7
+ # setters that scope the connection by tenant.
8
+
9
+ id: row_level_security_policy
10
+ description: >
11
+ Postgres row-level security policy authored in the codebase, or session
12
+ GUCs that scope a connection by tenant for the duration of a request.
13
+
14
+ applicable_languages:
15
+ - typescript
16
+ - javascript
17
+ - python
18
+
19
+ detection:
20
+ - language: typescript
21
+ rule:
22
+ any:
23
+ # SET LOCAL app.org_id pattern (per-request connection scoping)
24
+ - pattern: $_.$$queryRaw`SET LOCAL $$$`
25
+ - pattern: $_.$$executeRaw`SET LOCAL $$$`
26
+ # Common helpers
27
+ - pattern: setRlsContext($$$)
28
+ - pattern: setOrgContext($$$)
29
+ - language: python
30
+ rule:
31
+ any:
32
+ - pattern: $_.execute("SET LOCAL $$$")
33
+ - pattern: set_rls_context($$$)
34
+ - pattern: set_org_context($$$)
35
+
36
+ owner: platform-threat-modeling
37
+ lifecycle: draft
@@ -0,0 +1,129 @@
1
+ {
2
+ "schema_version": "1.0.0",
3
+ "pack_id": "zivis-public",
4
+ "pack_name": "ZIVIS Public Pattern Pack",
5
+ "version": "0.1.0",
6
+ "built_at": "2026-05-07T12:24:59.867Z",
7
+ "tier": "customer_safe",
8
+ "description": "ZIVIS-curated public pattern pack — capsules + inference prompts evaluated locally on the user's machine.",
9
+ "capsules": [
10
+ {
11
+ "id": "agents.privilege-separation",
12
+ "version": "0.1.0",
13
+ "path": "capsules/agents/privilege-separation.yaml",
14
+ "sha256": "716f9c766add6f51eac2568eefc328a030901520cf0d3098ed577651c98773ea",
15
+ "category": "agents",
16
+ "lifecycle": "draft",
17
+ "applicable_languages": [
18
+ "typescript",
19
+ "javascript",
20
+ "python"
21
+ ]
22
+ },
23
+ {
24
+ "id": "prompting.self-consistency-no-isolation",
25
+ "version": "0.1.0",
26
+ "path": "capsules/prompting/self-consistency-no-isolation.yaml",
27
+ "sha256": "e1b11e02f7d13cfaeb8ed7f8ae7cae05062bcf8e6fbb226eed75e8bf7fa6e8d1",
28
+ "category": "prompting",
29
+ "lifecycle": "draft",
30
+ "applicable_languages": [
31
+ "typescript",
32
+ "javascript",
33
+ "python"
34
+ ]
35
+ },
36
+ {
37
+ "id": "security.late-org-filter",
38
+ "version": "0.1.0",
39
+ "path": "capsules/security/late-org-filter.yaml",
40
+ "sha256": "aeb9b327be936770ea6d208f6e687ae095a79976770e47b27ce5c97df3e43a3b",
41
+ "category": "security",
42
+ "lifecycle": "draft",
43
+ "applicable_languages": [
44
+ "typescript",
45
+ "javascript",
46
+ "python",
47
+ "go"
48
+ ]
49
+ }
50
+ ],
51
+ "prompts": [
52
+ {
53
+ "path": "prompts/agents/privilege-separation/identify.md",
54
+ "sha256": "eda7d288f9d1c6f7dfc18d2856300df76e8c4fdb5f18f3490f39ddf7117853e4",
55
+ "capsule_id": "agents.privilege-separation"
56
+ },
57
+ {
58
+ "path": "prompts/prompting/self-consistency-no-isolation/identify.md",
59
+ "sha256": "e05ffb777550a2a79b3c5b8e75d3e3c7fb2da2cd43ab0fcf202a9eda0f29c10f",
60
+ "capsule_id": "prompting.self-consistency-no-isolation"
61
+ },
62
+ {
63
+ "path": "prompts/security/late-org-filter/identify.md",
64
+ "sha256": "8e602179572ace5d457f8582b67e9d6ab29c675e82964481848c95e5ccb83e1e",
65
+ "capsule_id": "security.late-org-filter"
66
+ }
67
+ ],
68
+ "controls": [
69
+ {
70
+ "id": "external_validator_on_facts",
71
+ "path": "controls/external_validator_on_facts.yaml",
72
+ "sha256": "17ec428bf4cfc55b9f464d5c34e7a1ccc854a6c5822e1a4f6848db53ab3bccce",
73
+ "applicable_languages": [
74
+ "typescript",
75
+ "javascript",
76
+ "python"
77
+ ]
78
+ },
79
+ {
80
+ "id": "human_approval_gate",
81
+ "path": "controls/human_approval_gate.yaml",
82
+ "sha256": "ccafcb0bbf0fc68bb02fd018f87a870471fa4653363459293a24f0a65dc53b99",
83
+ "applicable_languages": [
84
+ "typescript",
85
+ "javascript",
86
+ "python"
87
+ ]
88
+ },
89
+ {
90
+ "id": "langgraph_state_machine_with_per_node_tools",
91
+ "path": "controls/langgraph_state_machine_with_per_node_tools.yaml",
92
+ "sha256": "29b46ad213173c52b5d96f744d74179193e61a6dc9c43e6b0b94b013ec94a32c",
93
+ "applicable_languages": [
94
+ "typescript",
95
+ "javascript",
96
+ "python"
97
+ ]
98
+ },
99
+ {
100
+ "id": "per_sample_retrieval_diversification",
101
+ "path": "controls/per_sample_retrieval_diversification.yaml",
102
+ "sha256": "9b4db7c6d620f46e93887f793a3e77bdabe280521b5ce0320d84d0c6b31ae653",
103
+ "applicable_languages": [
104
+ "typescript",
105
+ "javascript",
106
+ "python"
107
+ ]
108
+ },
109
+ {
110
+ "id": "prisma_extension_or_middleware_attaching_org_filter",
111
+ "path": "controls/prisma_extension_or_middleware_attaching_org_filter.yaml",
112
+ "sha256": "196676bebd107c4345a5f9227380982e5e2da0f2a8ad87e48517b5bd924abc31",
113
+ "applicable_languages": [
114
+ "typescript",
115
+ "javascript"
116
+ ]
117
+ },
118
+ {
119
+ "id": "row_level_security_policy",
120
+ "path": "controls/row_level_security_policy.yaml",
121
+ "sha256": "42d2a6f561a89b6dae80f04e25c65081fb22b660a59920e1f5c0ed438f394c68",
122
+ "applicable_languages": [
123
+ "typescript",
124
+ "javascript",
125
+ "python"
126
+ ]
127
+ }
128
+ ]
129
+ }
@@ -0,0 +1,54 @@
1
+ # Inference prompt: agents.privilege-separation — identify
2
+
3
+ You are assisting with secure agentic-AI architecture review. The user is
4
+ working on a codebase that has been flagged by deterministic detectors as
5
+ *possibly* lacking privilege separation between agents. Your job is to
6
+ look at the attached code context and produce a structured verdict.
7
+
8
+ ## Pattern definition
9
+
10
+ **Privilege separation** means each agent in a multi-agent system is bound
11
+ only to the minimum tools required for its role. The anti-pattern is a
12
+ single shared tool registry that every agent inherits.
13
+
14
+ ## What you are evaluating
15
+
16
+ 1. Does the code construct two or more agents (or two or more entry points
17
+ that invoke the same agent factory) with a **shared** tool array?
18
+ 2. Is there a per-agent capability filter, authorization layer, or
19
+ capability-token check between an agent and its tool dispatch? If so,
20
+ the pattern does *not* fire even when tools appear shared.
21
+ 3. If LangGraph is used: are tools bound per node (good), or is one tool
22
+ list passed to a top-level `create_react_agent` covering every node
23
+ (anti-pattern)?
24
+
25
+ ## What does NOT count as privilege separation
26
+
27
+ - Comments mentioning least privilege without code enforcing it.
28
+ - A shared tool list with one agent renamed at runtime.
29
+ - Defensive prompt instructions ("don't use the database tool unless…")
30
+ — prompts are not authorization.
31
+
32
+ ## Output
33
+
34
+ Return JSON only, matching the capsule's `expected_output_schema`:
35
+
36
+ ```json
37
+ {
38
+ "verdict": "absent | present | unclear",
39
+ "confidence": 0.0,
40
+ "rationale": "one paragraph",
41
+ "evidence_spans": [
42
+ { "file": "src/agent.ts", "start_line": 10, "end_line": 40, "note": "..." }
43
+ ]
44
+ }
45
+ ```
46
+
47
+ - `verdict: absent` — the anti-pattern fires (shared registry, no scoping).
48
+ - `verdict: present` — privilege separation is correctly implemented.
49
+ - `verdict: unclear` — the available context is ambiguous; do not guess.
50
+
51
+ Cite line numbers from the snippet only. Do not invent files. Do not
52
+ fabricate evidence. If a concept (e.g. "authorization layer") is mentioned
53
+ but you cannot identify the line that implements it, mark `unclear` and
54
+ explain.