opmsec 0.1.0 → 0.1.4
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/.env.example +23 -13
- package/.husky/pre-commit +1 -0
- package/README.md +256 -173
- package/bun.lock +4 -4
- package/docs/architecture/agents.mdx +77 -0
- package/docs/architecture/benchmarks.mdx +65 -0
- package/docs/architecture/overview.mdx +58 -0
- package/docs/architecture/scanner.mdx +53 -0
- package/docs/cli/audit.mdx +35 -0
- package/docs/cli/check.mdx +44 -0
- package/docs/cli/fix.mdx +49 -0
- package/docs/cli/info.mdx +44 -0
- package/docs/cli/install.mdx +71 -0
- package/docs/cli/push.mdx +99 -0
- package/docs/cli/register-agent.mdx +80 -0
- package/docs/cli/view.mdx +52 -0
- package/docs/concepts/multi-agent-consensus.mdx +58 -0
- package/docs/concepts/on-chain-registry.mdx +74 -0
- package/docs/concepts/security-model.mdx +76 -0
- package/docs/concepts/zk-agent-verification.mdx +82 -0
- package/docs/configuration.mdx +82 -0
- package/docs/contract/deployment.mdx +57 -0
- package/docs/contract/events.mdx +115 -0
- package/docs/contract/functions.mdx +220 -0
- package/docs/contract/overview.mdx +58 -0
- package/docs/favicon.svg +5 -0
- package/docs/introduction.mdx +43 -0
- package/docs/logo/dark.svg +5 -0
- package/docs/logo/light.svg +5 -0
- package/docs/mint.json +106 -0
- package/docs/quickstart.mdx +133 -0
- package/package.json +7 -6
- package/packages/cli/src/commands/author-view.tsx +9 -1
- package/packages/cli/src/commands/check.tsx +318 -0
- package/packages/cli/src/commands/fix.tsx +294 -0
- package/packages/cli/src/commands/install.tsx +501 -47
- package/packages/cli/src/commands/push.tsx +53 -22
- package/packages/cli/src/commands/register-agent.tsx +227 -0
- package/packages/cli/src/components/AgentScores.tsx +20 -6
- package/packages/cli/src/components/Hyperlink.tsx +30 -0
- package/packages/cli/src/components/ScanReport.tsx +3 -2
- package/packages/cli/src/index.tsx +44 -6
- package/packages/cli/src/services/avatar.ts +43 -6
- package/packages/cli/src/services/chainpatrol.ts +20 -17
- package/packages/cli/src/services/contract.ts +41 -8
- package/packages/cli/src/services/ens.ts +3 -5
- package/packages/cli/src/services/fileverse.ts +12 -13
- package/packages/cli/src/services/typosquat.ts +166 -0
- package/packages/cli/src/services/version.ts +156 -5
- package/packages/contracts/circuits/accuracy_verifier.circom +101 -0
- package/packages/contracts/contracts/OPMRegistry.sol +63 -0
- package/packages/contracts/scripts/deploy.ts +22 -3
- package/packages/core/src/abi.ts +221 -0
- package/packages/core/src/benchmarks.ts +450 -0
- package/packages/core/src/constants.ts +20 -0
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/model-rankings.ts +115 -0
- package/packages/core/src/prompt.ts +58 -0
- package/packages/core/src/types.ts +41 -0
- package/packages/core/src/utils.ts +142 -3
- package/packages/scanner/src/agents/base-agent.ts +13 -3
- package/packages/scanner/src/index.ts +5 -2
- package/packages/scanner/src/queue/memory-queue.ts +8 -3
- package/packages/scanner/src/services/benchmark-runner.ts +114 -0
- package/packages/scanner/src/services/contract-writer.ts +2 -3
- package/packages/scanner/src/services/fileverse.ts +26 -7
- package/packages/scanner/src/services/openrouter.ts +61 -4
- package/packages/scanner/src/services/report-formatter.ts +122 -3
- package/packages/scanner/src/services/zk-verifier.ts +118 -0
- package/packages/web/.next/BUILD_ID +1 -0
- package/packages/web/.next/app-build-manifest.json +26 -0
- package/packages/web/.next/app-path-routes-manifest.json +4 -0
- package/packages/web/.next/build-manifest.json +33 -0
- package/packages/web/.next/diagnostics/build-diagnostics.json +6 -0
- package/packages/web/.next/diagnostics/framework.json +1 -0
- package/packages/web/.next/export-marker.json +6 -0
- package/packages/web/.next/images-manifest.json +58 -0
- package/packages/web/.next/next-minimal-server.js.nft.json +1 -0
- package/packages/web/.next/next-server.js.nft.json +1 -0
- package/packages/web/.next/package.json +1 -0
- package/packages/web/.next/prerender-manifest.json +61 -0
- package/packages/web/.next/react-loadable-manifest.json +1 -0
- package/packages/web/.next/required-server-files.json +320 -0
- package/packages/web/.next/routes-manifest.json +53 -0
- package/packages/web/.next/server/app/_not-found/page.js +2 -0
- package/packages/web/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/packages/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/packages/web/.next/server/app/_not-found.html +1 -0
- package/packages/web/.next/server/app/_not-found.meta +8 -0
- package/packages/web/.next/server/app/_not-found.rsc +16 -0
- package/packages/web/.next/server/app/index.html +1 -0
- package/packages/web/.next/server/app/index.meta +7 -0
- package/packages/web/.next/server/app/index.rsc +20 -0
- package/packages/web/.next/server/app/page.js +2 -0
- package/packages/web/.next/server/app/page.js.nft.json +1 -0
- package/packages/web/.next/server/app/page_client-reference-manifest.js +1 -0
- package/packages/web/.next/server/app-paths-manifest.json +4 -0
- package/packages/web/.next/server/chunks/611.js +6 -0
- package/packages/web/.next/server/chunks/778.js +30 -0
- package/packages/web/.next/server/functions-config-manifest.json +4 -0
- package/packages/web/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/packages/web/.next/server/middleware-build-manifest.js +1 -0
- package/packages/web/.next/server/middleware-manifest.json +6 -0
- package/packages/web/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/packages/web/.next/server/next-font-manifest.js +1 -0
- package/packages/web/.next/server/next-font-manifest.json +1 -0
- package/packages/web/.next/server/pages/404.html +1 -0
- package/packages/web/.next/server/pages/500.html +1 -0
- package/packages/web/.next/server/pages/_app.js +1 -0
- package/packages/web/.next/server/pages/_app.js.nft.json +1 -0
- package/packages/web/.next/server/pages/_document.js +1 -0
- package/packages/web/.next/server/pages/_document.js.nft.json +1 -0
- package/packages/web/.next/server/pages/_error.js +19 -0
- package/packages/web/.next/server/pages/_error.js.nft.json +1 -0
- package/packages/web/.next/server/pages-manifest.json +6 -0
- package/packages/web/.next/server/server-reference-manifest.js +1 -0
- package/packages/web/.next/server/server-reference-manifest.json +1 -0
- package/packages/web/.next/server/webpack-runtime.js +1 -0
- package/packages/web/.next/static/2XIFCTTKVZwN_RsNE-Rrr/_buildManifest.js +1 -0
- package/packages/web/.next/static/2XIFCTTKVZwN_RsNE-Rrr/_ssgManifest.js +1 -0
- package/packages/web/.next/static/chunks/255-0dc49b7a6e8e5c05.js +1 -0
- package/packages/web/.next/static/chunks/4bd1b696-382748cc942d8a14.js +1 -0
- package/packages/web/.next/static/chunks/app/_not-found/page-0da542be7eb33a64.js +1 -0
- package/packages/web/.next/static/chunks/app/layout-28a489fb4398663f.js +1 -0
- package/packages/web/.next/static/chunks/app/page-e58ccdb78625bce6.js +1 -0
- package/packages/web/.next/static/chunks/framework-ac73abd125e371fe.js +1 -0
- package/packages/web/.next/static/chunks/main-app-dd261207182e5a23.js +1 -0
- package/packages/web/.next/static/chunks/main-ee293fa6aa18bdd1.js +1 -0
- package/packages/web/.next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/packages/web/.next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/packages/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/packages/web/.next/static/chunks/webpack-e1ae44446e7f7355.js +1 -0
- package/packages/web/.next/static/css/21d69157e271f2ab.css +3 -0
- package/packages/web/.next/trace +2 -0
- package/packages/web/.next/types/app/layout.ts +84 -0
- package/packages/web/.next/types/app/page.ts +84 -0
- package/packages/web/.next/types/cache-life.d.ts +141 -0
- package/packages/web/.next/types/package.json +1 -0
- package/packages/web/.next/types/routes.d.ts +57 -0
- package/packages/web/.next/types/validator.ts +61 -0
- package/packages/web/app/globals.css +75 -0
- package/packages/web/app/layout.tsx +26 -0
- package/packages/web/app/page.tsx +361 -0
- package/packages/web/bun.lock +300 -0
- package/packages/web/next-env.d.ts +6 -0
- package/packages/web/next.config.ts +5 -0
- package/packages/web/package.json +26 -0
- package/packages/web/postcss.config.mjs +8 -0
- package/packages/web/public/favicon.svg +5 -0
- package/packages/web/public/logo.svg +7 -0
- package/packages/web/tailwind.config.ts +48 -0
- package/packages/web/tsconfig.json +21 -0
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import type { ScanReport } from '@opm/core';
|
|
2
|
-
import { getEnvOrDefault } from '@opm/core';
|
|
3
|
-
import { formatReportAsMarkdown } from './report-formatter';
|
|
4
|
-
|
|
5
|
-
const DEFAULT_API_URL = 'http://localhost:8001';
|
|
1
|
+
import type { ScanReport, CheckReport } from '@opm/core';
|
|
2
|
+
import { getEnvOrDefault, FILEVERSE_DEFAULT_URL } from '@opm/core';
|
|
3
|
+
import { formatReportAsMarkdown, formatCheckReportAsMarkdown } from './report-formatter';
|
|
6
4
|
const POLL_INTERVAL_MS = 3000;
|
|
7
5
|
const POLL_TIMEOUT_MS = 60_000;
|
|
8
6
|
|
|
9
7
|
function getApiConfig() {
|
|
10
|
-
const apiUrl = getEnvOrDefault('FILEVERSE_API_URL',
|
|
8
|
+
const apiUrl = getEnvOrDefault('FILEVERSE_API_URL', FILEVERSE_DEFAULT_URL);
|
|
11
9
|
const apiKey = process.env.FILEVERSE_API_KEY;
|
|
12
10
|
if (!apiKey) throw new Error('FILEVERSE_API_KEY is required (generate at ddocs.new → Settings → Developer Mode)');
|
|
13
11
|
return { apiUrl, apiKey };
|
|
@@ -56,11 +54,32 @@ async function pollForSync(apiUrl: string, apiKey: string, ddocId: string): Prom
|
|
|
56
54
|
return `https://ddocs.new/pending/${ddocId}`;
|
|
57
55
|
}
|
|
58
56
|
|
|
57
|
+
export async function uploadCheckReportToFileverse(report: CheckReport): Promise<string> {
|
|
58
|
+
const { apiUrl, apiKey } = getApiConfig();
|
|
59
|
+
const title = `OPM Check Report: ${report.project} (${report.totalDeps} deps)`;
|
|
60
|
+
const content = formatCheckReportAsMarkdown(report);
|
|
61
|
+
|
|
62
|
+
const res = await fetch(`${apiUrl}/api/ddocs?apiKey=${encodeURIComponent(apiKey)}`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({ title, content }),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
const body = await res.text();
|
|
70
|
+
throw new Error(`Fileverse create failed (${res.status}): ${body}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { data } = await res.json() as { data: { ddocId: string; syncStatus: string; link?: string } };
|
|
74
|
+
if (data.syncStatus === 'synced' && data.link) return data.link;
|
|
75
|
+
return pollForSync(apiUrl, apiKey, data.ddocId);
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
export async function fetchReportFromFileverse(reportURI: string): Promise<ScanReport | null> {
|
|
60
79
|
if (!reportURI || reportURI.startsWith('local://')) return null;
|
|
61
80
|
|
|
62
81
|
const apiKey = process.env.FILEVERSE_API_KEY;
|
|
63
|
-
const apiUrl = getEnvOrDefault('FILEVERSE_API_URL',
|
|
82
|
+
const apiUrl = getEnvOrDefault('FILEVERSE_API_URL', FILEVERSE_DEFAULT_URL);
|
|
64
83
|
|
|
65
84
|
const ddocId = extractDdocId(reportURI);
|
|
66
85
|
if (ddocId && apiKey) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OPENROUTER_API_URL, OPENAI_API_URL } from '@opm/core';
|
|
2
2
|
import type { AgentScanResult } from '@opm/core';
|
|
3
|
-
import { validateScanResult, safeJsonParse } from '@opm/core';
|
|
3
|
+
import { validateScanResult, normalizeScanResult, safeJsonParse } from '@opm/core';
|
|
4
4
|
|
|
5
5
|
function getProvider(): { apiUrl: string; apiKey: string; kind: 'openai' | 'openrouter' } {
|
|
6
6
|
const forcedProvider = process.env.LLM_PROVIDER;
|
|
@@ -53,6 +53,10 @@ export async function callLLM(
|
|
|
53
53
|
headers['X-Title'] = 'OPM Security Scanner';
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const tokenLimit = kind === 'openai'
|
|
57
|
+
? { max_completion_tokens: 4096 }
|
|
58
|
+
: { max_tokens: 4096 };
|
|
59
|
+
|
|
56
60
|
const res = await fetch(apiUrl, {
|
|
57
61
|
method: 'POST',
|
|
58
62
|
headers,
|
|
@@ -64,7 +68,7 @@ export async function callLLM(
|
|
|
64
68
|
],
|
|
65
69
|
response_format: { type: 'json_object' },
|
|
66
70
|
temperature: 0.1,
|
|
67
|
-
|
|
71
|
+
...tokenLimit,
|
|
68
72
|
}),
|
|
69
73
|
});
|
|
70
74
|
|
|
@@ -78,9 +82,62 @@ export async function callLLM(
|
|
|
78
82
|
if (!raw) throw new Error(`Empty response from ${kind}/${model}`);
|
|
79
83
|
|
|
80
84
|
const parsed = safeJsonParse<AgentScanResult>(raw);
|
|
81
|
-
if (!parsed
|
|
82
|
-
|
|
85
|
+
if (!parsed) throw new Error(`Unparseable JSON from ${model}: ${raw.slice(0, 200)}`);
|
|
86
|
+
|
|
87
|
+
if (validateScanResult(parsed)) return parsed;
|
|
88
|
+
|
|
89
|
+
const normalized = normalizeScanResult(parsed);
|
|
90
|
+
if (!normalized) throw new Error(`Cannot normalize scan result from ${model}: ${raw.slice(0, 200)}`);
|
|
91
|
+
|
|
92
|
+
return normalized;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function callLLMRaw<T = unknown>(
|
|
96
|
+
model: string,
|
|
97
|
+
systemPrompt: string,
|
|
98
|
+
userPrompt: string,
|
|
99
|
+
): Promise<T> {
|
|
100
|
+
const { apiUrl, apiKey, kind } = getProvider();
|
|
101
|
+
|
|
102
|
+
const headers: Record<string, string> = {
|
|
103
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
104
|
+
'Content-Type': 'application/json',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (kind === 'openrouter') {
|
|
108
|
+
headers['HTTP-Referer'] = 'https://opm.dev';
|
|
109
|
+
headers['X-Title'] = 'OPM Security Scanner';
|
|
83
110
|
}
|
|
84
111
|
|
|
112
|
+
const tokenLimit = kind === 'openai'
|
|
113
|
+
? { max_completion_tokens: 4096 }
|
|
114
|
+
: { max_tokens: 4096 };
|
|
115
|
+
|
|
116
|
+
const res = await fetch(apiUrl, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers,
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
model,
|
|
121
|
+
messages: [
|
|
122
|
+
{ role: 'system', content: systemPrompt },
|
|
123
|
+
{ role: 'user', content: userPrompt },
|
|
124
|
+
],
|
|
125
|
+
response_format: { type: 'json_object' },
|
|
126
|
+
temperature: 0.1,
|
|
127
|
+
...tokenLimit,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const body = await res.text();
|
|
133
|
+
throw new Error(`${kind} ${res.status}: ${body}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const data = await res.json() as { choices: Array<{ message: { content: string } }> };
|
|
137
|
+
const raw = data.choices?.[0]?.message?.content;
|
|
138
|
+
if (!raw) throw new Error(`Empty response from ${kind}/${model}`);
|
|
139
|
+
|
|
140
|
+
const parsed = safeJsonParse<T>(raw);
|
|
141
|
+
if (!parsed) throw new Error(`Invalid JSON from ${model}: ${raw.slice(0, 200)}`);
|
|
85
142
|
return parsed;
|
|
86
143
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ScanReport, AgentEntry, Vulnerability, SupplyChainIndicators } from '@opm/core';
|
|
1
|
+
import type { ScanReport, AgentEntry, Vulnerability, SupplyChainIndicators, CheckReport } from '@opm/core';
|
|
2
2
|
|
|
3
3
|
function riskEmoji(score: number): string {
|
|
4
4
|
if (score >= 70) return '🔴';
|
|
@@ -49,10 +49,15 @@ function formatAgent(agent: AgentEntry, index: number): string {
|
|
|
49
49
|
const { result } = agent;
|
|
50
50
|
const emoji = riskEmoji(result.risk_score);
|
|
51
51
|
|
|
52
|
+
const modelLines = [`- **Model:** \`${agent.model}\``];
|
|
53
|
+
if (agent.model_intelligence || agent.model_coding) {
|
|
54
|
+
modelLines.push(`- **AI Intelligence Index:** ${agent.model_intelligence || '—'}/100 | **Coding Index:** ${agent.model_coding || '—'}/100 | **Weight:** ${agent.model_weight || '—'}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
return [
|
|
53
58
|
`### Agent ${index + 1}: \`${agent.agent_id}\``,
|
|
54
59
|
'',
|
|
55
|
-
|
|
60
|
+
...modelLines,
|
|
56
61
|
`- **Risk Score:** ${emoji} **${result.risk_score}/100** (${result.risk_level})`,
|
|
57
62
|
`- **Recommendation:** ${result.recommendation}`,
|
|
58
63
|
'',
|
|
@@ -101,10 +106,12 @@ export function formatReportAsMarkdown(report: ScanReport): string {
|
|
|
101
106
|
'',
|
|
102
107
|
'---',
|
|
103
108
|
'',
|
|
104
|
-
'## Aggregate Risk',
|
|
109
|
+
'## Aggregate Risk (Intelligence-Weighted)',
|
|
105
110
|
'',
|
|
106
111
|
`\`${riskBar(report.aggregate_risk_score)}\` **${report.aggregate_risk_score}/100** — ${report.consensus}`,
|
|
107
112
|
'',
|
|
113
|
+
`> Scores are weighted by each model's AI Intelligence and Coding indices from [Artificial Analysis](https://artificialanalysis.ai).`,
|
|
114
|
+
'',
|
|
108
115
|
report.aggregate_risk_score < 40
|
|
109
116
|
? '> ✅ This package appears safe based on multi-agent consensus.'
|
|
110
117
|
: report.aggregate_risk_score < 70
|
|
@@ -132,3 +139,115 @@ export function formatReportAsMarkdown(report: ScanReport): string {
|
|
|
132
139
|
|
|
133
140
|
return sections.join('\n');
|
|
134
141
|
}
|
|
142
|
+
|
|
143
|
+
export function formatCheckReportAsMarkdown(report: CheckReport): string {
|
|
144
|
+
const timestamp = new Date(report.timestamp).toLocaleString('en-US', {
|
|
145
|
+
dateStyle: 'long', timeStyle: 'short',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const typosquats = report.deps.filter((d) => d.typosquat);
|
|
149
|
+
const cveBlocked = report.deps.filter((d) => d.cveCritical > 0);
|
|
150
|
+
const cveWarned = report.deps.filter((d) => d.cveCount > 0 && d.cveCritical === 0);
|
|
151
|
+
const highRisk = report.deps.filter((d) => d.onChainScore !== null && d.onChainScore >= 70);
|
|
152
|
+
const safeCount = report.totalDeps - typosquats.length - cveBlocked.length - cveWarned.length - highRisk.length;
|
|
153
|
+
|
|
154
|
+
const sections: string[] = [
|
|
155
|
+
`# OPM Dependency Check Report`,
|
|
156
|
+
'',
|
|
157
|
+
`- **Project:** ${report.project}`,
|
|
158
|
+
`- **Scanned:** ${timestamp}`,
|
|
159
|
+
`- **Total dependencies:** ${report.totalDeps}`,
|
|
160
|
+
'',
|
|
161
|
+
'---',
|
|
162
|
+
'',
|
|
163
|
+
'## Summary',
|
|
164
|
+
'',
|
|
165
|
+
`| Category | Count |`,
|
|
166
|
+
`|---|---|`,
|
|
167
|
+
`| ${typosquats.length > 0 ? '🔴' : '🟢'} Typosquats | ${typosquats.length} |`,
|
|
168
|
+
`| ${cveBlocked.length > 0 ? '🔴' : '🟢'} Critical CVEs | ${cveBlocked.length} |`,
|
|
169
|
+
`| ${cveWarned.length > 0 ? '🟡' : '🟢'} CVE Warnings | ${cveWarned.length} |`,
|
|
170
|
+
`| ${highRisk.length > 0 ? '🔴' : '🟢'} High On-chain Risk | ${highRisk.length} |`,
|
|
171
|
+
`| 🟢 Safe | ${Math.max(0, safeCount)} |`,
|
|
172
|
+
'',
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
if (typosquats.length > 0) {
|
|
176
|
+
sections.push('---', '', '## Typosquat Risks', '');
|
|
177
|
+
for (const d of typosquats) {
|
|
178
|
+
sections.push(
|
|
179
|
+
`### \`${d.name}\`@${d.version}`,
|
|
180
|
+
'',
|
|
181
|
+
`- **Likely intended package:** \`${d.typosquat!.likelyTarget}\``,
|
|
182
|
+
`- **Confidence:** ${d.typosquat!.confidence}`,
|
|
183
|
+
`- **Reason:** ${d.typosquat!.reason}`,
|
|
184
|
+
'',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (cveBlocked.length > 0) {
|
|
190
|
+
sections.push('---', '', '## Critical Vulnerabilities', '');
|
|
191
|
+
for (const d of cveBlocked) {
|
|
192
|
+
sections.push(
|
|
193
|
+
`### \`${d.name}\`@${d.version}`,
|
|
194
|
+
'',
|
|
195
|
+
`- **Critical:** ${d.cveCritical} | **High:** ${d.cveHigh}`,
|
|
196
|
+
`- **CVEs:** ${d.cveIds.join(', ')}`,
|
|
197
|
+
d.fixVersion ? `- **Fix:** upgrade to \`${d.fixVersion}\`` : '',
|
|
198
|
+
'',
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (cveWarned.length > 0) {
|
|
204
|
+
sections.push('---', '', '## CVE Warnings', '');
|
|
205
|
+
for (const d of cveWarned) {
|
|
206
|
+
sections.push(
|
|
207
|
+
`- \`${d.name}\`@${d.version} — ${d.cveCount} CVE(s)${d.fixVersion ? ` → \`${d.fixVersion}\`` : ''}`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
sections.push('');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (highRisk.length > 0) {
|
|
214
|
+
sections.push('---', '', '## High On-chain Risk', '');
|
|
215
|
+
for (const d of highRisk) {
|
|
216
|
+
sections.push(`- \`${d.name}\`@${d.version} — risk score **${d.onChainScore}/100**`);
|
|
217
|
+
}
|
|
218
|
+
sections.push('');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (report.agents.length > 0) {
|
|
222
|
+
sections.push('---', '', '## AI Agent Analysis', '');
|
|
223
|
+
for (const a of report.agents) {
|
|
224
|
+
const flags = a.findings.filter((f) => f.issue !== 'safe' && f.severity !== 'NONE');
|
|
225
|
+
sections.push(
|
|
226
|
+
`### \`${a.agentId}\` — ${a.model}`,
|
|
227
|
+
'',
|
|
228
|
+
`- **AI Intelligence:** ${a.intelligence}/100 | **Coding:** ${a.coding}/100`,
|
|
229
|
+
`- **Risk Score:** ${a.riskScore}/100`,
|
|
230
|
+
'',
|
|
231
|
+
);
|
|
232
|
+
if (flags.length > 0) {
|
|
233
|
+
for (const f of flags) {
|
|
234
|
+
sections.push(
|
|
235
|
+
`- **[${f.severity}]** \`${f.package}\` — ${f.issue}: ${f.explanation}` +
|
|
236
|
+
(f.suggested_replacement ? ` → \`${f.suggested_replacement}\`` : ''),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
sections.push(`- ✅ No issues found`);
|
|
241
|
+
}
|
|
242
|
+
sections.push('', `> ${a.overall}`, '');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
sections.push(
|
|
247
|
+
'---',
|
|
248
|
+
'',
|
|
249
|
+
`*Report generated by [OPM](https://github.com/dhananjaypai08/opm) — On-chain Package Manager*`,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return sections.filter((l) => l !== undefined).join('\n');
|
|
253
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zero-knowledge accuracy verification via hash commitments.
|
|
5
|
+
*
|
|
6
|
+
* The scheme works as follows:
|
|
7
|
+
* 1. A trusted authority generates benchmark test cases with expected outputs
|
|
8
|
+
* 2. Expected outputs are hashed with a secret salt → commitment
|
|
9
|
+
* 3. The candidate agent runs against the benchmarks
|
|
10
|
+
* 4. Actual outputs are hashed with the same salt
|
|
11
|
+
* 5. A proof is generated: hash(commitment || result_hashes || accuracy_flag)
|
|
12
|
+
* 6. The verifier checks the proof without seeing individual test results
|
|
13
|
+
*
|
|
14
|
+
* This ensures:
|
|
15
|
+
* - Test cases remain private (can't be gamed)
|
|
16
|
+
* - Individual results aren't disclosed
|
|
17
|
+
* - Only a binary pass/fail is revealed
|
|
18
|
+
* - The proof is deterministic and verifiable
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface ZKCommitment {
|
|
22
|
+
salt: string;
|
|
23
|
+
expectedHash: string;
|
|
24
|
+
caseCount: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ZKProof {
|
|
28
|
+
commitment: ZKCommitment;
|
|
29
|
+
resultHash: string;
|
|
30
|
+
accuracyProof: string;
|
|
31
|
+
passed: boolean;
|
|
32
|
+
timestamp: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AccuracyWitness {
|
|
36
|
+
expectedVerdicts: number[];
|
|
37
|
+
actualVerdicts: number[];
|
|
38
|
+
salt: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function poseidonHash(...inputs: string[]): string {
|
|
42
|
+
const h = createHash('sha256');
|
|
43
|
+
for (const input of inputs) {
|
|
44
|
+
h.update(input);
|
|
45
|
+
}
|
|
46
|
+
return h.digest('hex');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function generateCommitment(expectedVerdicts: number[]): ZKCommitment {
|
|
50
|
+
const salt = randomBytes(32).toString('hex');
|
|
51
|
+
const verdictStr = expectedVerdicts.join(',');
|
|
52
|
+
const expectedHash = poseidonHash(salt, verdictStr);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
salt,
|
|
56
|
+
expectedHash,
|
|
57
|
+
caseCount: expectedVerdicts.length,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function generateProof(
|
|
62
|
+
commitment: ZKCommitment,
|
|
63
|
+
expectedVerdicts: number[],
|
|
64
|
+
actualVerdicts: number[],
|
|
65
|
+
): ZKProof {
|
|
66
|
+
if (expectedVerdicts.length !== actualVerdicts.length) {
|
|
67
|
+
throw new Error('Verdict arrays must have equal length');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const expectedStr = expectedVerdicts.join(',');
|
|
71
|
+
const commitmentCheck = poseidonHash(commitment.salt, expectedStr);
|
|
72
|
+
if (commitmentCheck !== commitment.expectedHash) {
|
|
73
|
+
throw new Error('Commitment verification failed — expected verdicts do not match');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const actualStr = actualVerdicts.join(',');
|
|
77
|
+
const resultHash = poseidonHash(commitment.salt, actualStr);
|
|
78
|
+
|
|
79
|
+
const allMatch = expectedVerdicts.every((e, i) => e === actualVerdicts[i]);
|
|
80
|
+
|
|
81
|
+
const accuracyProof = poseidonHash(
|
|
82
|
+
commitment.expectedHash,
|
|
83
|
+
resultHash,
|
|
84
|
+
allMatch ? '1' : '0',
|
|
85
|
+
commitment.salt,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
commitment,
|
|
90
|
+
resultHash,
|
|
91
|
+
accuracyProof,
|
|
92
|
+
passed: allMatch,
|
|
93
|
+
timestamp: Date.now(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function verifyProof(proof: ZKProof): boolean {
|
|
98
|
+
const recomputedProof = poseidonHash(
|
|
99
|
+
proof.commitment.expectedHash,
|
|
100
|
+
proof.resultHash,
|
|
101
|
+
proof.passed ? '1' : '0',
|
|
102
|
+
proof.commitment.salt,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return recomputedProof === proof.accuracyProof;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function proofToOnChainBytes(proof: ZKProof): string {
|
|
109
|
+
const payload = JSON.stringify({
|
|
110
|
+
commitment: proof.commitment.expectedHash,
|
|
111
|
+
resultHash: proof.resultHash,
|
|
112
|
+
accuracyProof: proof.accuracyProof,
|
|
113
|
+
passed: proof.passed,
|
|
114
|
+
timestamp: proof.timestamp,
|
|
115
|
+
caseCount: proof.commitment.caseCount,
|
|
116
|
+
});
|
|
117
|
+
return '0x' + Buffer.from(payload).toString('hex');
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2XIFCTTKVZwN_RsNE-Rrr
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pages": {
|
|
3
|
+
"/_not-found/page": [
|
|
4
|
+
"static/chunks/webpack-e1ae44446e7f7355.js",
|
|
5
|
+
"static/chunks/4bd1b696-382748cc942d8a14.js",
|
|
6
|
+
"static/chunks/255-0dc49b7a6e8e5c05.js",
|
|
7
|
+
"static/chunks/main-app-dd261207182e5a23.js",
|
|
8
|
+
"static/chunks/app/_not-found/page-0da542be7eb33a64.js"
|
|
9
|
+
],
|
|
10
|
+
"/layout": [
|
|
11
|
+
"static/chunks/webpack-e1ae44446e7f7355.js",
|
|
12
|
+
"static/chunks/4bd1b696-382748cc942d8a14.js",
|
|
13
|
+
"static/chunks/255-0dc49b7a6e8e5c05.js",
|
|
14
|
+
"static/chunks/main-app-dd261207182e5a23.js",
|
|
15
|
+
"static/css/21d69157e271f2ab.css",
|
|
16
|
+
"static/chunks/app/layout-28a489fb4398663f.js"
|
|
17
|
+
],
|
|
18
|
+
"/page": [
|
|
19
|
+
"static/chunks/webpack-e1ae44446e7f7355.js",
|
|
20
|
+
"static/chunks/4bd1b696-382748cc942d8a14.js",
|
|
21
|
+
"static/chunks/255-0dc49b7a6e8e5c05.js",
|
|
22
|
+
"static/chunks/main-app-dd261207182e5a23.js",
|
|
23
|
+
"static/chunks/app/page-e58ccdb78625bce6.js"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"polyfillFiles": [
|
|
3
|
+
"static/chunks/polyfills-42372ed130431b0a.js"
|
|
4
|
+
],
|
|
5
|
+
"devFiles": [],
|
|
6
|
+
"ampDevFiles": [],
|
|
7
|
+
"lowPriorityFiles": [
|
|
8
|
+
"static/2XIFCTTKVZwN_RsNE-Rrr/_buildManifest.js",
|
|
9
|
+
"static/2XIFCTTKVZwN_RsNE-Rrr/_ssgManifest.js"
|
|
10
|
+
],
|
|
11
|
+
"rootMainFiles": [
|
|
12
|
+
"static/chunks/webpack-e1ae44446e7f7355.js",
|
|
13
|
+
"static/chunks/4bd1b696-382748cc942d8a14.js",
|
|
14
|
+
"static/chunks/255-0dc49b7a6e8e5c05.js",
|
|
15
|
+
"static/chunks/main-app-dd261207182e5a23.js"
|
|
16
|
+
],
|
|
17
|
+
"rootMainFilesTree": {},
|
|
18
|
+
"pages": {
|
|
19
|
+
"/_app": [
|
|
20
|
+
"static/chunks/webpack-e1ae44446e7f7355.js",
|
|
21
|
+
"static/chunks/framework-ac73abd125e371fe.js",
|
|
22
|
+
"static/chunks/main-ee293fa6aa18bdd1.js",
|
|
23
|
+
"static/chunks/pages/_app-7d307437aca18ad4.js"
|
|
24
|
+
],
|
|
25
|
+
"/_error": [
|
|
26
|
+
"static/chunks/webpack-e1ae44446e7f7355.js",
|
|
27
|
+
"static/chunks/framework-ac73abd125e371fe.js",
|
|
28
|
+
"static/chunks/main-ee293fa6aa18bdd1.js",
|
|
29
|
+
"static/chunks/pages/_error-cb2a52f75f2162e2.js"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"ampFirstPages": []
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"Next.js","version":"15.5.12"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"images": {
|
|
4
|
+
"deviceSizes": [
|
|
5
|
+
640,
|
|
6
|
+
750,
|
|
7
|
+
828,
|
|
8
|
+
1080,
|
|
9
|
+
1200,
|
|
10
|
+
1920,
|
|
11
|
+
2048,
|
|
12
|
+
3840
|
|
13
|
+
],
|
|
14
|
+
"imageSizes": [
|
|
15
|
+
16,
|
|
16
|
+
32,
|
|
17
|
+
48,
|
|
18
|
+
64,
|
|
19
|
+
96,
|
|
20
|
+
128,
|
|
21
|
+
256,
|
|
22
|
+
384
|
|
23
|
+
],
|
|
24
|
+
"path": "/_next/image",
|
|
25
|
+
"loader": "default",
|
|
26
|
+
"loaderFile": "",
|
|
27
|
+
"domains": [],
|
|
28
|
+
"disableStaticImages": false,
|
|
29
|
+
"minimumCacheTTL": 60,
|
|
30
|
+
"formats": [
|
|
31
|
+
"image/webp"
|
|
32
|
+
],
|
|
33
|
+
"maximumResponseBody": 50000000,
|
|
34
|
+
"dangerouslyAllowSVG": false,
|
|
35
|
+
"contentSecurityPolicy": "script-src 'none'; frame-src 'none'; sandbox;",
|
|
36
|
+
"contentDispositionType": "attachment",
|
|
37
|
+
"remotePatterns": [],
|
|
38
|
+
"unoptimized": false,
|
|
39
|
+
"sizes": [
|
|
40
|
+
640,
|
|
41
|
+
750,
|
|
42
|
+
828,
|
|
43
|
+
1080,
|
|
44
|
+
1200,
|
|
45
|
+
1920,
|
|
46
|
+
2048,
|
|
47
|
+
3840,
|
|
48
|
+
16,
|
|
49
|
+
32,
|
|
50
|
+
48,
|
|
51
|
+
64,
|
|
52
|
+
96,
|
|
53
|
+
128,
|
|
54
|
+
256,
|
|
55
|
+
384
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":1,"files":["../node_modules/@swc/helpers/_/_interop_require_default/package.json","../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../node_modules/@swc/helpers/package.json","../node_modules/client-only/index.js","../node_modules/client-only/package.json","../node_modules/next/dist/client/components/app-router-headers.js","../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../node_modules/next/dist/compiled/babel-code-frame/index.js","../node_modules/next/dist/compiled/babel-code-frame/package.json","../node_modules/next/dist/compiled/babel/code-frame.js","../node_modules/next/dist/compiled/babel/package.json","../node_modules/next/dist/compiled/bytes/index.js","../node_modules/next/dist/compiled/bytes/package.json","../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../node_modules/next/dist/compiled/source-map/package.json","../node_modules/next/dist/compiled/source-map/source-map.js","../node_modules/next/dist/compiled/stacktrace-parser/package.json","../node_modules/next/dist/compiled/stacktrace-parser/stack-trace-parser.cjs.js","../node_modules/next/dist/compiled/ws/index.js","../node_modules/next/dist/compiled/ws/package.json","../node_modules/next/dist/experimental/testmode/context.js","../node_modules/next/dist/experimental/testmode/fetch.js","../node_modules/next/dist/experimental/testmode/server-edge.js","../node_modules/next/dist/lib/client-and-server-references.js","../node_modules/next/dist/lib/constants.js","../node_modules/next/dist/lib/interop-default.js","../node_modules/next/dist/lib/is-error.js","../node_modules/next/dist/lib/picocolors.js","../node_modules/next/dist/next-devtools/server/shared.js","../node_modules/next/dist/server/after/builtin-request-context.js","../node_modules/next/dist/server/app-render/after-task-async-storage-instance.js","../node_modules/next/dist/server/app-render/after-task-async-storage.external.js","../node_modules/next/dist/server/app-render/async-local-storage.js","../node_modules/next/dist/server/app-render/work-async-storage-instance.js","../node_modules/next/dist/server/app-render/work-async-storage.external.js","../node_modules/next/dist/server/app-render/work-unit-async-storage-instance.js","../node_modules/next/dist/server/app-render/work-unit-async-storage.external.js","../node_modules/next/dist/server/body-streams.js","../node_modules/next/dist/server/lib/cache-handlers/default.external.js","../node_modules/next/dist/server/lib/incremental-cache/memory-cache.external.js","../node_modules/next/dist/server/lib/incremental-cache/shared-cache-controls.external.js","../node_modules/next/dist/server/lib/incremental-cache/tags-manifest.external.js","../node_modules/next/dist/server/lib/lru-cache.js","../node_modules/next/dist/server/lib/parse-stack.js","../node_modules/next/dist/server/lib/router-utils/instrumentation-globals.external.js","../node_modules/next/dist/server/lib/router-utils/instrumentation-node-extensions.js","../node_modules/next/dist/server/lib/router-utils/router-server-context.js","../node_modules/next/dist/server/lib/source-maps.js","../node_modules/next/dist/server/lib/trace/constants.js","../node_modules/next/dist/server/lib/trace/tracer.js","../node_modules/next/dist/server/load-manifest.external.js","../node_modules/next/dist/server/patch-error-inspect.js","../node_modules/next/dist/server/response-cache/types.js","../node_modules/next/dist/server/route-modules/app-page/module.compiled.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/amp-context.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/router-context.js","../node_modules/next/dist/server/route-modules/app-page/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/route-modules/pages/module.compiled.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/amp-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/html-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/loadable.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/router-context.js","../node_modules/next/dist/server/route-modules/pages/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/web/utils.js","../node_modules/next/dist/shared/lib/constants.js","../node_modules/next/dist/shared/lib/deep-freeze.js","../node_modules/next/dist/shared/lib/error-source.js","../node_modules/next/dist/shared/lib/invariant-error.js","../node_modules/next/dist/shared/lib/is-internal.js","../node_modules/next/dist/shared/lib/is-plain-object.js","../node_modules/next/dist/shared/lib/is-thenable.js","../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../node_modules/next/dist/shared/lib/no-fallback-error.external.js","../node_modules/next/dist/shared/lib/runtime-config.external.js","../node_modules/next/dist/shared/lib/server-reference-info.js","../node_modules/next/package.json","../node_modules/react/cjs/react.production.js","../node_modules/react/index.js","../node_modules/react/package.json","../node_modules/styled-jsx/dist/index/index.js","../node_modules/styled-jsx/index.js","../node_modules/styled-jsx/package.json","../node_modules/styled-jsx/style.js","package.json"]}
|