@polygraphso/litmus 0.8.1 → 0.9.1

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.
@@ -1,250 +0,0 @@
1
- import {
2
- parseAuthFlags,
3
- resolveTarget
4
- } from "./chunk-VOPISHBU.js";
5
- import {
6
- runLitmus
7
- } from "./chunk-35UOPCBW.js";
8
- import {
9
- CATEGORY_STATUS_UINT8,
10
- METHODOLOGY_VERSION
11
- } from "./chunk-ZR6XRGMQ.js";
12
-
13
- // ../onchain/src/networks.ts
14
- var NETWORKS = {
15
- "base-sepolia": {
16
- chainId: 84532,
17
- rpc: "https://sepolia.base.org",
18
- eas: "0x4200000000000000000000000000000000000021",
19
- schemaRegistry: "0x4200000000000000000000000000000000000020",
20
- easscan: "https://base-sepolia.easscan.org"
21
- },
22
- base: {
23
- chainId: 8453,
24
- rpc: "https://mainnet.base.org",
25
- eas: "0x4200000000000000000000000000000000000021",
26
- schemaRegistry: "0x4200000000000000000000000000000000000020",
27
- easscan: "https://base.easscan.org"
28
- }
29
- };
30
- function selectedNetwork() {
31
- return process.env.NEXT_PUBLIC_POLYGRAPH_NETWORK === "base" ? "base" : "base-sepolia";
32
- }
33
- function networkConfig(net = selectedNetwork()) {
34
- return NETWORKS[net];
35
- }
36
- function rpcUrl(net = selectedNetwork()) {
37
- const override = net === "base" ? process.env.BASE_MAINNET_RPC_URL : process.env.BASE_SEPOLIA_RPC_URL;
38
- return override && override.length > 0 ? override : NETWORKS[net].rpc;
39
- }
40
-
41
- // ../onchain/src/eas.ts
42
- import { AbiCoder } from "ethers";
43
- var LITMUS_SCHEMA = "string serverRef,bytes32 toolDefsFingerprint,uint8 gradeC01,uint8 gradeC02,uint8 gradeC03,string overallGrade,string reportCID,string methodologyVersion,uint64 ranAt,string resolvedVersion";
44
- var LITMUS_ABI_TYPES = [
45
- "string",
46
- // serverRef
47
- "bytes32",
48
- // toolDefsFingerprint
49
- "uint8",
50
- // gradeC01
51
- "uint8",
52
- // gradeC02
53
- "uint8",
54
- // gradeC03
55
- "string",
56
- // overallGrade
57
- "string",
58
- // reportCID
59
- "string",
60
- // methodologyVersion
61
- "uint64",
62
- // ranAt
63
- "string"
64
- // resolvedVersion
65
- ];
66
- var LITMUS_ABI_NAMES = [
67
- "serverRef",
68
- "toolDefsFingerprint",
69
- "gradeC01",
70
- "gradeC02",
71
- "gradeC03",
72
- "overallGrade",
73
- "reportCID",
74
- "methodologyVersion",
75
- "ranAt",
76
- "resolvedVersion"
77
- ];
78
- function categoryUint8(bundle, code) {
79
- const status = bundle.categories.find((c) => c.code === code)?.status;
80
- return status ? CATEGORY_STATUS_UINT8[status] : CATEGORY_STATUS_UINT8.skipped;
81
- }
82
- function litmusFields(bundle, reportCID) {
83
- return {
84
- serverRef: bundle.serverRef,
85
- toolDefsFingerprint: bundle.toolDefsFingerprint,
86
- gradeC01: categoryUint8(bundle, "C-01"),
87
- gradeC02: categoryUint8(bundle, "C-02"),
88
- gradeC03: categoryUint8(bundle, "C-03"),
89
- overallGrade: bundle.grade,
90
- reportCID,
91
- methodologyVersion: bundle.methodologyVersion || METHODOLOGY_VERSION,
92
- ranAt: BigInt(Math.floor(Date.parse(bundle.ranAt) / 1e3)),
93
- resolvedVersion: bundle.resolvedVersion ?? ""
94
- };
95
- }
96
- function encodeLitmusAttestation(bundle, reportCID) {
97
- const f = litmusFields(bundle, reportCID);
98
- return AbiCoder.defaultAbiCoder().encode(
99
- [...LITMUS_ABI_TYPES],
100
- [
101
- f.serverRef,
102
- f.toolDefsFingerprint,
103
- f.gradeC01,
104
- f.gradeC02,
105
- f.gradeC03,
106
- f.overallGrade,
107
- f.reportCID,
108
- f.methodologyVersion,
109
- f.ranAt,
110
- f.resolvedVersion
111
- ]
112
- );
113
- }
114
- function decodeLitmusAttestation(encoded) {
115
- const values = AbiCoder.defaultAbiCoder().decode([...LITMUS_ABI_TYPES], encoded);
116
- const out = {};
117
- LITMUS_ABI_NAMES.forEach((name, i) => {
118
- out[name] = values[i];
119
- });
120
- return out;
121
- }
122
-
123
- // ../onchain/src/read.ts
124
- import { Contract, JsonRpcProvider, ZeroHash } from "ethers";
125
- var EAS_ABI = [
126
- "function getAttestation(bytes32 uid) view returns ((bytes32 uid, bytes32 schema, uint64 time, uint64 expirationTime, uint64 revocationTime, bytes32 refUID, address recipient, address attester, bool revocable, bytes data))"
127
- ];
128
- function litmusSchemaUID() {
129
- const uid = process.env.NEXT_PUBLIC_EAS_SCHEMA_UID;
130
- if (!uid) throw new Error("NEXT_PUBLIC_EAS_SCHEMA_UID is required \u2014 register the schema first.");
131
- return uid;
132
- }
133
- async function readAttestation(uid) {
134
- const cfg = networkConfig();
135
- const provider = new JsonRpcProvider(rpcUrl(), cfg.chainId);
136
- const eas = new Contract(cfg.eas, EAS_ABI, provider);
137
- const att = await eas.getAttestation(uid);
138
- if (!att || att.uid === ZeroHash) return null;
139
- if (String(att.schema).toLowerCase() !== litmusSchemaUID().toLowerCase()) return null;
140
- const d = decodeLitmusAttestation(att.data);
141
- return {
142
- uid: att.uid,
143
- serverRef: String(d.serverRef),
144
- toolDefsFingerprint: String(d.toolDefsFingerprint),
145
- overallGrade: String(d.overallGrade),
146
- reportCID: String(d.reportCID),
147
- resolvedVersion: d.resolvedVersion || null,
148
- revoked: att.revocationTime > 0n,
149
- attester: String(att.attester),
150
- expirationTime: BigInt(att.expirationTime ?? 0n)
151
- };
152
- }
153
-
154
- // src/tools/run-litmus.ts
155
- import { z } from "zod";
156
- var RUN_LITMUS_TOOL_NAME = "run_litmus";
157
- var RUN_LITMUS_TOOL_TITLE = "Run a behavioral litmus on an MCP server";
158
- var RUN_LITMUS_TOOL_DESCRIPTION = [
159
- `Grade an MCP server A\u2013F against the open behavioral litmus (${METHODOLOGY_VERSION}).`,
160
- "The harness connects the way an agent would, fingerprints the tool surface, and",
161
- "runs four checks: C-01 tool-output injection, C-02 permission/egress overreach",
162
- "(egress in a hardened default-deny Docker sandbox, plus a declared-permission",
163
- "honesty check), C-03 sensitive-data handling (planted canaries), and C-04",
164
- "adversarial-input handling (malformed/oversized and jailbreak inputs).",
165
- "",
166
- "This is ACTIVE: it launches the target server's code to exercise it (egress-",
167
- "sandboxed when Docker is available) and takes ~20\u201360s. It is not a lookup \u2014 for",
168
- "a server's already-published grade, use `verify_attestation`. No wallet or RPC",
169
- "needed.",
170
- "",
171
- "server_ref examples: npm/@modelcontextprotocol/server-filesystem \xB7",
172
- "https://example.com/mcp \xB7 ./build/index.js. For a token-gated https:// target,",
173
- "pass `bearer`. If Docker is unavailable, C-02 is skipped and the grade is capped",
174
- "at B for that run."
175
- ].join("\n");
176
- var runLitmusInputShape = {
177
- server_ref: z.string().min(1).max(512).describe("What to grade: a registry ref (npm/@scope/server), an https:// MCP URL, or a local path to an MCP entry file."),
178
- bearer: z.string().min(1).max(8192).optional().describe("Bearer token for a token-gated https:// MCP server. Sent as `Authorization: Bearer <token>` to the target origin only. Ignored for stdio/local targets."),
179
- header: z.array(z.string()).max(20).optional().describe('Extra HTTP headers for a gated https:// target, each "Key: Value" (e.g. "X-Api-Key: \u2026"). Overrides the bearer-derived Authorization for the same key. Ignored for stdio/local targets.')
180
- };
181
- var PROGRESS_TOTAL = 5;
182
- async function handleRunLitmus({ server_ref, bearer, header }, extra) {
183
- try {
184
- const argv = [
185
- ...bearer ? ["--bearer", bearer] : [],
186
- ...(header ?? []).flatMap((h) => ["--header", h])
187
- ];
188
- const { headers } = parseAuthFlags(argv, {});
189
- const progressToken = extra._meta?.progressToken;
190
- const sendProgress = progressToken !== void 0 ? (progress, message) => void extra.sendNotification({
191
- method: "notifications/progress",
192
- params: { progressToken, progress, total: PROGRESS_TOTAL, message }
193
- }) : void 0;
194
- sendProgress?.(0, `Connecting to ${server_ref}\u2026`);
195
- const bundle = await runLitmus(resolveTarget(server_ref), {
196
- ...Object.keys(headers).length ? { headers } : {},
197
- ...sendProgress ? { onProgress: (done, _total, label) => sendProgress(done, label) } : {}
198
- });
199
- const payload = summarize(bundle);
200
- return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
201
- } catch (err) {
202
- const message = err instanceof Error ? err.message : String(err);
203
- return { isError: true, content: [{ type: "text", text: `run_litmus failed: ${message}` }] };
204
- }
205
- }
206
- var CATEGORY_LABEL = {
207
- "C-01": "tool-output injection",
208
- "C-02": "permission / egress overreach",
209
- "C-03": "sensitive-data handling",
210
- "C-04": "adversarial-input handling"
211
- };
212
- function summarize(b) {
213
- const find = (code) => b.categories.find((c) => c.code === code);
214
- const categories = ["C-01", "C-02", "C-03", "C-04"].map((code) => {
215
- const c = find(code);
216
- const findings = c?.status === "fail" ? c.probes.flatMap((p) => p.findings).filter((f) => f.severity === "high").slice(0, 5).map((f) => ({ tool: f.tool, kind: f.kind, match: truncate(f.match, 120), host: f.host, port: f.port })) : [];
217
- return { code, check: CATEGORY_LABEL[code], status: c?.status ?? "unknown", reason: c?.reason ?? null, findings };
218
- });
219
- return {
220
- grade: b.grade,
221
- summary: b.gradeRationale,
222
- serverRef: b.serverRef,
223
- resolvedVersion: b.resolvedVersion,
224
- fingerprint: b.toolDefsFingerprint,
225
- ranAt: b.ranAt,
226
- methodologyVersion: b.methodologyVersion,
227
- categories
228
- };
229
- }
230
- function truncate(s, n) {
231
- return s.length > n ? `${s.slice(0, n)}\u2026` : s;
232
- }
233
-
234
- export {
235
- NETWORKS,
236
- selectedNetwork,
237
- networkConfig,
238
- rpcUrl,
239
- LITMUS_SCHEMA,
240
- litmusFields,
241
- encodeLitmusAttestation,
242
- decodeLitmusAttestation,
243
- litmusSchemaUID,
244
- readAttestation,
245
- RUN_LITMUS_TOOL_NAME,
246
- RUN_LITMUS_TOOL_TITLE,
247
- RUN_LITMUS_TOOL_DESCRIPTION,
248
- runLitmusInputShape,
249
- handleRunLitmus
250
- };
@@ -1,31 +0,0 @@
1
- import {
2
- assembleBundle,
3
- canaryMatch,
4
- classifyTool,
5
- connectTarget,
6
- fingerprintToolDefs,
7
- gradeFromCategories,
8
- hasHighSeverity,
9
- instructionMimicry,
10
- internalsLeak,
11
- invisibleUnicode,
12
- markdownTricks,
13
- runLitmus,
14
- stateChangingToolNames
15
- } from "./chunk-35UOPCBW.js";
16
- import "./chunk-ZR6XRGMQ.js";
17
- export {
18
- assembleBundle,
19
- canaryMatch,
20
- classifyTool,
21
- connectTarget,
22
- fingerprintToolDefs,
23
- gradeFromCategories,
24
- hasHighSeverity,
25
- instructionMimicry,
26
- internalsLeak,
27
- invisibleUnicode,
28
- markdownTricks,
29
- runLitmus,
30
- stateChangingToolNames
31
- };