npmguard-cli 0.4.0 → 0.5.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/install.js +316 -7
- package/dist/contract.d.ts +121 -0
- package/dist/contract.js +92 -0
- package/dist/ens-source.js +18 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/package.json +4 -1
package/dist/commands/install.js
CHANGED
|
@@ -1,7 +1,189 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import ora from "ora";
|
|
3
|
+
import qrcode from "qrcode-terminal";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
3
5
|
import { execSync } from "node:child_process";
|
|
6
|
+
import { createPublicClient, createWalletClient, http, formatEther, encodeFunctionData, } from "viem";
|
|
7
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
8
|
+
import { defineChain } from "viem";
|
|
9
|
+
import { SignClient } from "@walletconnect/sign-client";
|
|
10
|
+
import { AUDIT_REQUEST_ADDRESS_0G, AUDIT_REQUEST_ABI, } from "../contract.js";
|
|
11
|
+
const ogGalileo = defineChain({
|
|
12
|
+
id: 16602,
|
|
13
|
+
name: "0G-Galileo-Testnet",
|
|
14
|
+
nativeCurrency: { name: "0G", symbol: "0G", decimals: 18 },
|
|
15
|
+
rpcUrls: { default: { http: ["https://evmrpc-testnet.0g.ai"] } },
|
|
16
|
+
blockExplorers: { default: { name: "0G Explorer", url: "https://chainscan-galileo.0g.ai" } },
|
|
17
|
+
testnet: true,
|
|
18
|
+
});
|
|
4
19
|
const IPFS_GATEWAY = "https://gateway.pinata.cloud/ipfs";
|
|
20
|
+
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
21
|
+
const DEFAULT_AUDIT_API_URL = "http://209.38.42.28:8000/audit";
|
|
22
|
+
const WALLETCONNECT_PROJECT_ID = process.env.WALLETCONNECT_PROJECT_ID ?? "d5eb170c427570e15ac00ae53acc93ba";
|
|
23
|
+
const OG_RPC = "https://evmrpc-testnet.0g.ai";
|
|
24
|
+
const BLOCK_EXPLORER = "https://chainscan-galileo.0g.ai";
|
|
25
|
+
function prompt(question) {
|
|
26
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
rl.question(question, (answer) => {
|
|
29
|
+
rl.close();
|
|
30
|
+
resolve(answer.trim().toLowerCase());
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function generateQrCode(text) {
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
qrcode.generate(text, { small: true }, (code) => {
|
|
37
|
+
console.log(code);
|
|
38
|
+
resolve();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async function askInstallWithoutAudit(packageSpec) {
|
|
43
|
+
const answer = await prompt(chalk.white(` Install from npm without audit? (y/n) `));
|
|
44
|
+
if (answer === "y" || answer === "yes") {
|
|
45
|
+
console.log();
|
|
46
|
+
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function requestAuditOnChain(packageName, version, privateKey) {
|
|
50
|
+
const account = privateKeyToAccount(privateKey);
|
|
51
|
+
const rpcUrl = OG_RPC;
|
|
52
|
+
const publicClient = createPublicClient({
|
|
53
|
+
chain: ogGalileo,
|
|
54
|
+
transport: http(rpcUrl),
|
|
55
|
+
});
|
|
56
|
+
const walletClient = createWalletClient({
|
|
57
|
+
account,
|
|
58
|
+
chain: ogGalileo,
|
|
59
|
+
transport: http(rpcUrl),
|
|
60
|
+
});
|
|
61
|
+
const auditFee = await publicClient.readContract({
|
|
62
|
+
address: AUDIT_REQUEST_ADDRESS_0G,
|
|
63
|
+
abi: AUDIT_REQUEST_ABI,
|
|
64
|
+
functionName: "auditFee",
|
|
65
|
+
});
|
|
66
|
+
const hash = await walletClient.writeContract({
|
|
67
|
+
address: AUDIT_REQUEST_ADDRESS_0G,
|
|
68
|
+
abi: AUDIT_REQUEST_ABI,
|
|
69
|
+
functionName: "requestAudit",
|
|
70
|
+
args: [packageName, version],
|
|
71
|
+
value: auditFee,
|
|
72
|
+
});
|
|
73
|
+
await publicClient.waitForTransactionReceipt({ hash });
|
|
74
|
+
return hash;
|
|
75
|
+
}
|
|
76
|
+
async function payViaWalletConnect(packageName, version, feeWei, feeDisplay) {
|
|
77
|
+
const calldata = encodeFunctionData({
|
|
78
|
+
abi: AUDIT_REQUEST_ABI,
|
|
79
|
+
functionName: "requestAudit",
|
|
80
|
+
args: [packageName, version],
|
|
81
|
+
});
|
|
82
|
+
let signClient = null;
|
|
83
|
+
// WalletConnect throws unhandled errors when MetaMask sends
|
|
84
|
+
// session events after the session is cleaned up — suppress them
|
|
85
|
+
const wcErrorHandler = (err) => {
|
|
86
|
+
if (err?.message?.includes("No matching key"))
|
|
87
|
+
return;
|
|
88
|
+
console.error(err);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
};
|
|
91
|
+
process.on("uncaughtException", wcErrorHandler);
|
|
92
|
+
try {
|
|
93
|
+
const initSpinner = ora(" Connecting to WalletConnect...").start();
|
|
94
|
+
signClient = await SignClient.init({
|
|
95
|
+
projectId: WALLETCONNECT_PROJECT_ID,
|
|
96
|
+
metadata: {
|
|
97
|
+
name: "NpmGuard",
|
|
98
|
+
description: "NPM package security audit",
|
|
99
|
+
url: "https://npmguard.dev",
|
|
100
|
+
icons: [],
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
initSpinner.stop();
|
|
104
|
+
const { uri, approval } = await signClient.connect({
|
|
105
|
+
requiredNamespaces: {
|
|
106
|
+
eip155: {
|
|
107
|
+
methods: ["eth_sendTransaction"],
|
|
108
|
+
chains: ["eip155:16602"],
|
|
109
|
+
events: ["chainChanged", "accountsChanged"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
if (!uri) {
|
|
114
|
+
console.log(chalk.red(" Failed to generate WalletConnect URI"));
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
console.log();
|
|
118
|
+
console.log(chalk.cyan(` Scan with your wallet to connect:`));
|
|
119
|
+
console.log();
|
|
120
|
+
await generateQrCode(uri);
|
|
121
|
+
console.log();
|
|
122
|
+
const pairSpinner = ora(" Waiting for wallet connection...").start();
|
|
123
|
+
const session = await approval();
|
|
124
|
+
// Find the 0G Galileo account in approved namespaces
|
|
125
|
+
const accounts = session.namespaces.eip155?.accounts ?? [];
|
|
126
|
+
const ogAccount = accounts.find((a) => a.startsWith("eip155:16602:"));
|
|
127
|
+
const account = ogAccount
|
|
128
|
+
? ogAccount.split(":")[2]
|
|
129
|
+
: accounts[0]?.split(":")[2];
|
|
130
|
+
if (!account) {
|
|
131
|
+
pairSpinner.fail("Wallet did not approve any accounts");
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
pairSpinner.succeed(`Connected: ${account.slice(0, 6)}...${account.slice(-4)}`);
|
|
135
|
+
console.log(chalk.cyan(` Confirm the ${feeDisplay} transaction in your wallet...`));
|
|
136
|
+
const txHash = await signClient.request({
|
|
137
|
+
topic: session.topic,
|
|
138
|
+
chainId: "eip155:16602",
|
|
139
|
+
request: {
|
|
140
|
+
method: "eth_sendTransaction",
|
|
141
|
+
params: [
|
|
142
|
+
{
|
|
143
|
+
from: account,
|
|
144
|
+
to: AUDIT_REQUEST_ADDRESS_0G,
|
|
145
|
+
data: calldata,
|
|
146
|
+
value: "0x" + feeWei.toString(16),
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
const confirmSpinner = ora(" Waiting for on-chain confirmation...").start();
|
|
152
|
+
const ogClient = createPublicClient({
|
|
153
|
+
chain: ogGalileo,
|
|
154
|
+
transport: http(OG_RPC),
|
|
155
|
+
});
|
|
156
|
+
const receipt = await ogClient.waitForTransactionReceipt({
|
|
157
|
+
hash: txHash,
|
|
158
|
+
});
|
|
159
|
+
if (receipt.status === "success") {
|
|
160
|
+
confirmSpinner.succeed("Payment confirmed on-chain!");
|
|
161
|
+
console.log(chalk.gray(` Tx: ${BLOCK_EXPLORER}/tx/${txHash}`));
|
|
162
|
+
console.log();
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
confirmSpinner.fail("Transaction reverted");
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
const msg = err.message ?? String(err);
|
|
172
|
+
if (msg.includes("rejected") || msg.includes("denied")) {
|
|
173
|
+
console.log(chalk.yellow(" Transaction rejected by user."));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
console.log(chalk.red(` WalletConnect error: ${msg}`));
|
|
177
|
+
}
|
|
178
|
+
console.log();
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
signClient = null;
|
|
183
|
+
// Remove handler after a delay to catch late WC events
|
|
184
|
+
setTimeout(() => process.off("uncaughtException", wcErrorHandler), 5000);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
5
187
|
export async function installCommand(packageSpec, auditSource, force = false) {
|
|
6
188
|
const atIndex = packageSpec.lastIndexOf("@");
|
|
7
189
|
let packageName;
|
|
@@ -35,16 +217,144 @@ export async function installCommand(packageSpec, auditSource, force = false) {
|
|
|
35
217
|
console.log();
|
|
36
218
|
console.log(chalk.bold(` ${packageName}@${requestedVersion}`));
|
|
37
219
|
console.log();
|
|
38
|
-
// No audit found
|
|
220
|
+
// ─── No audit found ───────────────────────────────────────────────
|
|
39
221
|
if (!audit) {
|
|
40
222
|
console.log(chalk.gray(` NOT AUDITED — no NpmGuard record found for this version.`));
|
|
41
223
|
console.log();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
224
|
+
const privateKey = process.env.NPMGUARD_PRIVATE_KEY;
|
|
225
|
+
const contractDeployed = AUDIT_REQUEST_ADDRESS_0G !== ZERO_ADDRESS;
|
|
226
|
+
if (contractDeployed) {
|
|
227
|
+
const publicClient = createPublicClient({
|
|
228
|
+
chain: ogGalileo,
|
|
229
|
+
transport: http(OG_RPC),
|
|
230
|
+
});
|
|
231
|
+
// Check if user already paid (previous attempt where audit engine failed)
|
|
232
|
+
let alreadyPaid = false;
|
|
233
|
+
try {
|
|
234
|
+
alreadyPaid = (await publicClient.readContract({
|
|
235
|
+
address: AUDIT_REQUEST_ADDRESS_0G,
|
|
236
|
+
abi: AUDIT_REQUEST_ABI,
|
|
237
|
+
functionName: "isRequested",
|
|
238
|
+
args: [packageName, requestedVersion],
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// can't read — assume not paid
|
|
243
|
+
}
|
|
244
|
+
if (alreadyPaid) {
|
|
245
|
+
console.log(chalk.cyan(` Already paid on-chain — re-triggering audit...`));
|
|
246
|
+
console.log();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// Read fee for display
|
|
250
|
+
let feeDisplay = "0.01 0G";
|
|
251
|
+
let feeWei = 10000000000000000n;
|
|
252
|
+
try {
|
|
253
|
+
feeWei = (await publicClient.readContract({
|
|
254
|
+
address: AUDIT_REQUEST_ADDRESS_0G,
|
|
255
|
+
abi: AUDIT_REQUEST_ABI,
|
|
256
|
+
functionName: "auditFee",
|
|
257
|
+
}));
|
|
258
|
+
feeDisplay = `${formatEther(feeWei)} 0G`;
|
|
259
|
+
}
|
|
260
|
+
catch { }
|
|
261
|
+
const wantAudit = await prompt(chalk.yellow(` Request on-chain audit for ${feeDisplay}? (y/n) `));
|
|
262
|
+
if (wantAudit !== "y" && wantAudit !== "yes") {
|
|
263
|
+
return askInstallWithoutAudit(packageSpec);
|
|
264
|
+
}
|
|
265
|
+
// Ask how to pay
|
|
266
|
+
console.log();
|
|
267
|
+
console.log(chalk.bold(` How to pay?`));
|
|
268
|
+
if (privateKey) {
|
|
269
|
+
console.log(` 1) Wallet (NPMGUARD_PRIVATE_KEY)`);
|
|
270
|
+
console.log(` 2) WalletConnect (mobile wallet)`);
|
|
271
|
+
console.log(` 3) Back`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
console.log(` 1) WalletConnect (mobile wallet)`);
|
|
275
|
+
console.log(` 2) Back`);
|
|
276
|
+
}
|
|
277
|
+
console.log();
|
|
278
|
+
const choice = await prompt(` Choice: `);
|
|
279
|
+
const backChoice = privateKey ? "3" : "2";
|
|
280
|
+
if (choice === backChoice) {
|
|
281
|
+
return askInstallWithoutAudit(packageSpec);
|
|
282
|
+
}
|
|
283
|
+
if (privateKey && choice === "1") {
|
|
284
|
+
const txSpinner = ora(" Sending payment transaction...").start();
|
|
285
|
+
try {
|
|
286
|
+
const txHash = await requestAuditOnChain(packageName, requestedVersion, privateKey);
|
|
287
|
+
txSpinner.succeed("Payment confirmed on-chain!");
|
|
288
|
+
console.log(chalk.gray(` Tx: ${BLOCK_EXPLORER}/tx/${txHash}`));
|
|
289
|
+
console.log();
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
txSpinner.fail("Transaction failed");
|
|
293
|
+
console.log(chalk.red(` ${err.shortMessage ?? err.message}`));
|
|
294
|
+
console.log();
|
|
295
|
+
return askInstallWithoutAudit(packageSpec);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else if ((privateKey && choice === "2") || (!privateKey && choice === "1")) {
|
|
299
|
+
const paid = await payViaWalletConnect(packageName, requestedVersion, feeWei, feeDisplay);
|
|
300
|
+
if (!paid)
|
|
301
|
+
return askInstallWithoutAudit(packageSpec);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
return askInstallWithoutAudit(packageSpec);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Trigger audit engine
|
|
308
|
+
const auditApiUrl = process.env.NPMGUARD_AUDIT_API_URL ?? DEFAULT_AUDIT_API_URL;
|
|
309
|
+
const auditSpinner = ora(" Running security audit...").start();
|
|
310
|
+
try {
|
|
311
|
+
const resp = await fetch(auditApiUrl, {
|
|
312
|
+
method: "POST",
|
|
313
|
+
headers: { "Content-Type": "application/json" },
|
|
314
|
+
body: JSON.stringify({ packageName, version: requestedVersion }),
|
|
315
|
+
});
|
|
316
|
+
if (!resp.ok)
|
|
317
|
+
throw new Error(`Audit engine returned ${resp.status}`);
|
|
318
|
+
const result = await resp.json();
|
|
319
|
+
auditSpinner.stop();
|
|
320
|
+
console.log();
|
|
321
|
+
const verdict = (result.verdict ?? "UNKNOWN").toUpperCase();
|
|
322
|
+
const capabilities = result.capabilities ?? [];
|
|
323
|
+
if (verdict === "SAFE") {
|
|
324
|
+
console.log(chalk.green(` Verdict: SAFE`));
|
|
325
|
+
}
|
|
326
|
+
else if (verdict === "DANGEROUS") {
|
|
327
|
+
console.log(chalk.red(` Verdict: DANGEROUS`));
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
console.log(chalk.yellow(` Verdict: ${verdict}`));
|
|
331
|
+
}
|
|
332
|
+
if (capabilities.length > 0) {
|
|
333
|
+
console.log(chalk.gray(` Capabilities: ${capabilities.join(", ")}`));
|
|
334
|
+
}
|
|
335
|
+
console.log();
|
|
336
|
+
if (verdict === "DANGEROUS" && !force) {
|
|
337
|
+
console.log(chalk.red.bold(" Installation blocked. This package is dangerous."));
|
|
338
|
+
console.log(chalk.gray(" Use --force to install anyway."));
|
|
339
|
+
console.log();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
console.log(chalk.gray(" Installing from npm..."));
|
|
343
|
+
console.log();
|
|
344
|
+
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
auditSpinner.fail("Audit engine unreachable");
|
|
348
|
+
console.log(chalk.gray(` ${err.message ?? err}`));
|
|
349
|
+
console.log();
|
|
350
|
+
return askInstallWithoutAudit(packageSpec);
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
// Contract not deployed
|
|
355
|
+
return askInstallWithoutAudit(packageSpec);
|
|
46
356
|
}
|
|
47
|
-
//
|
|
357
|
+
// ─── Audit found — show verdict ───────────────────────────────────
|
|
48
358
|
if (audit.verdict === "SAFE") {
|
|
49
359
|
console.log(chalk.green(` SAFE (score: ${audit.score})`));
|
|
50
360
|
}
|
|
@@ -82,7 +392,6 @@ export async function installCommand(packageSpec, auditSource, force = false) {
|
|
|
82
392
|
}
|
|
83
393
|
}
|
|
84
394
|
else {
|
|
85
|
-
// No sourceCid — install from npm
|
|
86
395
|
console.log(chalk.gray(" No IPFS source available, installing from npm..."));
|
|
87
396
|
console.log();
|
|
88
397
|
execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export declare const AUDIT_REQUEST_ADDRESS: `0x${string}`;
|
|
2
|
+
export declare const AUDIT_REQUEST_ADDRESS_0G: `0x${string}`;
|
|
3
|
+
export declare const AUDIT_REQUEST_ABI: readonly [{
|
|
4
|
+
readonly inputs: readonly [{
|
|
5
|
+
readonly name: "_auditFee";
|
|
6
|
+
readonly type: "uint256";
|
|
7
|
+
}];
|
|
8
|
+
readonly stateMutability: "nonpayable";
|
|
9
|
+
readonly type: "constructor";
|
|
10
|
+
}, {
|
|
11
|
+
readonly anonymous: false;
|
|
12
|
+
readonly inputs: readonly [{
|
|
13
|
+
readonly indexed: false;
|
|
14
|
+
readonly name: "packageName";
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
}, {
|
|
17
|
+
readonly indexed: false;
|
|
18
|
+
readonly name: "version";
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
}, {
|
|
21
|
+
readonly indexed: true;
|
|
22
|
+
readonly name: "requester";
|
|
23
|
+
readonly type: "address";
|
|
24
|
+
}];
|
|
25
|
+
readonly name: "AuditRequested";
|
|
26
|
+
readonly type: "event";
|
|
27
|
+
}, {
|
|
28
|
+
readonly anonymous: false;
|
|
29
|
+
readonly inputs: readonly [{
|
|
30
|
+
readonly indexed: true;
|
|
31
|
+
readonly name: "key";
|
|
32
|
+
readonly type: "bytes32";
|
|
33
|
+
}, {
|
|
34
|
+
readonly indexed: true;
|
|
35
|
+
readonly name: "requester";
|
|
36
|
+
readonly type: "address";
|
|
37
|
+
}];
|
|
38
|
+
readonly name: "AuditRequestedByKey";
|
|
39
|
+
readonly type: "event";
|
|
40
|
+
}, {
|
|
41
|
+
readonly inputs: readonly [{
|
|
42
|
+
readonly name: "packageName";
|
|
43
|
+
readonly type: "string";
|
|
44
|
+
}, {
|
|
45
|
+
readonly name: "version";
|
|
46
|
+
readonly type: "string";
|
|
47
|
+
}];
|
|
48
|
+
readonly name: "requestAudit";
|
|
49
|
+
readonly outputs: readonly [];
|
|
50
|
+
readonly stateMutability: "payable";
|
|
51
|
+
readonly type: "function";
|
|
52
|
+
}, {
|
|
53
|
+
readonly inputs: readonly [{
|
|
54
|
+
readonly name: "key";
|
|
55
|
+
readonly type: "bytes32";
|
|
56
|
+
}];
|
|
57
|
+
readonly name: "requestAuditByKey";
|
|
58
|
+
readonly outputs: readonly [];
|
|
59
|
+
readonly stateMutability: "payable";
|
|
60
|
+
readonly type: "function";
|
|
61
|
+
}, {
|
|
62
|
+
readonly inputs: readonly [];
|
|
63
|
+
readonly name: "auditFee";
|
|
64
|
+
readonly outputs: readonly [{
|
|
65
|
+
readonly name: "";
|
|
66
|
+
readonly type: "uint256";
|
|
67
|
+
}];
|
|
68
|
+
readonly stateMutability: "view";
|
|
69
|
+
readonly type: "function";
|
|
70
|
+
}, {
|
|
71
|
+
readonly inputs: readonly [];
|
|
72
|
+
readonly name: "owner";
|
|
73
|
+
readonly outputs: readonly [{
|
|
74
|
+
readonly name: "";
|
|
75
|
+
readonly type: "address";
|
|
76
|
+
}];
|
|
77
|
+
readonly stateMutability: "view";
|
|
78
|
+
readonly type: "function";
|
|
79
|
+
}, {
|
|
80
|
+
readonly inputs: readonly [{
|
|
81
|
+
readonly name: "packageName";
|
|
82
|
+
readonly type: "string";
|
|
83
|
+
}, {
|
|
84
|
+
readonly name: "version";
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
}];
|
|
87
|
+
readonly name: "isRequested";
|
|
88
|
+
readonly outputs: readonly [{
|
|
89
|
+
readonly name: "";
|
|
90
|
+
readonly type: "bool";
|
|
91
|
+
}];
|
|
92
|
+
readonly stateMutability: "view";
|
|
93
|
+
readonly type: "function";
|
|
94
|
+
}, {
|
|
95
|
+
readonly inputs: readonly [{
|
|
96
|
+
readonly name: "";
|
|
97
|
+
readonly type: "bytes32";
|
|
98
|
+
}];
|
|
99
|
+
readonly name: "requested";
|
|
100
|
+
readonly outputs: readonly [{
|
|
101
|
+
readonly name: "";
|
|
102
|
+
readonly type: "bool";
|
|
103
|
+
}];
|
|
104
|
+
readonly stateMutability: "view";
|
|
105
|
+
readonly type: "function";
|
|
106
|
+
}, {
|
|
107
|
+
readonly inputs: readonly [{
|
|
108
|
+
readonly name: "_fee";
|
|
109
|
+
readonly type: "uint256";
|
|
110
|
+
}];
|
|
111
|
+
readonly name: "setFee";
|
|
112
|
+
readonly outputs: readonly [];
|
|
113
|
+
readonly stateMutability: "nonpayable";
|
|
114
|
+
readonly type: "function";
|
|
115
|
+
}, {
|
|
116
|
+
readonly inputs: readonly [];
|
|
117
|
+
readonly name: "withdraw";
|
|
118
|
+
readonly outputs: readonly [];
|
|
119
|
+
readonly stateMutability: "nonpayable";
|
|
120
|
+
readonly type: "function";
|
|
121
|
+
}];
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// NpmGuardAuditRequest contract — deployed on Sepolia + 0G Galileo Testnet
|
|
2
|
+
// Update these addresses after running: cd contracts && npm run deploy
|
|
3
|
+
export const AUDIT_REQUEST_ADDRESS = "0x4bbaf196bde9e02594631e03c28ebe16719214f3"; // Sepolia
|
|
4
|
+
export const AUDIT_REQUEST_ADDRESS_0G = "0x1201448ae5f00e1783036439569e71ab3757d0de"; // 0G Galileo Testnet
|
|
5
|
+
export const AUDIT_REQUEST_ABI = [
|
|
6
|
+
{
|
|
7
|
+
inputs: [{ name: "_auditFee", type: "uint256" }],
|
|
8
|
+
stateMutability: "nonpayable",
|
|
9
|
+
type: "constructor",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
anonymous: false,
|
|
13
|
+
inputs: [
|
|
14
|
+
{ indexed: false, name: "packageName", type: "string" },
|
|
15
|
+
{ indexed: false, name: "version", type: "string" },
|
|
16
|
+
{ indexed: true, name: "requester", type: "address" },
|
|
17
|
+
],
|
|
18
|
+
name: "AuditRequested",
|
|
19
|
+
type: "event",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
anonymous: false,
|
|
23
|
+
inputs: [
|
|
24
|
+
{ indexed: true, name: "key", type: "bytes32" },
|
|
25
|
+
{ indexed: true, name: "requester", type: "address" },
|
|
26
|
+
],
|
|
27
|
+
name: "AuditRequestedByKey",
|
|
28
|
+
type: "event",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
inputs: [
|
|
32
|
+
{ name: "packageName", type: "string" },
|
|
33
|
+
{ name: "version", type: "string" },
|
|
34
|
+
],
|
|
35
|
+
name: "requestAudit",
|
|
36
|
+
outputs: [],
|
|
37
|
+
stateMutability: "payable",
|
|
38
|
+
type: "function",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
inputs: [{ name: "key", type: "bytes32" }],
|
|
42
|
+
name: "requestAuditByKey",
|
|
43
|
+
outputs: [],
|
|
44
|
+
stateMutability: "payable",
|
|
45
|
+
type: "function",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
inputs: [],
|
|
49
|
+
name: "auditFee",
|
|
50
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
51
|
+
stateMutability: "view",
|
|
52
|
+
type: "function",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
inputs: [],
|
|
56
|
+
name: "owner",
|
|
57
|
+
outputs: [{ name: "", type: "address" }],
|
|
58
|
+
stateMutability: "view",
|
|
59
|
+
type: "function",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
inputs: [
|
|
63
|
+
{ name: "packageName", type: "string" },
|
|
64
|
+
{ name: "version", type: "string" },
|
|
65
|
+
],
|
|
66
|
+
name: "isRequested",
|
|
67
|
+
outputs: [{ name: "", type: "bool" }],
|
|
68
|
+
stateMutability: "view",
|
|
69
|
+
type: "function",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
inputs: [{ name: "", type: "bytes32" }],
|
|
73
|
+
name: "requested",
|
|
74
|
+
outputs: [{ name: "", type: "bool" }],
|
|
75
|
+
stateMutability: "view",
|
|
76
|
+
type: "function",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
inputs: [{ name: "_fee", type: "uint256" }],
|
|
80
|
+
name: "setFee",
|
|
81
|
+
outputs: [],
|
|
82
|
+
stateMutability: "nonpayable",
|
|
83
|
+
type: "function",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
inputs: [],
|
|
87
|
+
name: "withdraw",
|
|
88
|
+
outputs: [],
|
|
89
|
+
stateMutability: "nonpayable",
|
|
90
|
+
type: "function",
|
|
91
|
+
},
|
|
92
|
+
];
|
package/dist/ens-source.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { createPublicClient, http } from "viem";
|
|
2
2
|
import { sepolia } from "viem/chains";
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const RPC_URLS = process.env.SEPOLIA_RPC_URL
|
|
4
|
+
? [process.env.SEPOLIA_RPC_URL]
|
|
5
|
+
: [
|
|
6
|
+
"https://ethereum-sepolia-rpc.publicnode.com",
|
|
7
|
+
"https://rpc.sepolia.org",
|
|
8
|
+
"https://sepolia.drpc.org",
|
|
9
|
+
];
|
|
10
|
+
function makeClient(url) {
|
|
11
|
+
return createPublicClient({ chain: sepolia, transport: http(url) });
|
|
12
|
+
}
|
|
7
13
|
async function getText(ensName, key) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
for (const url of RPC_URLS) {
|
|
15
|
+
try {
|
|
16
|
+
return await makeClient(url).getEnsText({ name: ensName, key });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
13
21
|
}
|
|
22
|
+
return null;
|
|
14
23
|
}
|
|
15
24
|
export class ENSAuditSource {
|
|
16
25
|
async getAudit(packageName, version) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import "dotenv/config";
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npmguard-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Check npm packages against NpmGuard security audits on ENS before installing",
|
|
6
6
|
"bin": "./dist/index.js",
|
|
@@ -22,10 +22,13 @@
|
|
|
22
22
|
],
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"@walletconnect/sign-client": "^2.23.9",
|
|
25
26
|
"chalk": "^5.4.0",
|
|
26
27
|
"cli-table3": "^0.6.5",
|
|
27
28
|
"commander": "^13.1.0",
|
|
29
|
+
"dotenv": "^17.4.0",
|
|
28
30
|
"ora": "^8.2.0",
|
|
31
|
+
"qrcode-terminal": "^0.12.0",
|
|
29
32
|
"viem": "^2.34.0"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|