@shubhambattoo/look-see 0.1.0 → 0.2.1

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 CHANGED
@@ -4,12 +4,19 @@ Look-See is a Node.js CLI that scans supported dependency lockfiles, identifies
4
4
 
5
5
  > Early MVP: npm lockfiles and Go `go.mod` requirements are supported. Automated dependency fixes are not implemented yet.
6
6
 
7
- ## Run
7
+ ## Usage
8
8
 
9
- Requires Node.js 22 or newer. Once published, run it with `npx` or install it globally:
9
+ Requires Node.js 22 or newer. Use the CLI with `npx` without a global install, or install it globally to use the `look-see` command directly.
10
+
11
+ ### Run with npx
10
12
 
11
13
  ```bash
12
14
  npx @shubhambattoo/look-see /path/to/project
15
+ ```
16
+
17
+ ### Install globally
18
+
19
+ ```bash
13
20
  npm install -g @shubhambattoo/look-see
14
21
  look-see /path/to/project
15
22
  ```
@@ -21,7 +28,7 @@ npm install
21
28
  npm start -- /path/to/project --no-open
22
29
  ```
23
30
 
24
- The CLI prints a LOOK SEE ASCII banner at scan startup, then displays colored stage/progress output in interactive terminals and plain progress lines in non-interactive runs. The default output directory is `<project>/.look-see/`:
31
+ The default output directory is `<project>/.look-see/`:
25
32
 
26
33
  - `look-see-output.html`
27
34
  - `look-see-output.json`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shubhambattoo/look-see",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Look-See scans locked project dependencies for known vulnerabilities",
5
5
  "author": "shubhambattoo",
6
6
  "type": "module",
@@ -4,6 +4,7 @@ const NVD_URL = 'https://services.nvd.nist.gov/rest/json/cves/2.0';
4
4
  const CACHE_TTL = 7 * 24 * 60 * 60 * 1000;
5
5
  const NVD_API_KEY = process.env.NVD_API_KEY;
6
6
  const MIN_INTERVAL_MS = NVD_API_KEY ? 700 : 6_100;
7
+ const MAX_CVES_PER_REQUEST = 100;
7
8
 
8
9
  function cveIdsFrom(vulnerabilities) {
9
10
  const ids = new Set();
@@ -16,15 +17,17 @@ function cveIdsFrom(vulnerabilities) {
16
17
  return [...ids].sort();
17
18
  }
18
19
 
19
- async function fetchCve(cveId) {
20
+ async function fetchCves(cveIds) {
20
21
  const url = new URL(NVD_URL);
21
- url.searchParams.set('cveId', cveId);
22
+ url.searchParams.set('cveIds', cveIds.join(','));
22
23
  const headers = { accept: 'application/json' };
23
24
  if (NVD_API_KEY) headers.apiKey = NVD_API_KEY;
24
25
  const response = await fetch(url, { headers, signal: AbortSignal.timeout(30_000) });
25
26
  if (!response.ok) throw new Error(`NVD returned HTTP ${response.status}`);
26
27
  const body = await response.json();
27
- return body.vulnerabilities?.[0]?.cve ?? null;
28
+ return new Map((body.vulnerabilities ?? [])
29
+ .map(({ cve }) => [cve?.id, cve])
30
+ .filter(([id]) => id));
28
31
  }
29
32
 
30
33
  export async function queryNvd(vulnerabilities, { onProgress } = {}) {
@@ -33,6 +36,7 @@ export async function queryNvd(vulnerabilities, { onProgress } = {}) {
33
36
  const errors = [];
34
37
  let lastRequestAt = 0;
35
38
  let completed = 0;
39
+ const missing = [];
36
40
  onProgress?.({ stage: 'nvd', completed, total: ids.length });
37
41
  for (const cveId of ids) {
38
42
  const cacheKey = `nvd:${cveId}`;
@@ -41,21 +45,29 @@ export async function queryNvd(vulnerabilities, { onProgress } = {}) {
41
45
  byCve.set(cveId, cached && Object.hasOwn(cached, 'cve') ? cached.cve : cached);
42
46
  completed += 1;
43
47
  onProgress?.({ stage: 'nvd', completed, total: ids.length });
44
- continue;
45
- }
48
+ } else missing.push(cveId);
49
+ }
50
+
51
+ for (let offset = 0; offset < missing.length; offset += MAX_CVES_PER_REQUEST) {
52
+ const batch = missing.slice(offset, offset + MAX_CVES_PER_REQUEST);
46
53
  const waitMs = Math.max(0, MIN_INTERVAL_MS - (Date.now() - lastRequestAt));
47
54
  if (waitMs) await new Promise((resolve) => setTimeout(resolve, waitMs));
48
55
  try {
49
- const cve = await fetchCve(cveId);
56
+ const cves = await fetchCves(batch);
50
57
  lastRequestAt = Date.now();
51
- byCve.set(cveId, cve);
52
- await setCached(cacheKey, { cve });
53
- completed += 1;
58
+ await Promise.all(batch.map(async (cveId) => {
59
+ const cve = cves.get(cveId) ?? null;
60
+ byCve.set(cveId, cve);
61
+ await setCached(`nvd:${cveId}`, { cve });
62
+ }));
63
+ completed += batch.length;
54
64
  onProgress?.({ stage: 'nvd', completed, total: ids.length });
55
65
  } catch (error) {
56
66
  lastRequestAt = Date.now();
57
- errors.push({ source: 'NVD', cveId, message: error.message });
58
- completed += 1;
67
+ for (const cveId of batch) {
68
+ errors.push({ source: 'NVD', cveId, message: error.message });
69
+ }
70
+ completed += batch.length;
59
71
  onProgress?.({ stage: 'nvd', completed, total: ids.length });
60
72
  if (/HTTP 403|HTTP 429/.test(error.message)) {
61
73
  errors.push({ source: 'NVD', message: 'Stopped further NVD lookups after a rate-limit response.' });
package/src/cli.js CHANGED
@@ -26,7 +26,7 @@ Environment:
26
26
  NVD_API_KEY Optional NVD API key for higher request limits
27
27
  LOOK_SEE_CACHE_DIR Override the local advisory cache directory`;
28
28
 
29
- const VERSION = '0.1.0';
29
+ const VERSION = '0.2.1';
30
30
  const SEVERITY_RANK = { UNKNOWN: 0, LOW: 1, MEDIUM: 2, HIGH: 3, CRITICAL: 4 };
31
31
 
32
32
  function parseArgs(args) {