proof-of-commitment 1.8.0 → 1.9.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/README.md +22 -0
- package/index.js +231 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,6 +51,28 @@ npx proof-of-commitment --file go.sum # full transitive set
|
|
|
51
51
|
|
|
52
52
|
**Web demo (no install):** [getcommit.dev/audit](https://getcommit.dev/audit) — paste your packages, see risk scores in seconds.
|
|
53
53
|
|
|
54
|
+
**Commit Pro — daily monitoring + alerts (v1.9.0):**
|
|
55
|
+
```bash
|
|
56
|
+
# Install once, then use the `poc` alias:
|
|
57
|
+
npm install -g proof-of-commitment
|
|
58
|
+
|
|
59
|
+
# Add packages to daily monitoring (requires Pro API key):
|
|
60
|
+
poc watch chalk
|
|
61
|
+
poc watch requests --ecosystem pypi
|
|
62
|
+
poc watch serde --ecosystem cargo
|
|
63
|
+
|
|
64
|
+
# View your watchlist with current scores:
|
|
65
|
+
poc watchlist
|
|
66
|
+
|
|
67
|
+
# Remove a package:
|
|
68
|
+
poc unwatch chalk
|
|
69
|
+
|
|
70
|
+
# API key: set COMMIT_API_KEY env or add api_key=<key> to ~/.commit/config
|
|
71
|
+
# Get a key at https://getcommit.dev/pricing
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Alerts fire on: score drop ≥10 points · package crosses CRITICAL threshold · recovery to HEALTHY.
|
|
75
|
+
|
|
54
76
|
**MCP server (zero install):**
|
|
55
77
|
|
|
56
78
|
```json
|
package/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* proof-of-commitment CLI v1.
|
|
3
|
+
* proof-of-commitment CLI v1.9.0
|
|
4
4
|
* Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
|
|
5
5
|
* Usage: npx proof-of-commitment [packages...] [options]
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const API = 'https://poc-backend.amdal-dev.workers.dev/api/audit';
|
|
9
|
+
const WATCHLIST_API = 'https://poc-backend.amdal-dev.workers.dev/api/watchlist';
|
|
9
10
|
const WEB = 'https://getcommit.dev/audit';
|
|
10
11
|
|
|
11
12
|
// ANSI color helpers
|
|
@@ -170,12 +171,18 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
170
171
|
const topPkgs = results.slice(0, 10).map(r => r.name).join(',');
|
|
171
172
|
console.log(clr(c.cyan, `\n 🔗 Full report: ${WEB}?packages=${encodeURIComponent(topPkgs)}`));
|
|
172
173
|
console.log(clr(c.cyan, ` 🤖 GitHub Action: github.com/piiiico/commit-action — block CRITICAL packages in CI`));
|
|
174
|
+
|
|
175
|
+
// Contextual upsell — show when findings make monitoring relevant
|
|
176
|
+
if (effectiveCritical > 0) {
|
|
177
|
+
console.log(clr(c.dim, `\n 📊 Track ${effectiveCritical === 1 ? 'this package' : 'these packages'} daily. Get alerted on score changes.`));
|
|
178
|
+
console.log(clr(c.dim, ` Commit Pro — batch API, monitoring, alerts → https://getcommit.dev/pricing`));
|
|
179
|
+
}
|
|
173
180
|
console.log();
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
function printHelp() {
|
|
177
184
|
console.log(`
|
|
178
|
-
${clr(c.bold, 'proof-of-commitment')} v1.
|
|
185
|
+
${clr(c.bold, 'proof-of-commitment')} v1.9.0 — supply chain risk scorer
|
|
179
186
|
|
|
180
187
|
${clr(c.bold, 'Usage:')}
|
|
181
188
|
npx proof-of-commitment Auto-detect manifest in current dir
|
|
@@ -192,6 +199,15 @@ ${clr(c.bold, 'Usage:')}
|
|
|
192
199
|
npx proof-of-commitment --file go.mod Audit Go direct + indirect deps
|
|
193
200
|
npx proof-of-commitment --file go.sum Audit Go full transitive set
|
|
194
201
|
|
|
202
|
+
${clr(c.bold, 'Commit Pro monitoring (poc watch):')}
|
|
203
|
+
poc watch <package> [--ecosystem npm|pypi|cargo|golang]
|
|
204
|
+
Add a package to daily monitoring (Pro tier)
|
|
205
|
+
poc watchlist List monitored packages with current scores + risk
|
|
206
|
+
poc unwatch <pkg> Remove a package from monitoring
|
|
207
|
+
|
|
208
|
+
API key: set COMMIT_API_KEY env or add api_key=<key> to ~/.commit/config
|
|
209
|
+
Get a key: https://getcommit.dev/pricing
|
|
210
|
+
|
|
195
211
|
${clr(c.bold, 'Options:')}
|
|
196
212
|
--json Output results as JSON
|
|
197
213
|
--fail-on=<level> Exit 1 when findings meet the threshold. Levels:
|
|
@@ -610,6 +626,183 @@ function shouldFail(results, failOn) {
|
|
|
610
626
|
return false;
|
|
611
627
|
}
|
|
612
628
|
|
|
629
|
+
/**
|
|
630
|
+
* Read API key from env or ~/.commit/config file.
|
|
631
|
+
* Returns the key string, or null if not found.
|
|
632
|
+
*/
|
|
633
|
+
async function readApiKey() {
|
|
634
|
+
if (process.env.COMMIT_API_KEY) return process.env.COMMIT_API_KEY.trim();
|
|
635
|
+
try {
|
|
636
|
+
const os = await import('os');
|
|
637
|
+
const fs = await import('fs');
|
|
638
|
+
const path = await import('path');
|
|
639
|
+
const configPath = path.join(os.homedir(), '.commit', 'config');
|
|
640
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
641
|
+
for (const line of content.split('\n')) {
|
|
642
|
+
const m = line.match(/^api_key\s*=\s*(.+)$/);
|
|
643
|
+
if (m) return m[1].trim();
|
|
644
|
+
}
|
|
645
|
+
} catch {}
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Handle 402 upgrade response from watchlist endpoints.
|
|
651
|
+
*/
|
|
652
|
+
function printUpgradeRequired() {
|
|
653
|
+
console.error(clr(c.yellow + c.bold, '\n ✦ Commit Pro required'));
|
|
654
|
+
console.error(clr(c.dim, ' Monitoring, daily scans, and alerts are Pro features.'));
|
|
655
|
+
console.error(clr(c.cyan, ' Upgrade at https://getcommit.dev/pricing\n'));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* poc watch <package> [--ecosystem npm|pypi|cargo|golang]
|
|
660
|
+
*/
|
|
661
|
+
async function cmdWatch(pkg, ecosystem) {
|
|
662
|
+
const key = await readApiKey();
|
|
663
|
+
if (!key) {
|
|
664
|
+
console.error(clr(c.red, 'No API key found. Set COMMIT_API_KEY or add api_key=<key> to ~/.commit/config'));
|
|
665
|
+
console.error(clr(c.dim, 'Get a key at https://getcommit.dev/pricing'));
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
process.stdout.write(clr(c.dim, `Adding ${pkg} (${ecosystem}) to watchlist...`));
|
|
670
|
+
const res = await fetch(WATCHLIST_API, {
|
|
671
|
+
method: 'POST',
|
|
672
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` },
|
|
673
|
+
body: JSON.stringify({ package: pkg, ecosystem }),
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
if (res.status === 402) { printUpgradeRequired(); process.exit(1); }
|
|
677
|
+
|
|
678
|
+
const data = await res.json();
|
|
679
|
+
if (!res.ok) {
|
|
680
|
+
console.error(`\n${clr(c.red, 'Error:')} ${data.message || JSON.stringify(data)}`);
|
|
681
|
+
process.exit(1);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const isNew = data.new_packages > 0;
|
|
685
|
+
process.stdout.write('\n');
|
|
686
|
+
if (isNew) {
|
|
687
|
+
console.log(clr(c.green, ` ✓ Now watching ${pkg}`));
|
|
688
|
+
console.log(clr(c.dim, ' Daily scan runs at 06:00 UTC. Alerts on score drop ≥10 or CRITICAL threshold.'));
|
|
689
|
+
} else {
|
|
690
|
+
console.log(clr(c.dim, ` ↩ ${pkg} is already in your watchlist`));
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* poc watchlist — show monitored packages table
|
|
696
|
+
*/
|
|
697
|
+
async function cmdWatchlist() {
|
|
698
|
+
const key = await readApiKey();
|
|
699
|
+
if (!key) {
|
|
700
|
+
console.error(clr(c.red, 'No API key found. Set COMMIT_API_KEY or add api_key=<key> to ~/.commit/config'));
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const res = await fetch(WATCHLIST_API, {
|
|
705
|
+
headers: { 'Authorization': `Bearer ${key}` },
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
if (res.status === 402) { printUpgradeRequired(); process.exit(1); }
|
|
709
|
+
|
|
710
|
+
const data = await res.json();
|
|
711
|
+
if (!res.ok) {
|
|
712
|
+
console.error(clr(c.red, `Error: ${data.message || JSON.stringify(data)}`));
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const pkgs = data.packages || [];
|
|
717
|
+
if (pkgs.length === 0) {
|
|
718
|
+
console.log(clr(c.dim, '\n No packages monitored yet.'));
|
|
719
|
+
console.log(clr(c.dim, ' Add one: poc watch <package>\n'));
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const COL = { name: 24, eco: 8, score: 7, prev: 7, risk: 14, scanned: 22 };
|
|
724
|
+
|
|
725
|
+
function riskLabelFromLevel(level) {
|
|
726
|
+
if (!level) return clr(c.dim, '—');
|
|
727
|
+
if (level === 'CRITICAL') return clr(c.red + c.bold, '🔴 CRITICAL');
|
|
728
|
+
if (level === 'HIGH') return clr(c.yellow + c.bold, '🟠 HIGH');
|
|
729
|
+
if (level === 'MODERATE') return clr(c.yellow, '🟡 MODERATE');
|
|
730
|
+
if (level === 'GOOD') return clr(c.yellow, '🟡 GOOD');
|
|
731
|
+
return clr(c.green, '🟢 HEALTHY');
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const header = [
|
|
735
|
+
padEnd(clr(c.bold, 'Package'), COL.name),
|
|
736
|
+
padEnd(clr(c.bold, 'Eco'), COL.eco),
|
|
737
|
+
padEnd(clr(c.bold, 'Score'), COL.score),
|
|
738
|
+
padEnd(clr(c.bold, 'Prev'), COL.prev),
|
|
739
|
+
padEnd(clr(c.bold, 'Risk'), COL.risk),
|
|
740
|
+
padEnd(clr(c.bold, 'Last scanned'), COL.scanned),
|
|
741
|
+
].join(' ');
|
|
742
|
+
|
|
743
|
+
const divWidth = COL.name + COL.eco + COL.score + COL.prev + COL.risk + COL.scanned + 10;
|
|
744
|
+
const divider = '─'.repeat(divWidth);
|
|
745
|
+
|
|
746
|
+
console.log('\n' + divider);
|
|
747
|
+
console.log(clr(c.dim, ` Commit Pro watchlist · ${pkgs.length}/${data.limit} packages · tier: ${data.tier}`));
|
|
748
|
+
console.log(divider);
|
|
749
|
+
console.log(header);
|
|
750
|
+
console.log(divider);
|
|
751
|
+
|
|
752
|
+
for (const pkg of pkgs) {
|
|
753
|
+
const scoreStr = pkg.current_score !== null ? String(pkg.current_score) : clr(c.dim, '—');
|
|
754
|
+
const prevStr = pkg.previous_score !== null ? String(pkg.previous_score) : clr(c.dim, '—');
|
|
755
|
+
const scanned = pkg.last_scanned_at ? pkg.last_scanned_at.replace('T', ' ').slice(0, 19) + ' UTC' : clr(c.dim, 'not yet');
|
|
756
|
+
|
|
757
|
+
const row = [
|
|
758
|
+
padEnd(pkg.name, COL.name),
|
|
759
|
+
padEnd(pkg.ecosystem, COL.eco),
|
|
760
|
+
padEnd(scoreStr, COL.score),
|
|
761
|
+
padEnd(prevStr, COL.prev),
|
|
762
|
+
padEnd(riskLabelFromLevel(pkg.risk_level), COL.risk),
|
|
763
|
+
padEnd(scanned, COL.scanned),
|
|
764
|
+
].join(' ');
|
|
765
|
+
console.log(row);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
console.log(divider);
|
|
769
|
+
console.log(clr(c.dim, '\n Alerts sent on: score drop ≥10 · CRITICAL threshold · recovery to HEALTHY'));
|
|
770
|
+
console.log(clr(c.cyan, ' Remove a package: poc unwatch <package>\n'));
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* poc unwatch <package> [--ecosystem npm|pypi|cargo|golang]
|
|
775
|
+
*/
|
|
776
|
+
async function cmdUnwatch(pkg, ecosystem) {
|
|
777
|
+
const key = await readApiKey();
|
|
778
|
+
if (!key) {
|
|
779
|
+
console.error(clr(c.red, 'No API key found. Set COMMIT_API_KEY or add api_key=<key> to ~/.commit/config'));
|
|
780
|
+
process.exit(1);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
process.stdout.write(clr(c.dim, `Removing ${pkg} (${ecosystem}) from watchlist...`));
|
|
784
|
+
const res = await fetch(WATCHLIST_API, {
|
|
785
|
+
method: 'DELETE',
|
|
786
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` },
|
|
787
|
+
body: JSON.stringify({ package: pkg, ecosystem }),
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
if (res.status === 402) { printUpgradeRequired(); process.exit(1); }
|
|
791
|
+
|
|
792
|
+
const data = await res.json();
|
|
793
|
+
if (!res.ok) {
|
|
794
|
+
console.error(`\n${clr(c.red, 'Error:')} ${data.message || JSON.stringify(data)}`);
|
|
795
|
+
process.exit(1);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
process.stdout.write('\n');
|
|
799
|
+
if ((data.removed ?? 0) > 0) {
|
|
800
|
+
console.log(clr(c.green, ` ✓ Removed ${pkg} from watchlist`));
|
|
801
|
+
} else {
|
|
802
|
+
console.log(clr(c.dim, ` ${pkg} was not in your watchlist`));
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
613
806
|
async function main() {
|
|
614
807
|
const args = process.argv.slice(2);
|
|
615
808
|
|
|
@@ -618,6 +811,42 @@ async function main() {
|
|
|
618
811
|
process.exit(0);
|
|
619
812
|
}
|
|
620
813
|
|
|
814
|
+
// Watchlist subcommands
|
|
815
|
+
const subcmd = args[0];
|
|
816
|
+
|
|
817
|
+
if (subcmd === 'watch') {
|
|
818
|
+
const pkg = args[1];
|
|
819
|
+
if (!pkg) { console.error('Usage: poc watch <package> [--ecosystem npm|pypi|cargo|golang]'); process.exit(1); }
|
|
820
|
+
let ecosystem = 'npm';
|
|
821
|
+
for (let i = 2; i < args.length; i++) {
|
|
822
|
+
if (args[i] === '--ecosystem' || args[i] === '-e') ecosystem = args[++i] || 'npm';
|
|
823
|
+
else if (args[i] === '--pypi') ecosystem = 'pypi';
|
|
824
|
+
else if (args[i] === '--cargo') ecosystem = 'cargo';
|
|
825
|
+
else if (args[i] === '--golang' || args[i] === '--go') ecosystem = 'golang';
|
|
826
|
+
}
|
|
827
|
+
await cmdWatch(pkg, ecosystem);
|
|
828
|
+
process.exit(0);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (subcmd === 'watchlist') {
|
|
832
|
+
await cmdWatchlist();
|
|
833
|
+
process.exit(0);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (subcmd === 'unwatch') {
|
|
837
|
+
const pkg = args[1];
|
|
838
|
+
if (!pkg) { console.error('Usage: poc unwatch <package> [--ecosystem npm|pypi|cargo|golang]'); process.exit(1); }
|
|
839
|
+
let ecosystem = 'npm';
|
|
840
|
+
for (let i = 2; i < args.length; i++) {
|
|
841
|
+
if (args[i] === '--ecosystem' || args[i] === '-e') ecosystem = args[++i] || 'npm';
|
|
842
|
+
else if (args[i] === '--pypi') ecosystem = 'pypi';
|
|
843
|
+
else if (args[i] === '--cargo') ecosystem = 'cargo';
|
|
844
|
+
else if (args[i] === '--golang' || args[i] === '--go') ecosystem = 'golang';
|
|
845
|
+
}
|
|
846
|
+
await cmdUnwatch(pkg, ecosystem);
|
|
847
|
+
process.exit(0);
|
|
848
|
+
}
|
|
849
|
+
|
|
621
850
|
let ecosystem = 'npm';
|
|
622
851
|
let packages = [];
|
|
623
852
|
let filePath = null;
|