@zivis/mcp 0.1.0-alpha.21 → 0.1.0-alpha.22
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/pattern-pack/index.d.ts +133 -0
- package/dist/pattern-pack/index.js +212 -0
- package/dist/pattern-pack/index.js.map +1 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/agents/privilege-separation.yaml +228 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/prompting/self-consistency-no-isolation.yaml +240 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/security/late-org-filter.yaml +270 -0
- package/dist/pattern-packs/zivis-public-0.1.0/manifest.json +68 -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 +26 -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-data-asset.d.ts +1 -1
- package/dist/tools/enterprise/manage-data-flow.d.ts +1 -1
- package/dist/tools/enterprise/manage-test-case.d.ts +1 -1
- package/dist/tools/inspect.d.ts +31 -0
- package/dist/tools/inspect.js +100 -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-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/package.json +10 -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
package/dist/pattern-packs/zivis-public-0.1.0/capsules/prompting/self-consistency-no-isolation.yaml
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Capsule: Self-consistency without context isolation
|
|
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: prompting.self-consistency-no-isolation
|
|
7
|
+
slug: self-consistency-no-isolation
|
|
8
|
+
version: 0.1.0
|
|
9
|
+
category: prompting
|
|
10
|
+
title: Self-Consistency Without Context Isolation
|
|
11
|
+
headline: Vote Without Independence
|
|
12
|
+
description: >
|
|
13
|
+
Detects code that aggregates multiple LLM samples (vote, consensus, ranking)
|
|
14
|
+
while every sample shares the same untrusted context. Majority voting only
|
|
15
|
+
filters random noise — when context is shared, correlated errors override
|
|
16
|
+
correctness and the vote count is mis-read as calibrated confidence.
|
|
17
|
+
|
|
18
|
+
detection_tier: public
|
|
19
|
+
scoring_tier: proprietary
|
|
20
|
+
|
|
21
|
+
applicable_languages:
|
|
22
|
+
- typescript
|
|
23
|
+
- javascript
|
|
24
|
+
- python
|
|
25
|
+
|
|
26
|
+
relevance_filter:
|
|
27
|
+
any_of:
|
|
28
|
+
- dependency: openai
|
|
29
|
+
direct_only: true
|
|
30
|
+
- dependency: "@anthropic-ai/sdk"
|
|
31
|
+
direct_only: true
|
|
32
|
+
- dependency: anthropic
|
|
33
|
+
direct_only: true
|
|
34
|
+
- dependency: "ai"
|
|
35
|
+
direct_only: true
|
|
36
|
+
- dependency: langchain
|
|
37
|
+
direct_only: true
|
|
38
|
+
- dependency: llama-index
|
|
39
|
+
direct_only: true
|
|
40
|
+
|
|
41
|
+
strategies:
|
|
42
|
+
- code: AST
|
|
43
|
+
language: python
|
|
44
|
+
expression: |
|
|
45
|
+
rule:
|
|
46
|
+
kind: for_statement
|
|
47
|
+
has:
|
|
48
|
+
kind: call
|
|
49
|
+
any:
|
|
50
|
+
- pattern: $CLIENT.chat.completions.create($$$)
|
|
51
|
+
- pattern: $CLIENT.messages.create($$$)
|
|
52
|
+
- pattern: openai.ChatCompletion.create($$$)
|
|
53
|
+
evidence_fields: [file_path, line_span, symbol]
|
|
54
|
+
confidence_weight: 0.30
|
|
55
|
+
|
|
56
|
+
- code: AST
|
|
57
|
+
language: typescript
|
|
58
|
+
expression: |
|
|
59
|
+
rule:
|
|
60
|
+
any:
|
|
61
|
+
- pattern: |
|
|
62
|
+
for ($_; $_; $_) {
|
|
63
|
+
$$$
|
|
64
|
+
generateText($$$)
|
|
65
|
+
$$$
|
|
66
|
+
}
|
|
67
|
+
- pattern: |
|
|
68
|
+
[...].map($_ => generateText($$$))
|
|
69
|
+
- pattern: |
|
|
70
|
+
Promise.all([... .map($_ => $CLIENT.chat.completions.create($$$))])
|
|
71
|
+
evidence_fields: [file_path, line_span, symbol]
|
|
72
|
+
confidence_weight: 0.30
|
|
73
|
+
|
|
74
|
+
- code: GRAPH
|
|
75
|
+
expression: |
|
|
76
|
+
same_messages_argument_passed_to_n_completion_calls(n >= 2)
|
|
77
|
+
AND aggregator_observed(any_of: Counter, max(set(.), key=.count), majority_vote, np.argmax, statistics.mode)
|
|
78
|
+
evidence_fields: [graph_path, file_spans, node_ids]
|
|
79
|
+
confidence_weight: 0.30
|
|
80
|
+
|
|
81
|
+
- code: META
|
|
82
|
+
expression: |
|
|
83
|
+
NOT exists_any_of(
|
|
84
|
+
per_sample_retrieval_diversification,
|
|
85
|
+
per_sample_temperature_variation,
|
|
86
|
+
per_sample_seed_variation,
|
|
87
|
+
external_validator_on_facts,
|
|
88
|
+
human_approval_gate
|
|
89
|
+
)
|
|
90
|
+
evidence_fields: [missing_controls]
|
|
91
|
+
confidence_weight: 0.25
|
|
92
|
+
|
|
93
|
+
- code: INFER
|
|
94
|
+
expression: prompt_id:prompting.self-consistency-no-isolation.identify
|
|
95
|
+
evidence_fields: [llm_rationale]
|
|
96
|
+
confidence_weight: 0.15
|
|
97
|
+
trusted: false
|
|
98
|
+
|
|
99
|
+
required_evidence_count: 3
|
|
100
|
+
|
|
101
|
+
control_negations:
|
|
102
|
+
- "presence_of(per_sample_retrieval_diversification)"
|
|
103
|
+
- "presence_of(external_validator_on_facts)"
|
|
104
|
+
- "presence_of(human_approval_gate)"
|
|
105
|
+
|
|
106
|
+
threats:
|
|
107
|
+
- title: Vote count mistaken for calibrated confidence
|
|
108
|
+
stride: [tampering, repudiation]
|
|
109
|
+
description: >
|
|
110
|
+
The application treats agreement across samples as independent confirmation,
|
|
111
|
+
when in fact every sample saw the same poisoned retrieval context or the
|
|
112
|
+
same prompt-injection payload. Downstream actions (auto-approve, send,
|
|
113
|
+
execute) fire under false high-confidence signal.
|
|
114
|
+
remediation: >
|
|
115
|
+
Diversify per-sample retrieval queries, temperatures, or seeds.
|
|
116
|
+
Add an external fact-validator. For high-impact outputs, require a human
|
|
117
|
+
approval gate. Never expose vote count as a confidence number to users
|
|
118
|
+
without disclosing the independence assumption.
|
|
119
|
+
- title: Correlated injection amplification
|
|
120
|
+
stride: [tampering]
|
|
121
|
+
description: >
|
|
122
|
+
A single prompt-injection payload in shared retrieved context biases all N
|
|
123
|
+
samples identically; majority vote then ratifies the attack rather than
|
|
124
|
+
filtering it.
|
|
125
|
+
remediation: >
|
|
126
|
+
Per-sample context isolation. Run independent retrieval (different queries
|
|
127
|
+
or different stores) per sample so an injection only contaminates a subset.
|
|
128
|
+
|
|
129
|
+
fixtures:
|
|
130
|
+
positive:
|
|
131
|
+
- "fixtures/positive/self-consistency-no-isolation/openai_loop_vote_python"
|
|
132
|
+
- "fixtures/positive/self-consistency-no-isolation/anthropic_promise_all_typescript"
|
|
133
|
+
negative:
|
|
134
|
+
- "fixtures/negative/self-consistency-no-isolation/per_sample_diverse_retrieval"
|
|
135
|
+
- "fixtures/negative/self-consistency-no-isolation/single_completion_no_aggregation"
|
|
136
|
+
|
|
137
|
+
lifecycle: draft
|
|
138
|
+
owner: platform-threat-modeling
|
|
139
|
+
tags:
|
|
140
|
+
- prompting
|
|
141
|
+
- self-consistency
|
|
142
|
+
- context-isolation
|
|
143
|
+
- prompt-injection-amplification
|
|
144
|
+
|
|
145
|
+
sensitivity_level: customer_safe
|
|
146
|
+
allowed_execution_modes:
|
|
147
|
+
- local_only
|
|
148
|
+
- local_model
|
|
149
|
+
- hybrid_cloud_eval
|
|
150
|
+
|
|
151
|
+
safe_summary: >
|
|
152
|
+
Multiple LLM completions are aggregated by voting or consensus, but every
|
|
153
|
+
sample sees the same retrieved context or the same user message. Independence
|
|
154
|
+
is assumed and not enforced. A single prompt-injection or a single
|
|
155
|
+
hallucinated retrieval contaminates the vote uniformly; majority count is
|
|
156
|
+
mistaken for calibrated confidence and downstream actions fire under
|
|
157
|
+
false certainty.
|
|
158
|
+
|
|
159
|
+
detect_when: >
|
|
160
|
+
Two or more LLM completions for the same user task are merged by voting,
|
|
161
|
+
consensus, or similarity scoring without independent context isolation —
|
|
162
|
+
no per-sample retrieval diversification, no external validator, no human gate.
|
|
163
|
+
|
|
164
|
+
risk_hints:
|
|
165
|
+
- Shared retrieval/injection in the user message affects every sample the same way
|
|
166
|
+
- Majority vote amplifies systematic bias rather than filtering noise
|
|
167
|
+
- Vote count surfaced as a confidence number is a UX lie when context is shared
|
|
168
|
+
- Async Promise.all with the same messages array is the most common JS shape
|
|
169
|
+
|
|
170
|
+
architectural_signals:
|
|
171
|
+
- Same `messages` argument passed to N completion calls inside a loop or Promise.all
|
|
172
|
+
- majority_vote, Counter, np.argmax, statistics.mode applied to the result array
|
|
173
|
+
- Single retrieval call upstream feeding all samples
|
|
174
|
+
|
|
175
|
+
model_task_prompt_ref: prompts/prompting/self-consistency-no-isolation/identify.md
|
|
176
|
+
|
|
177
|
+
expected_output_schema:
|
|
178
|
+
type: object
|
|
179
|
+
required: [independence_assessment, confidence_claim_risk, evidence_spans]
|
|
180
|
+
properties:
|
|
181
|
+
independence_assessment:
|
|
182
|
+
type: string
|
|
183
|
+
enum: [shared_context, isolated_context, unclear]
|
|
184
|
+
confidence_claim_risk:
|
|
185
|
+
type: string
|
|
186
|
+
enum: [high, medium, low]
|
|
187
|
+
description: How dangerous is the confidence claim users see, given the (lack of) independence
|
|
188
|
+
rationale:
|
|
189
|
+
type: string
|
|
190
|
+
recommended_controls:
|
|
191
|
+
type: array
|
|
192
|
+
items:
|
|
193
|
+
type: string
|
|
194
|
+
enum: [per_sample_retrieval_diversification, external_validator, human_approval, temperature_variation, seed_variation]
|
|
195
|
+
evidence_spans:
|
|
196
|
+
type: array
|
|
197
|
+
items:
|
|
198
|
+
type: object
|
|
199
|
+
required: [file]
|
|
200
|
+
properties:
|
|
201
|
+
file: { type: string }
|
|
202
|
+
start_line: { type: integer }
|
|
203
|
+
end_line: { type: integer }
|
|
204
|
+
note: { type: string }
|
|
205
|
+
|
|
206
|
+
minimum_context_required:
|
|
207
|
+
lines: 80
|
|
208
|
+
symbols:
|
|
209
|
+
- chat.completions.create
|
|
210
|
+
- messages.create
|
|
211
|
+
- generateText
|
|
212
|
+
- Counter
|
|
213
|
+
- majority
|
|
214
|
+
- np.argmax
|
|
215
|
+
|
|
216
|
+
related_patterns:
|
|
217
|
+
- prompting.chain-of-thought-leakage
|
|
218
|
+
- retrieval.shared-context-poisoning
|
|
219
|
+
|
|
220
|
+
repair_contract:
|
|
221
|
+
allowed:
|
|
222
|
+
- Diversify retrieval queries per sample
|
|
223
|
+
- Vary temperature or seed across samples
|
|
224
|
+
- Add an external fact validator on aggregated output
|
|
225
|
+
- Add a human approval gate before high-impact actions
|
|
226
|
+
forbidden:
|
|
227
|
+
- Silently treating vote count as calibrated confidence
|
|
228
|
+
- Removing the aggregation entirely for performance reasons (does not address the architectural issue)
|
|
229
|
+
- Adding more samples without addressing context independence
|
|
230
|
+
|
|
231
|
+
validation_contract:
|
|
232
|
+
requires_tests_pass: false
|
|
233
|
+
requires_static_rules:
|
|
234
|
+
- aggregator_remains_present_after_patch
|
|
235
|
+
- independence_mechanism_introduced
|
|
236
|
+
|
|
237
|
+
unsafe_to_expose_fields:
|
|
238
|
+
- internal_rubric
|
|
239
|
+
- golden_transcripts
|
|
240
|
+
- exploit_templates
|
|
@@ -0,0 +1,270 @@
|
|
|
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: |
|
|
59
|
+
const $ROWS = await $QUERY($$$);
|
|
60
|
+
$ROWS.filter($_ => $_.orgId === $REQ.orgId).$$$;
|
|
61
|
+
- pattern: |
|
|
62
|
+
const $ROWS = await $QUERY($$$);
|
|
63
|
+
for (const $R of $ROWS) {
|
|
64
|
+
if ($R.orgId === $REQ.orgId) { $$$ }
|
|
65
|
+
}
|
|
66
|
+
# SSE / stream where filter applied per emit, not at subscription.
|
|
67
|
+
- pattern: |
|
|
68
|
+
$STREAM.on("$_", $EVENT => {
|
|
69
|
+
if ($EVENT.orgId === $REQ.orgId) { $$$ }
|
|
70
|
+
});
|
|
71
|
+
evidence_fields: [file_path, line_span, symbol]
|
|
72
|
+
confidence_weight: 0.35
|
|
73
|
+
|
|
74
|
+
- code: AST
|
|
75
|
+
language: python
|
|
76
|
+
expression: |
|
|
77
|
+
rule:
|
|
78
|
+
any:
|
|
79
|
+
- pattern: |
|
|
80
|
+
rows = $QUERY($$$)
|
|
81
|
+
filtered = [r for r in rows if r.org_id == $REQ.org_id]
|
|
82
|
+
- pattern: |
|
|
83
|
+
for row in $QUERY($$$):
|
|
84
|
+
if row.org_id == request.org_id:
|
|
85
|
+
yield row
|
|
86
|
+
evidence_fields: [file_path, line_span, symbol]
|
|
87
|
+
confidence_weight: 0.35
|
|
88
|
+
|
|
89
|
+
- code: GRAPH
|
|
90
|
+
expression: |
|
|
91
|
+
query_call_returns_rows
|
|
92
|
+
AND filter_predicate_references(req_org_id OR session_org_id)
|
|
93
|
+
AND filter_executes_AFTER(query_call) IN response_handler_or_stream_emit
|
|
94
|
+
AND query_call.where_clause DOES_NOT include org_id
|
|
95
|
+
evidence_fields: [graph_path, file_spans, node_ids]
|
|
96
|
+
confidence_weight: 0.30
|
|
97
|
+
|
|
98
|
+
- code: META
|
|
99
|
+
expression: |
|
|
100
|
+
NOT exists_any_of(
|
|
101
|
+
row_level_security_policy,
|
|
102
|
+
query_helper_with_implicit_org_scope,
|
|
103
|
+
prisma_extension_or_middleware_attaching_org_filter,
|
|
104
|
+
sqlalchemy_loader_option_with_org_scope
|
|
105
|
+
)
|
|
106
|
+
evidence_fields: [missing_controls]
|
|
107
|
+
confidence_weight: 0.20
|
|
108
|
+
|
|
109
|
+
- code: INFER
|
|
110
|
+
expression: prompt_id:security.late-org-filter.identify
|
|
111
|
+
evidence_fields: [llm_rationale]
|
|
112
|
+
confidence_weight: 0.15
|
|
113
|
+
trusted: false
|
|
114
|
+
|
|
115
|
+
required_evidence_count: 3
|
|
116
|
+
|
|
117
|
+
control_negations:
|
|
118
|
+
- "presence_of(row_level_security_policy)"
|
|
119
|
+
- "presence_of(query_helper_with_implicit_org_scope)"
|
|
120
|
+
- "presence_of(prisma_extension_or_middleware_attaching_org_filter)"
|
|
121
|
+
|
|
122
|
+
threats:
|
|
123
|
+
- title: Cross-tenant data leak via SSE / stream pre-filter window
|
|
124
|
+
stride: [information_disclosure, elevation_of_privilege]
|
|
125
|
+
description: >
|
|
126
|
+
In a streaming or async-iterator response, the underlying query returns
|
|
127
|
+
rows from all tenants and the per-emit filter rejects mismatches. A
|
|
128
|
+
brief window where rows are buffered, logged, traced, or partially
|
|
129
|
+
rendered before the filter executes can leak data across tenants. This
|
|
130
|
+
is the SSE / queue/stream variant of the pattern.
|
|
131
|
+
remediation: >
|
|
132
|
+
Push the org/tenant predicate into the data layer — RLS, query helpers,
|
|
133
|
+
ORM middleware. Make every query mandatorily org-scoped at the boundary;
|
|
134
|
+
treat presentation-layer filters as defense in depth, never the primary
|
|
135
|
+
barrier.
|
|
136
|
+
- title: Cross-tenant leak via error path or partial response
|
|
137
|
+
stride: [information_disclosure]
|
|
138
|
+
description: >
|
|
139
|
+
An exception inside the post-query filter loop returns the unfiltered
|
|
140
|
+
buffer or a serialization mid-state. Other tenants' rows reach the
|
|
141
|
+
wire because the only org check sits past the failure point.
|
|
142
|
+
remediation: >
|
|
143
|
+
Same control — RLS or query-time scoping. Plus: serialize the response
|
|
144
|
+
body only after filtering completes; on filter error, return a
|
|
145
|
+
structured error with no payload.
|
|
146
|
+
- title: Logged or traced rows expose other tenants
|
|
147
|
+
stride: [information_disclosure, repudiation]
|
|
148
|
+
description: >
|
|
149
|
+
Observability hooks (request logger, tracer, metrics) often capture the
|
|
150
|
+
raw query result before the presentation-layer filter runs. Other
|
|
151
|
+
tenants' identifiers, names, or PII land in logs the requesting tenant
|
|
152
|
+
should never have seen.
|
|
153
|
+
remediation: >
|
|
154
|
+
Either log only post-filter results, or — better — make the query
|
|
155
|
+
itself org-scoped so the unfiltered result never exists.
|
|
156
|
+
|
|
157
|
+
fixtures:
|
|
158
|
+
positive:
|
|
159
|
+
- "fixtures/positive/late-org-filter/express_sse_post_filter_typescript"
|
|
160
|
+
- "fixtures/positive/late-org-filter/fastapi_iterator_post_filter_python"
|
|
161
|
+
negative:
|
|
162
|
+
- "fixtures/negative/late-org-filter/prisma_middleware_org_scope"
|
|
163
|
+
- "fixtures/negative/late-org-filter/postgres_rls_enabled"
|
|
164
|
+
|
|
165
|
+
lifecycle: draft
|
|
166
|
+
owner: platform-threat-modeling
|
|
167
|
+
tags:
|
|
168
|
+
- multi-tenant
|
|
169
|
+
- cross-tenant-leak
|
|
170
|
+
- data-scoping
|
|
171
|
+
- sse
|
|
172
|
+
- stream
|
|
173
|
+
|
|
174
|
+
sensitivity_level: customer_safe
|
|
175
|
+
allowed_execution_modes:
|
|
176
|
+
- local_only
|
|
177
|
+
- local_model
|
|
178
|
+
- hybrid_cloud_eval
|
|
179
|
+
|
|
180
|
+
safe_summary: >
|
|
181
|
+
Tenant filtering applied after the query — at the response loop, the SSE
|
|
182
|
+
emit, or the stream iterator — instead of inside the query. Any error,
|
|
183
|
+
race, observability hook, or partial response that fires before the filter
|
|
184
|
+
reaches the wire leaks rows across tenants. The fix is to push org/tenant
|
|
185
|
+
scoping down to the data layer (RLS, query helpers, ORM middleware) so the
|
|
186
|
+
unfiltered set never exists in the first place.
|
|
187
|
+
|
|
188
|
+
detect_when: >
|
|
189
|
+
A handler, stream, or async iterator runs a data query, then filters the
|
|
190
|
+
result by `request.orgId` (or a session org) before responding — and the
|
|
191
|
+
query itself doesn't include the org predicate. Multi-tenant routes only.
|
|
192
|
+
|
|
193
|
+
risk_hints:
|
|
194
|
+
- The bug class behind a meaningful share of cross-tenant SaaS incidents
|
|
195
|
+
- SSE / queue / WebSocket variants are the hardest to spot because the filter looks "inside" the loop
|
|
196
|
+
- Logs and traces often capture the unfiltered set even when the response itself is clean
|
|
197
|
+
- Tests often pass because they only run with a single tenant in the test database
|
|
198
|
+
|
|
199
|
+
architectural_signals:
|
|
200
|
+
- Query call followed by an explicit filter referencing a request/session org id
|
|
201
|
+
- SSE or async iterator that yields events conditional on a tenant match
|
|
202
|
+
- No row-level security policy, no implicit-org-scope helper, no ORM middleware
|
|
203
|
+
|
|
204
|
+
model_task_prompt_ref: prompts/security/late-org-filter/identify.md
|
|
205
|
+
|
|
206
|
+
expected_output_schema:
|
|
207
|
+
type: object
|
|
208
|
+
required: [verdict, scope_location, evidence_spans]
|
|
209
|
+
properties:
|
|
210
|
+
verdict:
|
|
211
|
+
type: string
|
|
212
|
+
enum: [late_filter_present, scoped_at_query, partial_scope, unclear]
|
|
213
|
+
scope_location:
|
|
214
|
+
type: string
|
|
215
|
+
enum: [data_layer, query_layer, presentation_layer, multiple, none]
|
|
216
|
+
rationale:
|
|
217
|
+
type: string
|
|
218
|
+
blast_radius:
|
|
219
|
+
type: string
|
|
220
|
+
enum: [single_endpoint, route_family, service_wide, unclear]
|
|
221
|
+
recommended_controls:
|
|
222
|
+
type: array
|
|
223
|
+
items:
|
|
224
|
+
type: string
|
|
225
|
+
enum: [row_level_security, query_helper, orm_middleware, repository_pattern_with_org_scope, none]
|
|
226
|
+
evidence_spans:
|
|
227
|
+
type: array
|
|
228
|
+
items:
|
|
229
|
+
type: object
|
|
230
|
+
required: [file]
|
|
231
|
+
properties:
|
|
232
|
+
file: { type: string }
|
|
233
|
+
start_line: { type: integer }
|
|
234
|
+
end_line: { type: integer }
|
|
235
|
+
note: { type: string }
|
|
236
|
+
|
|
237
|
+
minimum_context_required:
|
|
238
|
+
lines: 100
|
|
239
|
+
symbols:
|
|
240
|
+
- orgId
|
|
241
|
+
- org_id
|
|
242
|
+
- tenant_id
|
|
243
|
+
- filter
|
|
244
|
+
- SSE
|
|
245
|
+
- stream
|
|
246
|
+
|
|
247
|
+
related_patterns:
|
|
248
|
+
- security.idor-via-direct-id
|
|
249
|
+
- security.broken-object-level-authz
|
|
250
|
+
|
|
251
|
+
repair_contract:
|
|
252
|
+
allowed:
|
|
253
|
+
- Push org/tenant scoping into the query (where clause, RLS, ORM middleware)
|
|
254
|
+
- Introduce a repository wrapper that mandates org_id on every method
|
|
255
|
+
- Add a Postgres row-level security policy
|
|
256
|
+
- Keep the presentation-layer filter as defense in depth IN ADDITION TO the data-layer scope
|
|
257
|
+
forbidden:
|
|
258
|
+
- Removing the presentation-layer filter without first introducing a data-layer scope (regresses safety)
|
|
259
|
+
- Treating the patch as complete after only fixing the immediate route — the bug class is structural
|
|
260
|
+
|
|
261
|
+
validation_contract:
|
|
262
|
+
requires_tests_pass: false
|
|
263
|
+
requires_static_rules:
|
|
264
|
+
- data_layer_org_scope_present_after_patch
|
|
265
|
+
- no_unscoped_query_remains_in_route_handler
|
|
266
|
+
|
|
267
|
+
unsafe_to_expose_fields:
|
|
268
|
+
- internal_rubric
|
|
269
|
+
- golden_transcripts
|
|
270
|
+
- exploit_templates
|
|
@@ -0,0 +1,68 @@
|
|
|
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-06T21:01:49.106Z",
|
|
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": "31b5984b167ab97ef5c8902327bc80363d1ed3c43d08c82d979b0e34f2b88fde",
|
|
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": "1663fef4b9d544e7ef067501becde5d499eb64e439cf88255b4fae2cdc4d53ea",
|
|
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": "34ef82b1da0f690af66579a09355674dd132755fbfc0411ea7c7ef5bbb1b3270",
|
|
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
|
+
}
|
|
@@ -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.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Inference prompt: prompting.self-consistency-no-isolation — identify
|
|
2
|
+
|
|
3
|
+
You are reviewing code that has been flagged as *possibly* aggregating multiple
|
|
4
|
+
LLM samples without context isolation. Your job is to determine whether each
|
|
5
|
+
sample actually sees independent context, or whether they all share the same
|
|
6
|
+
input — making the aggregation a confidence-amplifier rather than a
|
|
7
|
+
confidence-filter.
|
|
8
|
+
|
|
9
|
+
## What you are evaluating
|
|
10
|
+
|
|
11
|
+
1. How many LLM completion calls happen for one user request? Identify the
|
|
12
|
+
loop, `Promise.all`, list comprehension, or fan-out construct.
|
|
13
|
+
2. Does each sample receive the **same** `messages` / prompt argument verbatim,
|
|
14
|
+
or is there per-sample variation (different retrieval query, different
|
|
15
|
+
temperature, different seed, different context window)?
|
|
16
|
+
3. How is the result aggregated? Voting, ranking, similarity-based merging,
|
|
17
|
+
first-passing-validator, or simple averaging?
|
|
18
|
+
4. Is there an external check on the aggregated output — e.g. a fact validator,
|
|
19
|
+
tool-output validation, human approval gate?
|
|
20
|
+
5. Does the user-facing surface advertise the vote count as a "confidence"
|
|
21
|
+
number? That's the strongest tell that independence is being assumed.
|
|
22
|
+
|
|
23
|
+
## What does NOT count as context isolation
|
|
24
|
+
|
|
25
|
+
- Different agent *names* with the same prompt and same retrieval.
|
|
26
|
+
- Comments saying "ensemble" without per-sample variation in the call.
|
|
27
|
+
- Setting `n=N` on a single OpenAI completion call (the model produces N
|
|
28
|
+
samples but they share the entire prompt and context — same problem).
|
|
29
|
+
- Different temperatures on every sample WITH the same retrieval — partial
|
|
30
|
+
mitigation but not full isolation; mark `unclear` and explain.
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
Return JSON only, matching the capsule's `expected_output_schema`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"independence_assessment": "shared_context | isolated_context | unclear",
|
|
39
|
+
"confidence_claim_risk": "high | medium | low",
|
|
40
|
+
"rationale": "one paragraph",
|
|
41
|
+
"recommended_controls": [
|
|
42
|
+
"per_sample_retrieval_diversification",
|
|
43
|
+
"external_validator",
|
|
44
|
+
"human_approval"
|
|
45
|
+
],
|
|
46
|
+
"evidence_spans": [
|
|
47
|
+
{ "file": "src/agent.ts", "start_line": 10, "end_line": 40, "note": "..." }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `shared_context` — every sample sees identical input. The vote is
|
|
53
|
+
amplifying rather than filtering. `confidence_claim_risk` should be `high`
|
|
54
|
+
if any user-facing label calls the vote count a confidence.
|
|
55
|
+
- `isolated_context` — per-sample retrieval, temperature, or seed variation
|
|
56
|
+
IS present. The aggregation is doing real work.
|
|
57
|
+
- `unclear` — context plumbing is too complex to determine from the snippet,
|
|
58
|
+
or the code uses a custom aggregator. Don't guess; ask in the rationale.
|
|
59
|
+
|
|
60
|
+
Cite line numbers from the snippet only. Do not fabricate evidence.
|