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.
Files changed (2) hide show
  1. package/index.js +145 -2
  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.11.0
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.11.0 — supply chain risk scorer
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 = `[![Commit Score](${badgeUrl})](${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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-of-commitment",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Supply chain risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
5
5
  "type": "module",
6
6
  "bin": {