proof-of-commitment 1.11.0 → 1.12.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.
- package/index.js +145 -2
- 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.0
|
|
4
4
|
* Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
|
|
5
5
|
* Usage: npx proof-of-commitment [packages...] [options]
|
|
6
6
|
*/
|
|
@@ -182,6 +182,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
182
182
|
const topPkgs = results.slice(0, 10).map(r => r.name).join(',');
|
|
183
183
|
console.log(clr(c.cyan, `\n 🔗 Full report: ${WEB}?packages=${encodeURIComponent(topPkgs)}`));
|
|
184
184
|
console.log(clr(c.cyan, ` 🤖 GitHub Action: github.com/piiiico/commit-action — block CRITICAL packages in CI`));
|
|
185
|
+
console.log(clr(c.dim, ` 📋 Add to this project: `) + clr(c.cyan, `poc init`) + clr(c.dim, ` — creates workflow + README badge`));
|
|
185
186
|
|
|
186
187
|
// Contextual upsell — show when findings make monitoring relevant
|
|
187
188
|
if (effectiveCritical > 0) {
|
|
@@ -201,7 +202,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
201
202
|
|
|
202
203
|
function printHelp() {
|
|
203
204
|
console.log(`
|
|
204
|
-
${clr(c.bold, 'proof-of-commitment')} v1.
|
|
205
|
+
${clr(c.bold, 'proof-of-commitment')} v1.12.0 — supply chain risk scorer
|
|
205
206
|
|
|
206
207
|
${clr(c.bold, 'Usage:')}
|
|
207
208
|
npx proof-of-commitment Auto-detect manifest in current dir
|
|
@@ -218,6 +219,10 @@ ${clr(c.bold, 'Usage:')}
|
|
|
218
219
|
npx proof-of-commitment --file go.mod Audit Go direct + indirect deps
|
|
219
220
|
npx proof-of-commitment --file go.sum Audit Go full transitive set
|
|
220
221
|
|
|
222
|
+
${clr(c.bold, 'Setup:')}
|
|
223
|
+
poc init Add a GitHub Action + README badge to the current project
|
|
224
|
+
Auto-detects ecosystem. Blocks CRITICAL packages on every PR.
|
|
225
|
+
|
|
221
226
|
${clr(c.bold, 'Reports:')}
|
|
222
227
|
poc report Scan and generate a shareable HTML report + Markdown snippet
|
|
223
228
|
poc report [pkgs] Same flags as scan — packages, --pypi, --cargo, --file, etc.
|
|
@@ -1251,6 +1256,139 @@ async function cmdReport(packages, ecosystem, { filePath, isLockfile, totalScann
|
|
|
1251
1256
|
console.log();
|
|
1252
1257
|
}
|
|
1253
1258
|
|
|
1259
|
+
/**
|
|
1260
|
+
* poc init — scaffold a GitHub Action workflow + README badge for the current project.
|
|
1261
|
+
* Turns every CLI user into a permanent distribution node.
|
|
1262
|
+
*/
|
|
1263
|
+
async function cmdInit() {
|
|
1264
|
+
const fs = await import('fs');
|
|
1265
|
+
const path = await import('path');
|
|
1266
|
+
const cwd = process.cwd();
|
|
1267
|
+
|
|
1268
|
+
// Detect ecosystem from project files
|
|
1269
|
+
let ecosystem = 'npm';
|
|
1270
|
+
let manifestName = 'package.json';
|
|
1271
|
+
const checks = [
|
|
1272
|
+
['package.json', 'npm'],
|
|
1273
|
+
['package-lock.json', 'npm'],
|
|
1274
|
+
['yarn.lock', 'npm'],
|
|
1275
|
+
['pnpm-lock.yaml', 'npm'],
|
|
1276
|
+
['requirements.txt', 'pypi'],
|
|
1277
|
+
['Cargo.toml', 'cargo'],
|
|
1278
|
+
['go.mod', 'golang'],
|
|
1279
|
+
];
|
|
1280
|
+
for (const [file, eco] of checks) {
|
|
1281
|
+
if (fs.existsSync(path.join(cwd, file))) {
|
|
1282
|
+
ecosystem = eco;
|
|
1283
|
+
manifestName = file;
|
|
1284
|
+
break;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
console.log(clr(c.bold, '\n Commit — supply chain audit for CI\n'));
|
|
1289
|
+
console.log(clr(c.dim, ` Detected: ${manifestName} (${ecosystem})`));
|
|
1290
|
+
|
|
1291
|
+
// ── 1. GitHub Action workflow ──
|
|
1292
|
+
const workflowDir = path.join(cwd, '.github', 'workflows');
|
|
1293
|
+
const workflowPath = path.join(workflowDir, 'commit-audit.yml');
|
|
1294
|
+
|
|
1295
|
+
const ecoFlag = ecosystem === 'npm' ? '' : ` --${ecosystem === 'golang' ? 'go' : ecosystem}`;
|
|
1296
|
+
const workflowContent = `# Supply chain audit — powered by Commit (getcommit.dev)
|
|
1297
|
+
# Scores dependencies on behavioral signals: publisher concentration,
|
|
1298
|
+
# download anomalies, release patterns, trusted publishing adoption.
|
|
1299
|
+
# Blocks PRs when CRITICAL packages are found (configurable).
|
|
1300
|
+
|
|
1301
|
+
name: Supply Chain Audit
|
|
1302
|
+
|
|
1303
|
+
on:
|
|
1304
|
+
pull_request:
|
|
1305
|
+
push:
|
|
1306
|
+
branches: [main, master]
|
|
1307
|
+
schedule:
|
|
1308
|
+
- cron: '0 9 * * 1' # Weekly Monday 09:00 UTC
|
|
1309
|
+
|
|
1310
|
+
jobs:
|
|
1311
|
+
audit:
|
|
1312
|
+
runs-on: ubuntu-latest
|
|
1313
|
+
steps:
|
|
1314
|
+
- uses: actions/checkout@v4
|
|
1315
|
+
- uses: actions/setup-node@v4
|
|
1316
|
+
with:
|
|
1317
|
+
node-version: '20'
|
|
1318
|
+
- run: npx -y proof-of-commitment${ecoFlag} --fail-on=critical
|
|
1319
|
+
`;
|
|
1320
|
+
|
|
1321
|
+
let workflowCreated = false;
|
|
1322
|
+
if (fs.existsSync(workflowPath)) {
|
|
1323
|
+
console.log(clr(c.yellow, ` ⚠ .github/workflows/commit-audit.yml already exists — skipped`));
|
|
1324
|
+
} else {
|
|
1325
|
+
fs.mkdirSync(workflowDir, { recursive: true });
|
|
1326
|
+
fs.writeFileSync(workflowPath, workflowContent);
|
|
1327
|
+
workflowCreated = true;
|
|
1328
|
+
console.log(clr(c.green, ` ✓ Created .github/workflows/commit-audit.yml`));
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// ── 2. README badge ──
|
|
1332
|
+
// Try to read project name from package.json or directory name
|
|
1333
|
+
let projectName = path.basename(cwd);
|
|
1334
|
+
try {
|
|
1335
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'));
|
|
1336
|
+
if (pkg.name) projectName = pkg.name;
|
|
1337
|
+
} catch {}
|
|
1338
|
+
|
|
1339
|
+
const badgeUrl = `https://poc-backend.amdal-dev.workers.dev/badge/npm/${encodeURIComponent(projectName)}`;
|
|
1340
|
+
const auditUrl = `https://getcommit.dev/audit?packages=${encodeURIComponent(projectName)}`;
|
|
1341
|
+
const badgeMd = `[](${auditUrl})`;
|
|
1342
|
+
|
|
1343
|
+
console.log(clr(c.green, ` ✓ README badge (paste into your README.md):\n`));
|
|
1344
|
+
console.log(` ${badgeMd}\n`);
|
|
1345
|
+
|
|
1346
|
+
// ── 3. Try to auto-insert badge into README ──
|
|
1347
|
+
let badgeInserted = false;
|
|
1348
|
+
const readmeCandidates = ['README.md', 'readme.md', 'Readme.md'];
|
|
1349
|
+
for (const name of readmeCandidates) {
|
|
1350
|
+
const readmePath = path.join(cwd, name);
|
|
1351
|
+
if (fs.existsSync(readmePath)) {
|
|
1352
|
+
const content = fs.readFileSync(readmePath, 'utf-8');
|
|
1353
|
+
if (content.includes('Commit Score') || content.includes('poc-backend.amdal-dev.workers.dev/badge')) {
|
|
1354
|
+
console.log(clr(c.dim, ` Badge already in ${name} — skipped`));
|
|
1355
|
+
badgeInserted = true;
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
// Insert after the first H1 heading, or at the top
|
|
1359
|
+
const h1Match = content.match(/^#\s+.+$/m);
|
|
1360
|
+
let newContent;
|
|
1361
|
+
if (h1Match) {
|
|
1362
|
+
const insertPos = h1Match.index + h1Match[0].length;
|
|
1363
|
+
newContent = content.slice(0, insertPos) + '\n\n' + badgeMd + content.slice(insertPos);
|
|
1364
|
+
} else {
|
|
1365
|
+
newContent = badgeMd + '\n\n' + content;
|
|
1366
|
+
}
|
|
1367
|
+
fs.writeFileSync(readmePath, newContent);
|
|
1368
|
+
badgeInserted = true;
|
|
1369
|
+
console.log(clr(c.green, ` ✓ Badge added to ${name}`));
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (!badgeInserted) {
|
|
1374
|
+
console.log(clr(c.dim, ` No README found — paste the badge manually`));
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// ── 4. Next steps ──
|
|
1378
|
+
console.log(clr(c.bold, '\n What happens next:\n'));
|
|
1379
|
+
if (workflowCreated) {
|
|
1380
|
+
console.log(clr(c.white, ' 1. Commit and push — the Action runs on your next PR'));
|
|
1381
|
+
console.log(clr(c.white, ' 2. PRs with CRITICAL dependencies are blocked automatically'));
|
|
1382
|
+
console.log(clr(c.white, ' 3. The badge updates daily with your project\'s score'));
|
|
1383
|
+
} else {
|
|
1384
|
+
console.log(clr(c.white, ' 1. The badge updates daily with your project\'s score'));
|
|
1385
|
+
console.log(clr(c.white, ' 2. Push to trigger the existing workflow'));
|
|
1386
|
+
}
|
|
1387
|
+
console.log(clr(c.dim, `\n Want daily monitoring + alerts? Get a free key:`));
|
|
1388
|
+
console.log(clr(c.cyan, ' https://getcommit.dev/get-started'));
|
|
1389
|
+
console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login') + clr(c.dim, ' + ') + clr(c.cyan, 'poc watch <package>\n'));
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1254
1392
|
async function main() {
|
|
1255
1393
|
const args = process.argv.slice(2);
|
|
1256
1394
|
|
|
@@ -1262,6 +1400,11 @@ async function main() {
|
|
|
1262
1400
|
// Subcommands
|
|
1263
1401
|
const subcmd = args[0];
|
|
1264
1402
|
|
|
1403
|
+
if (subcmd === 'init') {
|
|
1404
|
+
await cmdInit();
|
|
1405
|
+
process.exit(0);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1265
1408
|
if (subcmd === 'login') {
|
|
1266
1409
|
const keyArg = args[1] || null;
|
|
1267
1410
|
await cmdLogin(keyArg);
|