proof-of-commitment 1.11.0 → 1.12.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.
- package/index.js +163 -7
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* proof-of-commitment CLI v1.
|
|
3
|
+
* proof-of-commitment CLI v1.12.1
|
|
4
4
|
* Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
|
|
5
5
|
* Usage: npx proof-of-commitment [packages...] [options]
|
|
6
6
|
*/
|
|
@@ -180,8 +180,22 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
180
180
|
|
|
181
181
|
// Footer with web link + CI integration CTA
|
|
182
182
|
const topPkgs = results.slice(0, 10).map(r => r.name).join(',');
|
|
183
|
-
|
|
183
|
+
const utm = 'utm_source=cli&utm_medium=audit';
|
|
184
|
+
console.log(clr(c.cyan, `\n 🔗 Full report: ${WEB}?packages=${encodeURIComponent(topPkgs)}&${utm}`));
|
|
184
185
|
console.log(clr(c.cyan, ` 🤖 GitHub Action: github.com/piiiico/commit-action — block CRITICAL packages in CI`));
|
|
186
|
+
console.log(clr(c.dim, ` 📋 Add to this project: `) + clr(c.cyan, `poc init`) + clr(c.dim, ` — creates workflow + README badge`));
|
|
187
|
+
|
|
188
|
+
// Per-package profile URLs — drive traffic to permanent, indexable pages
|
|
189
|
+
const ecoPath = { npm: 'npm', pypi: 'pypi', cargo: 'cargo', golang: 'go' };
|
|
190
|
+
const profilePkgs = results.slice(0, 5).filter(r => r.name && ecoPath[r.ecosystem || 'npm']);
|
|
191
|
+
if (profilePkgs.length > 0) {
|
|
192
|
+
console.log(clr(c.dim, `\n 📄 Package profiles:`));
|
|
193
|
+
for (const r of profilePkgs) {
|
|
194
|
+
const eco = ecoPath[r.ecosystem || 'npm'];
|
|
195
|
+
const name = encodeURIComponent(r.name).replace(/%40/g, '@').replace(/%2F/g, '/');
|
|
196
|
+
console.log(clr(c.dim, ` getcommit.dev/${eco}/${name}`));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
185
199
|
|
|
186
200
|
// Contextual upsell — show when findings make monitoring relevant
|
|
187
201
|
if (effectiveCritical > 0) {
|
|
@@ -192,7 +206,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
192
206
|
clr(c.cyan, `poc watch ${results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name}`));
|
|
193
207
|
} else {
|
|
194
208
|
console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this' : 'these ' + effectiveCritical} CRITICAL ${effectiveCritical === 1 ? 'package' : 'packages'} — get alerted when scores change.`));
|
|
195
|
-
console.log(clr(c.dim, ' Get a free API key: ') + clr(c.cyan, 'https://getcommit.dev/get-started'));
|
|
209
|
+
console.log(clr(c.dim, ' Get a free API key: ') + clr(c.cyan, 'https://getcommit.dev/get-started?utm_source=cli'));
|
|
196
210
|
console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login'));
|
|
197
211
|
}
|
|
198
212
|
}
|
|
@@ -201,7 +215,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
201
215
|
|
|
202
216
|
function printHelp() {
|
|
203
217
|
console.log(`
|
|
204
|
-
${clr(c.bold, 'proof-of-commitment')} v1.
|
|
218
|
+
${clr(c.bold, 'proof-of-commitment')} v1.12.1 — supply chain risk scorer
|
|
205
219
|
|
|
206
220
|
${clr(c.bold, 'Usage:')}
|
|
207
221
|
npx proof-of-commitment Auto-detect manifest in current dir
|
|
@@ -218,6 +232,10 @@ ${clr(c.bold, 'Usage:')}
|
|
|
218
232
|
npx proof-of-commitment --file go.mod Audit Go direct + indirect deps
|
|
219
233
|
npx proof-of-commitment --file go.sum Audit Go full transitive set
|
|
220
234
|
|
|
235
|
+
${clr(c.bold, 'Setup:')}
|
|
236
|
+
poc init Add a GitHub Action + README badge to the current project
|
|
237
|
+
Auto-detects ecosystem. Blocks CRITICAL packages on every PR.
|
|
238
|
+
|
|
221
239
|
${clr(c.bold, 'Reports:')}
|
|
222
240
|
poc report Scan and generate a shareable HTML report + Markdown snippet
|
|
223
241
|
poc report [pkgs] Same flags as scan — packages, --pypi, --cargo, --file, etc.
|
|
@@ -780,7 +798,7 @@ async function cmdLogin(keyArg) {
|
|
|
780
798
|
console.log(clr(c.dim, ' poc watchlist View monitored packages'));
|
|
781
799
|
console.log(clr(c.dim, ' poc unwatch <package> Remove from monitoring'));
|
|
782
800
|
} else {
|
|
783
|
-
console.log(clr(c.dim, ' Upgrade to Pro for monitoring + alerts: https://getcommit.dev/pricing'));
|
|
801
|
+
console.log(clr(c.dim, ' Upgrade to Pro for monitoring + alerts: https://getcommit.dev/pricing?utm_source=cli'));
|
|
784
802
|
}
|
|
785
803
|
console.log();
|
|
786
804
|
}
|
|
@@ -859,7 +877,7 @@ async function cmdWatch(pkg, ecosystem) {
|
|
|
859
877
|
const key = await readApiKey();
|
|
860
878
|
if (!key) {
|
|
861
879
|
console.error(clr(c.red, 'No API key found. Set COMMIT_API_KEY or add api_key=<key> to ~/.commit/config'));
|
|
862
|
-
console.error(clr(c.dim, 'Get a key at https://getcommit.dev/pricing'));
|
|
880
|
+
console.error(clr(c.dim, 'Get a key at https://getcommit.dev/pricing?utm_source=cli'));
|
|
863
881
|
process.exit(1);
|
|
864
882
|
}
|
|
865
883
|
|
|
@@ -1124,7 +1142,7 @@ ${rows}
|
|
|
1124
1142
|
<div class="footer">
|
|
1125
1143
|
<span>Generated by <a href="${WEB}" target="_blank">proof-of-commitment</a></span>
|
|
1126
1144
|
<span><a href="https://github.com/piiiico/commit-action" target="_blank">GitHub Action</a></span>
|
|
1127
|
-
<span><a href="https://getcommit.dev/pricing" target="_blank">Commit Pro</a></span>
|
|
1145
|
+
<span><a href="https://getcommit.dev/pricing?utm_source=cli&utm_medium=report" target="_blank">Commit Pro</a></span>
|
|
1128
1146
|
</div>
|
|
1129
1147
|
<script>
|
|
1130
1148
|
function copyMd() {
|
|
@@ -1251,6 +1269,139 @@ async function cmdReport(packages, ecosystem, { filePath, isLockfile, totalScann
|
|
|
1251
1269
|
console.log();
|
|
1252
1270
|
}
|
|
1253
1271
|
|
|
1272
|
+
/**
|
|
1273
|
+
* poc init — scaffold a GitHub Action workflow + README badge for the current project.
|
|
1274
|
+
* Turns every CLI user into a permanent distribution node.
|
|
1275
|
+
*/
|
|
1276
|
+
async function cmdInit() {
|
|
1277
|
+
const fs = await import('fs');
|
|
1278
|
+
const path = await import('path');
|
|
1279
|
+
const cwd = process.cwd();
|
|
1280
|
+
|
|
1281
|
+
// Detect ecosystem from project files
|
|
1282
|
+
let ecosystem = 'npm';
|
|
1283
|
+
let manifestName = 'package.json';
|
|
1284
|
+
const checks = [
|
|
1285
|
+
['package.json', 'npm'],
|
|
1286
|
+
['package-lock.json', 'npm'],
|
|
1287
|
+
['yarn.lock', 'npm'],
|
|
1288
|
+
['pnpm-lock.yaml', 'npm'],
|
|
1289
|
+
['requirements.txt', 'pypi'],
|
|
1290
|
+
['Cargo.toml', 'cargo'],
|
|
1291
|
+
['go.mod', 'golang'],
|
|
1292
|
+
];
|
|
1293
|
+
for (const [file, eco] of checks) {
|
|
1294
|
+
if (fs.existsSync(path.join(cwd, file))) {
|
|
1295
|
+
ecosystem = eco;
|
|
1296
|
+
manifestName = file;
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
console.log(clr(c.bold, '\n Commit — supply chain audit for CI\n'));
|
|
1302
|
+
console.log(clr(c.dim, ` Detected: ${manifestName} (${ecosystem})`));
|
|
1303
|
+
|
|
1304
|
+
// ── 1. GitHub Action workflow ──
|
|
1305
|
+
const workflowDir = path.join(cwd, '.github', 'workflows');
|
|
1306
|
+
const workflowPath = path.join(workflowDir, 'commit-audit.yml');
|
|
1307
|
+
|
|
1308
|
+
const ecoFlag = ecosystem === 'npm' ? '' : ` --${ecosystem === 'golang' ? 'go' : ecosystem}`;
|
|
1309
|
+
const workflowContent = `# Supply chain audit — powered by Commit (getcommit.dev)
|
|
1310
|
+
# Scores dependencies on behavioral signals: publisher concentration,
|
|
1311
|
+
# download anomalies, release patterns, trusted publishing adoption.
|
|
1312
|
+
# Blocks PRs when CRITICAL packages are found (configurable).
|
|
1313
|
+
|
|
1314
|
+
name: Supply Chain Audit
|
|
1315
|
+
|
|
1316
|
+
on:
|
|
1317
|
+
pull_request:
|
|
1318
|
+
push:
|
|
1319
|
+
branches: [main, master]
|
|
1320
|
+
schedule:
|
|
1321
|
+
- cron: '0 9 * * 1' # Weekly Monday 09:00 UTC
|
|
1322
|
+
|
|
1323
|
+
jobs:
|
|
1324
|
+
audit:
|
|
1325
|
+
runs-on: ubuntu-latest
|
|
1326
|
+
steps:
|
|
1327
|
+
- uses: actions/checkout@v4
|
|
1328
|
+
- uses: actions/setup-node@v4
|
|
1329
|
+
with:
|
|
1330
|
+
node-version: '20'
|
|
1331
|
+
- run: npx -y proof-of-commitment${ecoFlag} --fail-on=critical
|
|
1332
|
+
`;
|
|
1333
|
+
|
|
1334
|
+
let workflowCreated = false;
|
|
1335
|
+
if (fs.existsSync(workflowPath)) {
|
|
1336
|
+
console.log(clr(c.yellow, ` ⚠ .github/workflows/commit-audit.yml already exists — skipped`));
|
|
1337
|
+
} else {
|
|
1338
|
+
fs.mkdirSync(workflowDir, { recursive: true });
|
|
1339
|
+
fs.writeFileSync(workflowPath, workflowContent);
|
|
1340
|
+
workflowCreated = true;
|
|
1341
|
+
console.log(clr(c.green, ` ✓ Created .github/workflows/commit-audit.yml`));
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// ── 2. README badge ──
|
|
1345
|
+
// Try to read project name from package.json or directory name
|
|
1346
|
+
let projectName = path.basename(cwd);
|
|
1347
|
+
try {
|
|
1348
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'));
|
|
1349
|
+
if (pkg.name) projectName = pkg.name;
|
|
1350
|
+
} catch {}
|
|
1351
|
+
|
|
1352
|
+
const badgeUrl = `https://poc-backend.amdal-dev.workers.dev/badge/npm/${encodeURIComponent(projectName)}`;
|
|
1353
|
+
const auditUrl = `https://getcommit.dev/audit?packages=${encodeURIComponent(projectName)}`;
|
|
1354
|
+
const badgeMd = `[](${auditUrl})`;
|
|
1355
|
+
|
|
1356
|
+
console.log(clr(c.green, ` ✓ README badge (paste into your README.md):\n`));
|
|
1357
|
+
console.log(` ${badgeMd}\n`);
|
|
1358
|
+
|
|
1359
|
+
// ── 3. Try to auto-insert badge into README ──
|
|
1360
|
+
let badgeInserted = false;
|
|
1361
|
+
const readmeCandidates = ['README.md', 'readme.md', 'Readme.md'];
|
|
1362
|
+
for (const name of readmeCandidates) {
|
|
1363
|
+
const readmePath = path.join(cwd, name);
|
|
1364
|
+
if (fs.existsSync(readmePath)) {
|
|
1365
|
+
const content = fs.readFileSync(readmePath, 'utf-8');
|
|
1366
|
+
if (content.includes('Commit Score') || content.includes('poc-backend.amdal-dev.workers.dev/badge')) {
|
|
1367
|
+
console.log(clr(c.dim, ` Badge already in ${name} — skipped`));
|
|
1368
|
+
badgeInserted = true;
|
|
1369
|
+
break;
|
|
1370
|
+
}
|
|
1371
|
+
// Insert after the first H1 heading, or at the top
|
|
1372
|
+
const h1Match = content.match(/^#\s+.+$/m);
|
|
1373
|
+
let newContent;
|
|
1374
|
+
if (h1Match) {
|
|
1375
|
+
const insertPos = h1Match.index + h1Match[0].length;
|
|
1376
|
+
newContent = content.slice(0, insertPos) + '\n\n' + badgeMd + content.slice(insertPos);
|
|
1377
|
+
} else {
|
|
1378
|
+
newContent = badgeMd + '\n\n' + content;
|
|
1379
|
+
}
|
|
1380
|
+
fs.writeFileSync(readmePath, newContent);
|
|
1381
|
+
badgeInserted = true;
|
|
1382
|
+
console.log(clr(c.green, ` ✓ Badge added to ${name}`));
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
if (!badgeInserted) {
|
|
1387
|
+
console.log(clr(c.dim, ` No README found — paste the badge manually`));
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// ── 4. Next steps ──
|
|
1391
|
+
console.log(clr(c.bold, '\n What happens next:\n'));
|
|
1392
|
+
if (workflowCreated) {
|
|
1393
|
+
console.log(clr(c.white, ' 1. Commit and push — the Action runs on your next PR'));
|
|
1394
|
+
console.log(clr(c.white, ' 2. PRs with CRITICAL dependencies are blocked automatically'));
|
|
1395
|
+
console.log(clr(c.white, ' 3. The badge updates daily with your project\'s score'));
|
|
1396
|
+
} else {
|
|
1397
|
+
console.log(clr(c.white, ' 1. The badge updates daily with your project\'s score'));
|
|
1398
|
+
console.log(clr(c.white, ' 2. Push to trigger the existing workflow'));
|
|
1399
|
+
}
|
|
1400
|
+
console.log(clr(c.dim, `\n Want daily monitoring + alerts? Get a free key:`));
|
|
1401
|
+
console.log(clr(c.cyan, ' https://getcommit.dev/get-started?utm_source=cli'));
|
|
1402
|
+
console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login') + clr(c.dim, ' + ') + clr(c.cyan, 'poc watch <package>\n'));
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1254
1405
|
async function main() {
|
|
1255
1406
|
const args = process.argv.slice(2);
|
|
1256
1407
|
|
|
@@ -1262,6 +1413,11 @@ async function main() {
|
|
|
1262
1413
|
// Subcommands
|
|
1263
1414
|
const subcmd = args[0];
|
|
1264
1415
|
|
|
1416
|
+
if (subcmd === 'init') {
|
|
1417
|
+
await cmdInit();
|
|
1418
|
+
process.exit(0);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1265
1421
|
if (subcmd === 'login') {
|
|
1266
1422
|
const keyArg = args[1] || null;
|
|
1267
1423
|
await cmdLogin(keyArg);
|