npmguard-cli 0.1.0 → 0.2.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/dist/commands/check.js +46 -50
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.js +38 -27
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/commands/check.js
CHANGED
|
@@ -2,26 +2,36 @@ import chalk from "chalk";
|
|
|
2
2
|
import Table from "cli-table3";
|
|
3
3
|
import ora from "ora";
|
|
4
4
|
import { scanProject } from "../scanner.js";
|
|
5
|
+
const IPFS_GATEWAY = "https://gateway.pinata.cloud/ipfs";
|
|
5
6
|
function verdictLabel(verdict, score) {
|
|
6
7
|
switch (verdict) {
|
|
7
8
|
case "SAFE":
|
|
8
|
-
return chalk.green(
|
|
9
|
+
return chalk.green(`SAFE (${score})`);
|
|
9
10
|
case "WARNING":
|
|
10
|
-
return chalk.yellow(
|
|
11
|
+
return chalk.yellow(`WARNING (${score})`);
|
|
11
12
|
case "CRITICAL":
|
|
12
|
-
return chalk.red(
|
|
13
|
+
return chalk.red(`CRITICAL (${score})`);
|
|
13
14
|
default:
|
|
14
|
-
return chalk.gray("
|
|
15
|
+
return chalk.gray("UNKNOWN");
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
if (
|
|
19
|
-
return chalk.gray(
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
function capsLabel(caps) {
|
|
19
|
+
if (caps.length === 0)
|
|
20
|
+
return chalk.gray("none");
|
|
21
|
+
return caps
|
|
22
|
+
.map((c) => {
|
|
23
|
+
if (["process_spawn", "binary_download", "eval_usage", "obfuscated_code"].includes(c)) {
|
|
24
|
+
return chalk.red(c);
|
|
25
|
+
}
|
|
26
|
+
if (["filesystem"].includes(c)) {
|
|
27
|
+
return chalk.yellow(c);
|
|
28
|
+
}
|
|
29
|
+
return chalk.gray(c);
|
|
30
|
+
})
|
|
31
|
+
.join(", ");
|
|
22
32
|
}
|
|
23
|
-
function
|
|
24
|
-
return
|
|
33
|
+
function shortCid(cid) {
|
|
34
|
+
return cid.slice(0, 8) + "..." + cid.slice(-4);
|
|
25
35
|
}
|
|
26
36
|
export async function checkCommand(projectPath, auditSource) {
|
|
27
37
|
const spinner = ora("Scanning project dependencies...").start();
|
|
@@ -32,7 +42,8 @@ export async function checkCommand(projectPath, auditSource) {
|
|
|
32
42
|
chalk.bold("Package"),
|
|
33
43
|
chalk.bold("Installed"),
|
|
34
44
|
chalk.bold("Latest"),
|
|
35
|
-
chalk.bold("
|
|
45
|
+
chalk.bold("Verdict"),
|
|
46
|
+
chalk.bold("Capabilities"),
|
|
36
47
|
],
|
|
37
48
|
style: { head: [], border: [] },
|
|
38
49
|
});
|
|
@@ -40,60 +51,47 @@ export async function checkCommand(projectPath, auditSource) {
|
|
|
40
51
|
let warningCount = 0;
|
|
41
52
|
let criticalCount = 0;
|
|
42
53
|
let notAuditedCount = 0;
|
|
43
|
-
const
|
|
54
|
+
const links = [];
|
|
44
55
|
for (const dep of deps) {
|
|
45
56
|
const versionToCheck = dep.latest ?? dep.installed;
|
|
46
|
-
|
|
47
|
-
let verdictCol;
|
|
57
|
+
let audit;
|
|
48
58
|
if (!dep.hasUpdate) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
safeCount++;
|
|
54
|
-
else if (currentAudit.verdict === "WARNING") {
|
|
55
|
-
warningCount++;
|
|
56
|
-
details.push(reportLink(currentAudit));
|
|
57
|
-
}
|
|
58
|
-
else if (currentAudit.verdict === "CRITICAL") {
|
|
59
|
-
criticalCount++;
|
|
60
|
-
details.push(reportLink(currentAudit));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
verdictCol = chalk.gray("❓ NOT AUDITED");
|
|
65
|
-
notAuditedCount++;
|
|
66
|
-
details.push(npmLink(dep.name, dep.installed));
|
|
67
|
-
}
|
|
59
|
+
audit = await auditSource.getAudit(dep.name, dep.installed);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
audit = await auditSource.getAudit(dep.name, versionToCheck);
|
|
68
63
|
}
|
|
69
|
-
|
|
64
|
+
let verdictCol;
|
|
65
|
+
let capsCol;
|
|
66
|
+
if (audit) {
|
|
70
67
|
verdictCol = verdictLabel(audit.verdict, audit.score);
|
|
68
|
+
capsCol = capsLabel(audit.capabilities);
|
|
71
69
|
if (audit.verdict === "SAFE")
|
|
72
70
|
safeCount++;
|
|
73
|
-
else if (audit.verdict === "WARNING")
|
|
71
|
+
else if (audit.verdict === "WARNING")
|
|
74
72
|
warningCount++;
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
else if (audit.verdict === "CRITICAL") {
|
|
73
|
+
else if (audit.verdict === "CRITICAL")
|
|
78
74
|
criticalCount++;
|
|
79
|
-
|
|
75
|
+
if (audit.reportCid) {
|
|
76
|
+
links.push(` ${dep.name}@${versionToCheck} report: ${IPFS_GATEWAY}/${audit.reportCid}`);
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
else {
|
|
83
|
-
verdictCol = chalk.gray("
|
|
80
|
+
verdictCol = chalk.gray("NOT AUDITED");
|
|
81
|
+
capsCol = chalk.gray("-");
|
|
84
82
|
notAuditedCount++;
|
|
85
|
-
details.push(npmLink(dep.name, versionToCheck));
|
|
86
83
|
}
|
|
87
84
|
table.push([
|
|
88
85
|
dep.name,
|
|
89
86
|
dep.installed,
|
|
90
87
|
dep.latest ?? dep.installed,
|
|
91
88
|
verdictCol,
|
|
89
|
+
capsCol,
|
|
92
90
|
]);
|
|
93
91
|
}
|
|
94
92
|
spinner.stop();
|
|
95
93
|
console.log();
|
|
96
|
-
console.log(chalk.bold("
|
|
94
|
+
console.log(chalk.bold("NpmGuard Dependency Audit"));
|
|
97
95
|
console.log();
|
|
98
96
|
console.log(table.toString());
|
|
99
97
|
console.log();
|
|
@@ -107,18 +105,16 @@ export async function checkCommand(projectPath, auditSource) {
|
|
|
107
105
|
parts.push(chalk.red(`${criticalCount} critical`));
|
|
108
106
|
if (notAuditedCount > 0)
|
|
109
107
|
parts.push(chalk.gray(`${notAuditedCount} not audited`));
|
|
110
|
-
console.log(` ${parts.join("
|
|
111
|
-
|
|
112
|
-
if (details.length > 0) {
|
|
108
|
+
console.log(` ${parts.join(" | ")}`);
|
|
109
|
+
if (links.length > 0) {
|
|
113
110
|
console.log();
|
|
114
|
-
for (const
|
|
115
|
-
|
|
116
|
-
console.log(d);
|
|
111
|
+
for (const link of links) {
|
|
112
|
+
console.log(chalk.gray(link));
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
if (criticalCount > 0) {
|
|
120
116
|
console.log();
|
|
121
|
-
console.log(chalk.red.bold(`
|
|
117
|
+
console.log(chalk.red.bold(` ${criticalCount} package(s) flagged as CRITICAL — do not update without reviewing the report.`));
|
|
122
118
|
}
|
|
123
119
|
console.log();
|
|
124
120
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { AuditSource } from "../audit-source.js";
|
|
2
|
-
export declare function installCommand(packageSpec: string, auditSource: AuditSource): Promise<void>;
|
|
2
|
+
export declare function installCommand(packageSpec: string, auditSource: AuditSource, force?: boolean): Promise<void>;
|
package/dist/commands/install.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import ora from "ora";
|
|
3
3
|
import { execSync } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const IPFS_GATEWAY = "https://gateway.pinata.cloud/ipfs";
|
|
5
|
+
export async function installCommand(packageSpec, auditSource, force = false) {
|
|
6
6
|
const atIndex = packageSpec.lastIndexOf("@");
|
|
7
7
|
let packageName;
|
|
8
8
|
let requestedVersion = null;
|
|
@@ -14,7 +14,6 @@ export async function installCommand(packageSpec, auditSource) {
|
|
|
14
14
|
packageName = packageSpec;
|
|
15
15
|
}
|
|
16
16
|
const spinner = ora(`Checking audit for ${packageName}...`).start();
|
|
17
|
-
// Get latest version from npm if not specified
|
|
18
17
|
if (!requestedVersion) {
|
|
19
18
|
try {
|
|
20
19
|
const resp = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
@@ -24,7 +23,7 @@ export async function installCommand(packageSpec, auditSource) {
|
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
catch {
|
|
27
|
-
// continue
|
|
26
|
+
// continue
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
if (!requestedVersion) {
|
|
@@ -33,46 +32,58 @@ export async function installCommand(packageSpec, auditSource) {
|
|
|
33
32
|
}
|
|
34
33
|
const audit = await auditSource.getAudit(packageName, requestedVersion);
|
|
35
34
|
spinner.stop();
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(chalk.bold(` ${packageName}@${requestedVersion}`));
|
|
37
|
+
console.log();
|
|
36
38
|
if (!audit) {
|
|
39
|
+
console.log(chalk.gray(` NOT AUDITED — no NpmGuard record found for this version.`));
|
|
40
|
+
console.log(chalk.gray(` https://www.npmjs.com/package/${packageName}/v/${requestedVersion}`));
|
|
37
41
|
console.log();
|
|
38
|
-
console.log(chalk.gray(` ❓ ${packageName}@${requestedVersion} has not been audited by NpmGuard.`));
|
|
39
42
|
console.log(chalk.gray(" Proceeding with standard npm install..."));
|
|
40
43
|
console.log();
|
|
41
44
|
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
// Show verdict
|
|
48
|
+
if (audit.verdict === "SAFE") {
|
|
49
|
+
console.log(chalk.green(` SAFE (score: ${audit.score})`));
|
|
50
|
+
}
|
|
51
|
+
else if (audit.verdict === "WARNING") {
|
|
52
|
+
console.log(chalk.yellow(` WARNING (score: ${audit.score})`));
|
|
53
|
+
}
|
|
54
|
+
else if (audit.verdict === "CRITICAL") {
|
|
55
|
+
console.log(chalk.red(` CRITICAL (score: ${audit.score})`));
|
|
56
|
+
}
|
|
57
|
+
// Show capabilities
|
|
58
|
+
if (audit.capabilities.length > 0) {
|
|
59
|
+
console.log(chalk.gray(` Capabilities: ${audit.capabilities.join(", ")}`));
|
|
60
|
+
}
|
|
61
|
+
// Show IPFS links
|
|
62
|
+
if (audit.reportCid) {
|
|
63
|
+
console.log(chalk.gray(` Report: ${IPFS_GATEWAY}/${audit.reportCid}`));
|
|
64
|
+
}
|
|
65
|
+
if (audit.sourceCid) {
|
|
66
|
+
console.log(chalk.gray(` Source: ${IPFS_GATEWAY}/${audit.sourceCid}`));
|
|
67
|
+
}
|
|
46
68
|
console.log();
|
|
47
69
|
if (audit.verdict === "SAFE") {
|
|
48
|
-
console.log(chalk.green(` ✅ SAFE (score: ${audit.score})`));
|
|
49
|
-
if (audit.capabilities.length > 0) {
|
|
50
|
-
console.log(chalk.gray(` Capabilities: ${audit.capabilities.join(", ")}`));
|
|
51
|
-
}
|
|
52
|
-
console.log();
|
|
53
70
|
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
54
71
|
}
|
|
55
72
|
else if (audit.verdict === "WARNING") {
|
|
56
|
-
console.log(chalk.yellow(` ⚠️ WARNING (score: ${audit.score})`));
|
|
57
|
-
console.log(chalk.yellow(` Capabilities: ${audit.capabilities.join(", ")}`));
|
|
58
|
-
if (audit.reportCid) {
|
|
59
|
-
console.log(chalk.gray(` Report: https://gateway.pinata.cloud/ipfs/${audit.reportCid}`));
|
|
60
|
-
}
|
|
61
|
-
console.log();
|
|
62
73
|
console.log(chalk.yellow(" Installing with warning..."));
|
|
63
74
|
console.log();
|
|
64
75
|
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
65
76
|
}
|
|
66
77
|
else if (audit.verdict === "CRITICAL") {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
if (force) {
|
|
79
|
+
console.log(chalk.red(" --force: Installing despite CRITICAL verdict..."));
|
|
80
|
+
console.log();
|
|
81
|
+
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log(chalk.red.bold(" Installation blocked. This package has critical security issues."));
|
|
85
|
+
console.log(chalk.gray(" Use --force to install anyway."));
|
|
86
|
+
console.log();
|
|
71
87
|
}
|
|
72
|
-
console.log();
|
|
73
|
-
console.log(chalk.red.bold(" ⛔ Installation blocked. This package has critical security issues."));
|
|
74
|
-
console.log(chalk.gray(" Use --force to install anyway: npmguard install --force " +
|
|
75
|
-
packageSpec));
|
|
76
|
-
console.log();
|
|
77
88
|
}
|
|
78
89
|
}
|
package/dist/index.js
CHANGED