proof-of-commitment 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +88 -0
  2. package/index.js +264 -0
  3. package/package.json +37 -0
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # proof-of-commitment
2
+
3
+ > Supply chain risk scorer for npm and PyPI packages. Behavioral signals that can't be faked.
4
+
5
+ ```
6
+ npx proof-of-commitment axios zod chalk
7
+ ```
8
+
9
+ ```
10
+ ──────────────────────────────────────────────────────────────────────────
11
+ Package Risk Score Maintainers Downloads Age
12
+ ──────────────────────────────────────────────────────────────────────────
13
+ axios 🔴 CRITICAL 89 1 102.0M/wk 11.6y
14
+ └ longevity=25 momentum=25 releases=20 maintainers=4 github=15
15
+ zod 🔴 CRITICAL 83 1 154.0M/wk 6.1y
16
+ └ longevity=25 momentum=25 releases=18 maintainers=4 github=11
17
+ chalk 🔴 CRITICAL 75 1 414.6M/wk 12.7y
18
+ └ longevity=25 momentum=22 releases=13 maintainers=4 github=11
19
+ ──────────────────────────────────────────────────────────────────────────
20
+
21
+ ⚠ 3 CRITICAL packages found.
22
+ CRITICAL = sole maintainer + >10M weekly downloads (high-value attack target)
23
+ Full breakdown: https://getcommit.dev/audit?packages=axios,zod,chalk
24
+ ```
25
+
26
+ ## What this does
27
+
28
+ `npm audit` finds *known* CVEs — vulnerabilities already catalogued in a database. This scores *structural risk before it becomes a CVE*.
29
+
30
+ The axios attack on April 1st, 2026: `npm audit` showed zero issues beforehand. Proof of Commitment flagged axios as CRITICAL (1 maintainer, 96M downloads/week) — the exact profile that made it a high-value target.
31
+
32
+ **Score dimensions:**
33
+ - **Longevity** (25 pts) — years in production
34
+ - **Download Momentum** (25 pts) — weekly download trend
35
+ - **Release Consistency** (20 pts) — days since last release
36
+ - **Maintainer Depth** (15 pts) — team size
37
+ - **GitHub Backing** (15 pts) — organization/team support
38
+
39
+ **CRITICAL** = sole maintainer + >10M weekly downloads (high-value attack target)
40
+
41
+ ## Usage
42
+
43
+ ```bash
44
+ # Score npm packages
45
+ npx proof-of-commitment axios zod chalk lodash express
46
+
47
+ # Score PyPI packages
48
+ npx proof-of-commitment --pypi litellm langchain requests numpy
49
+
50
+ # Auto-detect from package.json or requirements.txt
51
+ npx proof-of-commitment --file package.json
52
+ npx proof-of-commitment --file requirements.txt
53
+
54
+ # Short alias
55
+ npx poc axios zod chalk
56
+ ```
57
+
58
+ ## Zero-install MCP server (for Claude, Cursor, Windsurf)
59
+
60
+ Add to your AI tool's config:
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "proof-of-commitment": {
65
+ "type": "streamable-http",
66
+ "url": "https://poc-backend.amdal-dev.workers.dev/mcp"
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ Then ask: *"Audit the dependencies in my package.json"* or *"What's the risk profile of vercel/ai?"*
73
+
74
+ ## GitHub Action
75
+
76
+ Posts audit results directly on your PR:
77
+ ```yaml
78
+ - uses: piiiico/proof-of-commitment@main
79
+ with:
80
+ fail-on-critical: false
81
+ comment-on-pr: true
82
+ ```
83
+
84
+ ## Links
85
+
86
+ - **Web demo:** https://getcommit.dev/audit
87
+ - **Live watchlist:** https://getcommit.dev/watchlist (top 25 npm packages by structural risk)
88
+ - **GitHub:** https://github.com/piiiico/proof-of-commitment
package/index.js ADDED
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * proof-of-commitment CLI
4
+ * Scores npm/PyPI packages on behavioral commitment signals.
5
+ * Usage: npx proof-of-commitment [packages...] [options]
6
+ */
7
+
8
+ const API = 'https://poc-backend.amdal-dev.workers.dev/api/audit';
9
+ const WEB = 'https://getcommit.dev/audit';
10
+
11
+ // ANSI color helpers
12
+ const c = {
13
+ reset: '\x1b[0m',
14
+ bold: '\x1b[1m',
15
+ dim: '\x1b[2m',
16
+ red: '\x1b[31m',
17
+ yellow: '\x1b[33m',
18
+ green: '\x1b[32m',
19
+ cyan: '\x1b[36m',
20
+ white: '\x1b[37m',
21
+ bgRed: '\x1b[41m',
22
+ bgYellow: '\x1b[43m',
23
+ };
24
+
25
+ const NO_COLOR = process.env.NO_COLOR || !process.stdout.isTTY;
26
+ const col = NO_COLOR ? () => '' : (code) => code;
27
+
28
+ function clr(code, text) {
29
+ if (NO_COLOR) return text;
30
+ return `${code}${text}${c.reset}`;
31
+ }
32
+
33
+ function riskColor(flags, score) {
34
+ if (flags && flags.includes('CRITICAL')) return c.red + c.bold;
35
+ if (score < 40) return c.yellow + c.bold;
36
+ if (score < 60) return c.yellow;
37
+ return c.green;
38
+ }
39
+
40
+ function riskLabel(flags, score) {
41
+ if (flags && flags.includes('CRITICAL')) return '🔴 CRITICAL';
42
+ if (score < 40) return '🟠 HIGH';
43
+ if (score < 60) return '🟡 MODERATE';
44
+ if (score < 75) return '🟡 GOOD';
45
+ return '🟢 HEALTHY';
46
+ }
47
+
48
+ function fmtDownloads(n) {
49
+ if (n >= 1e9) return (n / 1e9).toFixed(1) + 'B/wk';
50
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M/wk';
51
+ if (n >= 1e3) return (n / 1e3).toFixed(0) + 'K/wk';
52
+ return n + '/wk';
53
+ }
54
+
55
+ function padEnd(str, len) {
56
+ const visible = str.replace(/\x1b\[[0-9;]*m/g, '');
57
+ return str + ' '.repeat(Math.max(0, len - visible.length));
58
+ }
59
+
60
+ function printTable(results) {
61
+ const COL = {
62
+ name: 20, risk: 14, score: 7, maintainers: 12, downloads: 12, age: 8,
63
+ };
64
+
65
+ const header = [
66
+ padEnd(clr(c.bold, 'Package'), COL.name),
67
+ padEnd(clr(c.bold, 'Risk'), COL.risk),
68
+ padEnd(clr(c.bold, 'Score'), COL.score),
69
+ padEnd(clr(c.bold, 'Maintainers'), COL.maintainers),
70
+ padEnd(clr(c.bold, 'Downloads'), COL.downloads),
71
+ padEnd(clr(c.bold, 'Age'), COL.age),
72
+ ].join(' ');
73
+
74
+ const divider = '─'.repeat(COL.name + COL.risk + COL.score + COL.maintainers + COL.downloads + COL.age + 10);
75
+
76
+ console.log('\n' + divider);
77
+ console.log(header);
78
+ console.log(divider);
79
+
80
+ let criticalCount = 0;
81
+
82
+ for (const pkg of results) {
83
+ const rc = riskColor(pkg.riskFlags, pkg.score);
84
+ const label = riskLabel(pkg.riskFlags, pkg.score);
85
+ if (pkg.riskFlags && pkg.riskFlags.includes('CRITICAL')) criticalCount++;
86
+
87
+ const row = [
88
+ padEnd(pkg.name, COL.name),
89
+ padEnd(clr(rc, label), COL.risk),
90
+ padEnd(String(pkg.score), COL.score),
91
+ padEnd(String(pkg.maintainers || '?'), COL.maintainers),
92
+ padEnd(fmtDownloads(pkg.weeklyDownloads || 0), COL.downloads),
93
+ padEnd((pkg.ageYears || '?').toString().replace(/(\.\d).*/, '$1') + 'y', COL.age),
94
+ ].join(' ');
95
+
96
+ console.log(row);
97
+
98
+ // Score breakdown if available
99
+ if (pkg.scoreBreakdown) {
100
+ const b = pkg.scoreBreakdown;
101
+ const breakdown = clr(c.dim,
102
+ ` └ longevity=${b.longevity} momentum=${b.downloadMomentum} ` +
103
+ `releases=${b.releaseConsistency} maintainers=${b.maintainerDepth} github=${b.githubBacking}`
104
+ );
105
+ console.log(breakdown);
106
+ }
107
+ }
108
+
109
+ console.log(divider);
110
+
111
+ if (criticalCount > 0) {
112
+ console.log('\n' + clr(c.red + c.bold, `⚠ ${criticalCount} CRITICAL package${criticalCount > 1 ? 's' : ''} found.`));
113
+ console.log(clr(c.dim, ' CRITICAL = sole maintainer + >10M weekly downloads (high-value attack target)'));
114
+ console.log(clr(c.dim, ` Full breakdown: ${WEB}?packages=${results.map(r => r.name).join(',')}`));
115
+ } else {
116
+ console.log('\n' + clr(c.green, '✓ No CRITICAL packages found.'));
117
+ }
118
+ console.log();
119
+ }
120
+
121
+ function printHelp() {
122
+ console.log(`
123
+ ${clr(c.bold, 'proof-of-commitment')} — supply chain risk scorer
124
+
125
+ ${clr(c.bold, 'Usage:')}
126
+ npx proof-of-commitment [packages...] Score npm packages
127
+ npx proof-of-commitment --pypi [pkgs...] Score PyPI packages
128
+ npx proof-of-commitment --file <path> Auto-detect from package.json / requirements.txt
129
+
130
+ ${clr(c.bold, 'Examples:')}
131
+ npx proof-of-commitment axios zod chalk
132
+ npx proof-of-commitment --pypi litellm langchain requests
133
+ npx proof-of-commitment --file package.json
134
+ npx proof-of-commitment --file requirements.txt
135
+
136
+ ${clr(c.bold, 'Score meaning:')}
137
+ 🔴 CRITICAL Sole maintainer + >10M downloads/wk (high-value attack target)
138
+ 🟠 HIGH Score < 40
139
+ 🟡 MODERATE Score 40–59
140
+ 🟡 GOOD Score 60–74
141
+ 🟢 HEALTHY Score 75+
142
+
143
+ ${clr(c.bold, 'Score dimensions:')} longevity · download momentum · release consistency · maintainer depth · GitHub backing
144
+
145
+ ${clr(c.bold, 'Web:')} ${WEB}
146
+ ${clr(c.bold, 'MCP:')} ${clr(c.dim, 'Add to Claude Desktop / Cursor for AI-assisted auditing')}
147
+ `);
148
+ }
149
+
150
+ async function readPackagesFromFile(filePath) {
151
+ const fs = await import('fs');
152
+ const path = await import('path');
153
+ const content = fs.readFileSync(filePath, 'utf-8');
154
+ const basename = path.basename(filePath).toLowerCase();
155
+
156
+ if (basename === 'package.json' || filePath.endsWith('.json')) {
157
+ const pkg = JSON.parse(content);
158
+ const deps = {
159
+ ...pkg.dependencies,
160
+ ...pkg.devDependencies,
161
+ };
162
+ return { packages: Object.keys(deps).slice(0, 20), ecosystem: 'npm' };
163
+ }
164
+
165
+ if (basename === 'requirements.txt' || filePath.endsWith('.txt')) {
166
+ const pkgs = content
167
+ .split('\n')
168
+ .map(l => l.trim())
169
+ .filter(l => l && !l.startsWith('#'))
170
+ .map(l => l.replace(/[>=<!\s].*/,'').trim())
171
+ .filter(Boolean)
172
+ .slice(0, 20);
173
+ return { packages: pkgs, ecosystem: 'pypi' };
174
+ }
175
+
176
+ throw new Error(`Unsupported file: ${filePath}. Use package.json or requirements.txt`);
177
+ }
178
+
179
+ async function main() {
180
+ const args = process.argv.slice(2);
181
+
182
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
183
+ printHelp();
184
+ process.exit(0);
185
+ }
186
+
187
+ let ecosystem = 'npm';
188
+ let packages = [];
189
+ let filePath = null;
190
+
191
+ let i = 0;
192
+ while (i < args.length) {
193
+ const a = args[i];
194
+ if (a === '--pypi') { ecosystem = 'pypi'; i++; }
195
+ else if (a === '--npm') { ecosystem = 'npm'; i++; }
196
+ else if (a === '--file' || a === '-f') {
197
+ filePath = args[++i];
198
+ i++;
199
+ }
200
+ else if (a.startsWith('--')) {
201
+ console.error(`Unknown flag: ${a}`);
202
+ process.exit(1);
203
+ }
204
+ else { packages.push(a); i++; }
205
+ }
206
+
207
+ if (filePath) {
208
+ try {
209
+ const result = await readPackagesFromFile(filePath);
210
+ packages = result.packages;
211
+ ecosystem = result.ecosystem;
212
+ console.log(clr(c.dim, `Detected ${packages.length} packages from ${filePath} (${ecosystem})`));
213
+ } catch (err) {
214
+ console.error(`Error reading ${filePath}: ${err.message}`);
215
+ process.exit(1);
216
+ }
217
+ }
218
+
219
+ if (packages.length === 0) {
220
+ console.error('No packages specified. Run with --help for usage.');
221
+ process.exit(1);
222
+ }
223
+
224
+ if (packages.length > 20) {
225
+ console.warn(clr(c.yellow, `Warning: truncating to first 20 packages (got ${packages.length})`));
226
+ packages = packages.slice(0, 20);
227
+ }
228
+
229
+ const pkgList = packages.join(', ');
230
+ process.stdout.write(clr(c.dim, `Scoring ${packages.length} ${ecosystem} package${packages.length > 1 ? 's' : ''}...`));
231
+
232
+ const t0 = Date.now();
233
+ let data;
234
+ try {
235
+ const res = await fetch(API, {
236
+ method: 'POST',
237
+ headers: { 'Content-Type': 'application/json' },
238
+ body: JSON.stringify({ packages, ecosystem }),
239
+ });
240
+ if (!res.ok) {
241
+ const text = await res.text();
242
+ throw new Error(`API error ${res.status}: ${text}`);
243
+ }
244
+ data = await res.json();
245
+ } catch (err) {
246
+ console.error(`\nError: ${err.message}`);
247
+ process.exit(1);
248
+ }
249
+
250
+ const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
251
+ process.stdout.write(clr(c.dim, ` done in ${elapsed}s\n`));
252
+
253
+ if (!data.results || data.results.length === 0) {
254
+ console.log('No results returned. Check package names and try again.');
255
+ process.exit(0);
256
+ }
257
+
258
+ printTable(data.results);
259
+ }
260
+
261
+ main().catch(err => {
262
+ console.error('Unexpected error:', err.message);
263
+ process.exit(1);
264
+ });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "proof-of-commitment",
3
+ "version": "1.0.0",
4
+ "description": "Supply chain risk scorer for npm and PyPI packages — behavioral signals that can't be faked",
5
+ "type": "module",
6
+ "bin": {
7
+ "proof-of-commitment": "./index.js",
8
+ "poc": "./index.js"
9
+ },
10
+ "main": "./index.js",
11
+ "files": [
12
+ "index.js",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "supply-chain",
17
+ "security",
18
+ "npm",
19
+ "pypi",
20
+ "dependencies",
21
+ "audit",
22
+ "risk",
23
+ "behavioral",
24
+ "commitment",
25
+ "maintainer"
26
+ ],
27
+ "author": "piiiico",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/piiiico/proof-of-commitment"
32
+ },
33
+ "homepage": "https://getcommit.dev/audit",
34
+ "engines": {
35
+ "node": ">=18"
36
+ }
37
+ }