muaddib-scanner 2.11.72 → 2.11.73
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/package.json
CHANGED
package/src/ioc/ghsa-poller.js
CHANGED
|
@@ -69,7 +69,7 @@ function _httpGetJson(pathName, { token, httpImpl = https, timeoutMs = 20_000 }
|
|
|
69
69
|
res.on('end', () => {
|
|
70
70
|
let json = null;
|
|
71
71
|
try { json = JSON.parse(body); } catch { /* leave null */ }
|
|
72
|
-
resolve({ status: res.statusCode, json });
|
|
72
|
+
resolve({ status: res.statusCode, json, headers: res.headers || {} });
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
req.on('timeout', () => { req.destroy(new Error('GHSA request timeout')); });
|
|
@@ -90,11 +90,43 @@ async function _defaultFetch(ecosystem, opts = {}) {
|
|
|
90
90
|
const p = `/advisories?type=malware&ecosystem=${encodeURIComponent(apiEco)}&per_page=100&sort=updated&direction=desc`;
|
|
91
91
|
const { status, json } = await _httpGetJson(p, { token, httpImpl: opts.httpImpl });
|
|
92
92
|
if (status !== 200 || !Array.isArray(json)) {
|
|
93
|
-
|
|
93
|
+
const hint = (status === 403 || status === 429) ? ' (rate-limited — set GITHUB_TOKEN in .env for 5000 req/hr)' : '';
|
|
94
|
+
throw new Error(`GHSA fetch ${ecosystem} failed: HTTP ${status}${hint}`);
|
|
94
95
|
}
|
|
95
96
|
return json;
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/** Extract the rel="next" advisories path from a GitHub Link header, or null. */
|
|
100
|
+
function _nextLink(linkHeader) {
|
|
101
|
+
if (!linkHeader) return null;
|
|
102
|
+
const m = /<https:\/\/api\.github\.com(\/advisories[^>]*)>;\s*rel="next"/.exec(linkHeader);
|
|
103
|
+
return m ? m[1] : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Fetch the FULL type=malware advisory list for one ecosystem by following Link
|
|
108
|
+
* pagination (capped at maxPages). Returns flattened, parsed rows (withdrawn included —
|
|
109
|
+
* the caller decides). Used by the Phase 5 coverage-audit to build the denominator;
|
|
110
|
+
* heavier than the incremental poll, so run it on demand, not every 15 min.
|
|
111
|
+
*/
|
|
112
|
+
async function fetchAllGhsaMalware(ecosystem, opts = {}) {
|
|
113
|
+
const token = opts.token || process.env.GITHUB_TOKEN || process.env.GH_TOKEN || null;
|
|
114
|
+
const maxPages = Number.isFinite(opts.maxPages) ? opts.maxPages : 30;
|
|
115
|
+
const apiEco = ecosystem === 'pypi' ? 'pip' : ecosystem;
|
|
116
|
+
let pathName = `/advisories?type=malware&ecosystem=${encodeURIComponent(apiEco)}&per_page=100&sort=published&direction=desc`;
|
|
117
|
+
const rows = [];
|
|
118
|
+
for (let page = 0; page < maxPages && pathName; page++) {
|
|
119
|
+
const { status, json, headers } = await _httpGetJson(pathName, { token, httpImpl: opts.httpImpl });
|
|
120
|
+
if (status !== 200 || !Array.isArray(json)) {
|
|
121
|
+
const hint = (status === 403 || status === 429) ? ' (rate-limited — set GITHUB_TOKEN in .env for 5000 req/hr)' : '';
|
|
122
|
+
throw new Error(`GHSA list ${ecosystem} failed: HTTP ${status}${hint}`);
|
|
123
|
+
}
|
|
124
|
+
for (const adv of json) rows.push(...parseAdvisory(adv, [ecosystem]));
|
|
125
|
+
pathName = _nextLink(headers && headers.link);
|
|
126
|
+
}
|
|
127
|
+
return rows;
|
|
128
|
+
}
|
|
129
|
+
|
|
98
130
|
// ── parsing (pure) ──
|
|
99
131
|
|
|
100
132
|
/**
|
|
@@ -327,6 +359,7 @@ function stopGhsaPoller() {
|
|
|
327
359
|
|
|
328
360
|
module.exports = {
|
|
329
361
|
parseAdvisory,
|
|
362
|
+
fetchAllGhsaMalware,
|
|
330
363
|
pollGhsaOnce,
|
|
331
364
|
loadGhsaCursor,
|
|
332
365
|
saveGhsaCursor,
|