@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.
- package/dist/matcher/ast-eval.d.ts +27 -0
- package/dist/matcher/ast-eval.js +132 -0
- package/dist/matcher/ast-eval.js.map +1 -0
- package/dist/matcher/finding.d.ts +76 -0
- package/dist/matcher/finding.js +14 -0
- package/dist/matcher/finding.js.map +1 -0
- package/dist/matcher/index.d.ts +30 -0
- package/dist/matcher/index.js +255 -0
- package/dist/matcher/index.js.map +1 -0
- package/dist/matcher/inference-candidates.d.ts +54 -0
- package/dist/matcher/inference-candidates.js +106 -0
- package/dist/matcher/inference-candidates.js.map +1 -0
- package/dist/matcher/walker.d.ts +14 -0
- package/dist/matcher/walker.js +79 -0
- package/dist/matcher/walker.js.map +1 -0
- package/dist/pattern-pack/index.d.ts +154 -0
- package/dist/pattern-pack/index.js +224 -0
- package/dist/pattern-pack/index.js.map +1 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/agents/privilege-separation.yaml +224 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/prompting/self-consistency-no-isolation.yaml +237 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/security/late-org-filter.yaml +266 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/external_validator_on_facts.yaml +37 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/human_approval_gate.yaml +44 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/langgraph_state_machine_with_per_node_tools.yaml +35 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/per_sample_retrieval_diversification.yaml +38 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/prisma_extension_or_middleware_attaching_org_filter.yaml +31 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/row_level_security_policy.yaml +37 -0
- package/dist/pattern-packs/zivis-public-0.1.0/manifest.json +129 -0
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/agents/privilege-separation/identify.md +54 -0
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/prompting/self-consistency-no-isolation/identify.md +60 -0
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/security/late-org-filter/identify.md +78 -0
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.js +1 -7
- package/dist/prompts/index.js.map +1 -1
- package/dist/server.js +36 -166
- package/dist/server.js.map +1 -1
- package/dist/tools/enterprise/get-threat-model-artifact.d.ts +30 -0
- package/dist/tools/enterprise/get-threat-model-artifact.js +80 -0
- package/dist/tools/enterprise/get-threat-model-artifact.js.map +1 -0
- package/dist/tools/enterprise/get-threat-model.d.ts +1 -1
- package/dist/tools/enterprise/get-threat-model.js +59 -11
- package/dist/tools/enterprise/get-threat-model.js.map +1 -1
- package/dist/tools/enterprise/list-threat-model-artifacts.d.ts +67 -0
- package/dist/tools/enterprise/list-threat-model-artifacts.js +121 -0
- package/dist/tools/enterprise/list-threat-model-artifacts.js.map +1 -0
- package/dist/tools/enterprise/manage-actor.d.ts +1 -1
- package/dist/tools/enterprise/manage-actor.js +14 -1
- package/dist/tools/enterprise/manage-actor.js.map +1 -1
- package/dist/tools/enterprise/manage-attack-scenario.d.ts +1 -1
- package/dist/tools/enterprise/manage-attack-scenario.js +13 -1
- package/dist/tools/enterprise/manage-attack-scenario.js.map +1 -1
- package/dist/tools/enterprise/manage-component.d.ts +1 -1
- package/dist/tools/enterprise/manage-component.js +20 -1
- package/dist/tools/enterprise/manage-component.js.map +1 -1
- package/dist/tools/enterprise/manage-data-asset.d.ts +2 -2
- package/dist/tools/enterprise/manage-data-asset.js +17 -1
- package/dist/tools/enterprise/manage-data-asset.js.map +1 -1
- package/dist/tools/enterprise/manage-data-flow.d.ts +2 -2
- package/dist/tools/enterprise/manage-data-flow.js +19 -1
- package/dist/tools/enterprise/manage-data-flow.js.map +1 -1
- package/dist/tools/enterprise/manage-security-control.d.ts +1 -1
- package/dist/tools/enterprise/manage-security-control.js +18 -1
- package/dist/tools/enterprise/manage-security-control.js.map +1 -1
- package/dist/tools/enterprise/manage-test-case.d.ts +2 -2
- package/dist/tools/enterprise/manage-test-case.js +8 -0
- package/dist/tools/enterprise/manage-test-case.js.map +1 -1
- package/dist/tools/get-started.d.ts +1 -1
- package/dist/tools/get-started.js +185 -8
- package/dist/tools/get-started.js.map +1 -1
- package/dist/tools/inspect.d.ts +38 -0
- package/dist/tools/inspect.js +252 -0
- package/dist/tools/inspect.js.map +1 -0
- package/dist/tools/manage-application.d.ts +1 -1
- package/dist/tools/security-review.js +67 -50
- package/dist/tools/security-review.js.map +1 -1
- package/dist/tools/threat-get-capsule.d.ts +25 -0
- package/dist/tools/threat-get-capsule.js +64 -0
- package/dist/tools/threat-get-capsule.js.map +1 -0
- package/dist/tools/threat-get-inference-prompt.d.ts +32 -0
- package/dist/tools/threat-get-inference-prompt.js +91 -0
- package/dist/tools/threat-get-inference-prompt.js.map +1 -0
- package/dist/tools/threat-guide.d.ts +39 -0
- package/dist/tools/threat-guide.js +111 -0
- package/dist/tools/threat-guide.js.map +1 -0
- package/dist/tools/threat-list-relevant-capsules.d.ts +30 -0
- package/dist/tools/threat-list-relevant-capsules.js +176 -0
- package/dist/tools/threat-list-relevant-capsules.js.map +1 -0
- package/dist/tools/threat-run-matcher.d.ts +27 -0
- package/dist/tools/threat-run-matcher.js +164 -0
- package/dist/tools/threat-run-matcher.js.map +1 -0
- package/package.json +13 -3
- package/dist/prompts/prep-for-launch.d.ts +0 -9
- package/dist/prompts/prep-for-launch.js +0 -62
- package/dist/prompts/prep-for-launch.js.map +0 -1
- package/dist/prompts/secure-my-app.d.ts +0 -9
- package/dist/prompts/secure-my-app.js +0 -70
- package/dist/prompts/secure-my-app.js.map +0 -1
- package/dist/prompts/weekly-check.d.ts +0 -9
- package/dist/prompts/weekly-check.js +0 -48
- package/dist/prompts/weekly-check.js.map +0 -1
- package/dist/tools/enterprise/capture-evidence.d.ts +0 -54
- package/dist/tools/enterprise/capture-evidence.js +0 -72
- package/dist/tools/enterprise/capture-evidence.js.map +0 -1
- package/dist/tools/enterprise/create-report-ticket.d.ts +0 -34
- package/dist/tools/enterprise/create-report-ticket.js +0 -44
- package/dist/tools/enterprise/create-report-ticket.js.map +0 -1
- package/dist/tools/enterprise/end-pentest-session.d.ts +0 -33
- package/dist/tools/enterprise/end-pentest-session.js +0 -42
- package/dist/tools/enterprise/end-pentest-session.js.map +0 -1
- package/dist/tools/enterprise/generate-report.d.ts +0 -55
- package/dist/tools/enterprise/generate-report.js +0 -111
- package/dist/tools/enterprise/generate-report.js.map +0 -1
- package/dist/tools/enterprise/get-campaign.d.ts +0 -27
- package/dist/tools/enterprise/get-campaign.js +0 -31
- package/dist/tools/enterprise/get-campaign.js.map +0 -1
- package/dist/tools/enterprise/get-report-status.d.ts +0 -27
- package/dist/tools/enterprise/get-report-status.js +0 -38
- package/dist/tools/enterprise/get-report-status.js.map +0 -1
- package/dist/tools/enterprise/list-agents.d.ts +0 -42
- package/dist/tools/enterprise/list-agents.js +0 -95
- package/dist/tools/enterprise/list-agents.js.map +0 -1
- package/dist/tools/enterprise/list-campaigns.d.ts +0 -37
- package/dist/tools/enterprise/list-campaigns.js +0 -60
- package/dist/tools/enterprise/list-campaigns.js.map +0 -1
- package/dist/tools/enterprise/list-report-issues.d.ts +0 -36
- package/dist/tools/enterprise/list-report-issues.js +0 -50
- package/dist/tools/enterprise/list-report-issues.js.map +0 -1
- package/dist/tools/enterprise/manage-campaign-agent-instance.d.ts +0 -42
- package/dist/tools/enterprise/manage-campaign-agent-instance.js +0 -68
- package/dist/tools/enterprise/manage-campaign-agent-instance.js.map +0 -1
- package/dist/tools/enterprise/manage-campaign-target.d.ts +0 -44
- package/dist/tools/enterprise/manage-campaign-target.js +0 -70
- package/dist/tools/enterprise/manage-campaign-target.js.map +0 -1
- package/dist/tools/enterprise/manage-campaign.d.ts +0 -62
- package/dist/tools/enterprise/manage-campaign.js +0 -114
- package/dist/tools/enterprise/manage-campaign.js.map +0 -1
- package/dist/tools/enterprise/manage-finding.d.ts +0 -69
- package/dist/tools/enterprise/manage-finding.js +0 -106
- package/dist/tools/enterprise/manage-finding.js.map +0 -1
- package/dist/tools/enterprise/promote-finding-to-issue.d.ts +0 -36
- package/dist/tools/enterprise/promote-finding-to-issue.js +0 -65
- package/dist/tools/enterprise/promote-finding-to-issue.js.map +0 -1
- package/dist/tools/enterprise/publish-report-version.d.ts +0 -34
- package/dist/tools/enterprise/publish-report-version.js +0 -75
- package/dist/tools/enterprise/publish-report-version.js.map +0 -1
- package/dist/tools/enterprise/publish-test-to-library.d.ts +0 -31
- package/dist/tools/enterprise/publish-test-to-library.js +0 -40
- package/dist/tools/enterprise/publish-test-to-library.js.map +0 -1
- package/dist/tools/enterprise/record-test-result.d.ts +0 -67
- package/dist/tools/enterprise/record-test-result.js +0 -75
- package/dist/tools/enterprise/record-test-result.js.map +0 -1
- package/dist/tools/enterprise/start-pentest-session.d.ts +0 -35
- package/dist/tools/enterprise/start-pentest-session.js +0 -50
- package/dist/tools/enterprise/start-pentest-session.js.map +0 -1
- package/dist/tools/enterprise/sync-report-section.d.ts +0 -41
- package/dist/tools/enterprise/sync-report-section.js +0 -79
- package/dist/tools/enterprise/sync-report-section.js.map +0 -1
- package/dist/tools/enterprise/update-report-issue.d.ts +0 -55
- package/dist/tools/enterprise/update-report-issue.js +0 -69
- package/dist/tools/enterprise/update-report-issue.js.map +0 -1
- package/dist/tools/get-agent-test-results.d.ts +0 -41
- package/dist/tools/get-agent-test-results.js +0 -86
- package/dist/tools/get-agent-test-results.js.map +0 -1
- package/dist/tools/get-application-auth-config.d.ts +0 -27
- package/dist/tools/get-application-auth-config.js +0 -44
- package/dist/tools/get-application-auth-config.js.map +0 -1
- package/dist/tools/get-artifact-content.d.ts +0 -29
- package/dist/tools/get-artifact-content.js +0 -86
- package/dist/tools/get-artifact-content.js.map +0 -1
- package/dist/tools/get-finding-details.d.ts +0 -22
- package/dist/tools/get-finding-details.js +0 -61
- package/dist/tools/get-finding-details.js.map +0 -1
- package/dist/tools/get-findings.d.ts +0 -51
- package/dist/tools/get-findings.js +0 -124
- package/dist/tools/get-findings.js.map +0 -1
- package/dist/tools/get-recon-scan.d.ts +0 -27
- package/dist/tools/get-recon-scan.js +0 -46
- package/dist/tools/get-recon-scan.js.map +0 -1
- package/dist/tools/get-scan-output.d.ts +0 -27
- package/dist/tools/get-scan-output.js +0 -64
- package/dist/tools/get-scan-output.js.map +0 -1
- package/dist/tools/get-scenario-details.d.ts +0 -27
- package/dist/tools/get-scenario-details.js +0 -46
- package/dist/tools/get-scenario-details.js.map +0 -1
- package/dist/tools/get-test-case-results.d.ts +0 -37
- package/dist/tools/get-test-case-results.js +0 -66
- package/dist/tools/get-test-case-results.js.map +0 -1
- package/dist/tools/get-test-details.d.ts +0 -29
- package/dist/tools/get-test-details.js +0 -49
- package/dist/tools/get-test-details.js.map +0 -1
- package/dist/tools/list-agent-test-cases.d.ts +0 -41
- package/dist/tools/list-agent-test-cases.js +0 -73
- package/dist/tools/list-agent-test-cases.js.map +0 -1
- package/dist/tools/list-recon-scans.d.ts +0 -36
- package/dist/tools/list-recon-scans.js +0 -74
- package/dist/tools/list-recon-scans.js.map +0 -1
- package/dist/tools/list-scan-artifacts.d.ts +0 -41
- package/dist/tools/list-scan-artifacts.js +0 -63
- package/dist/tools/list-scan-artifacts.js.map +0 -1
- package/dist/tools/list-scans.d.ts +0 -38
- package/dist/tools/list-scans.js +0 -125
- package/dist/tools/list-scans.js.map +0 -1
- package/dist/tools/list-test-library.d.ts +0 -44
- package/dist/tools/list-test-library.js +0 -83
- package/dist/tools/list-test-library.js.map +0 -1
- package/dist/tools/list-test-scenarios.d.ts +0 -25
- package/dist/tools/list-test-scenarios.js +0 -94
- package/dist/tools/list-test-scenarios.js.map +0 -1
- package/dist/tools/local-scan.d.ts +0 -40
- package/dist/tools/local-scan.js +0 -261
- package/dist/tools/local-scan.js.map +0 -1
- package/dist/tools/run-test-scenario.d.ts +0 -37
- package/dist/tools/run-test-scenario.js +0 -70
- package/dist/tools/run-test-scenario.js.map +0 -1
- package/dist/tools/scan-pr.d.ts +0 -27
- package/dist/tools/scan-pr.js +0 -51
- package/dist/tools/scan-pr.js.map +0 -1
- package/dist/tools/setup-red-team-target.d.ts +0 -73
- package/dist/tools/setup-red-team-target.js +0 -461
- package/dist/tools/setup-red-team-target.js.map +0 -1
- package/dist/tools/triage-finding.d.ts +0 -41
- package/dist/tools/triage-finding.js +0 -125
- 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
|
package/dist/pattern-packs/zivis-public-0.1.0/controls/per_sample_retrieval_diversification.yaml
ADDED
|
@@ -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.
|