Skip to content

Commit

Permalink
fix: update security advisory data endpoint
Browse files Browse the repository at this point in the history
The previous way of fetching https://npmjs.com/advisories/ URLs with
a `X-Spiferack: 1` header no longer returns JSON, most probably due
to such URLs now redirecting to the GitHub Advisory database. Switch
instead to use the endpoint:
`https://registry.npmjs.org/-/npm/v1/security/advisories/{id}`
  • Loading branch information
richardlau committed Nov 19, 2021
1 parent 5ba9066 commit fc82dd3
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 62 deletions.
34 changes: 13 additions & 21 deletions src/plugins/audit.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { differenceInDays } = require('date-fns');
const { differenceInDays, min } = require('date-fns');

const { fetch } = require('../lib/network');
const { getAuditInfo } = require('../lib/npm');
Expand All @@ -7,18 +7,12 @@ const { createError, createWarning } = require('../lib/result');

const ONE_MONTH = 30; // days

const isSourceVulnerability = (item) => item.url !== undefined;

const formatAuditEvents = (vulnerability) => {
const events = vulnerability.events.reduce((state, event) => {
state[event.type] = event.created;
return state;
}, {});
return events;
};
const isSourceVulnerability = (item) => item.source !== undefined;

const formatAudit = (v) => ({
name: v.name,
created: v.created,
updated: v.updated,
severity: v.severity,
via: v.via,
effects: v.effects,
Expand Down Expand Up @@ -53,12 +47,8 @@ const auditPlugin = async () => {
const auditPromises = audit.map((vulnerability) =>
(async () => {
// Getting extra advisory data from NPM
const data = await fetch(vulnerability.via.url, {
headers: { 'X-Spiferack': 1 }
});

const advisory = { cve: data.advisoryData.cves[0], events: data.events };

const data = await fetch(`https://registry.npmjs.org/-/npm/v1/security/advisories/${vulnerability.via.source}`);
const advisory = { cve: data.cves[0], created: data.created, updated: data.updated };
return {
...vulnerability,
...advisory
Expand All @@ -69,10 +59,8 @@ const auditPlugin = async () => {
const auditAsyncResult = await Promise.all(auditPromises);

const auditResult = auditAsyncResult.map((vulnerability) => {
const events = formatAuditEvents(vulnerability);
return {
...formatAudit(vulnerability),
events
...formatAudit(vulnerability)
};
});

Expand All @@ -85,7 +73,9 @@ const auditPlugin = async () => {
// Error if there is a high or above vulnerability that has not been fixed after 1 month.
const highRisk = auditResult.filter((v) => {
const now = new Date();
const publishDate = new Date(v.events.published || v.events.reported);
// In some cases, e.g. https://registry.npmjs.org/-/npm/v1/security/advisories/1002643
// the updated date is earlier than created. Pick the earlier one.
const publishDate = min([new Date(v.created), new Date(v.updated)]);
return (
(v.severity === 'high' || v.severity === 'critical') &&
differenceInDays(now, publishDate) > ONE_MONTH
Expand All @@ -107,7 +97,9 @@ const auditPlugin = async () => {
// Warn if there is a medium vulnerability that has not been fixed after 4 months. (report the number of these)
const mediumRisk = auditResult.filter((v) => {
const now = new Date();
const publishDate = new Date(v.events.published || v.events.reported);
// In some cases, e.g. https://registry.npmjs.org/-/npm/v1/security/advisories/1002643
// the updated date is earlier than created. Pick the earlier one.
const publishDate = min([new Date(v.created), new Date(v.updated)]);
return (
v.severity === 'moderate' &&
differenceInDays(now, publishDate) > ONE_MONTH * 4
Expand Down
55 changes: 14 additions & 41 deletions test/plugins/audit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,19 @@ it('should return an error if a critical vulnerability is active for more than 1
// mocking http request
network.fetch.mockImplementation(() => {
return Promise.resolve({
advisoryData: {
id: 1756,
cves: ['CVE-2021-25945']
},
events: [
{
id: 3241,
advisory_id: 1756,
created: '2021-06-08T23:17:06.692',
type: 'published'
}
]
id: 1756,
cves: ['CVE-2021-25945'],
created: '2021-06-08T23:17:06.692',
updated: '2021-06-08T23:17:06.692'
});
});

const result = await auditPlugin();

expect(result.length).toBe(1);
expect(result[0].type).toBe('error');
expect(network.fetch).toHaveBeenCalled();
expect(network.fetch).toHaveBeenCalledWith(
'https://npmjs.com/advisories/1756',
{ headers: { 'X-Spiferack': 1 } }
'https://registry.npmjs.org/-/npm/v1/security/advisories/1756'
);
expect(failure).toHaveBeenCalled();
});
Expand All @@ -103,18 +93,10 @@ it('should return a warning if a moderate vulnerability is active for more than
// mocking http request
network.fetch.mockImplementation(() => {
return Promise.resolve({
advisoryData: {
id: 1756,
cves: ['CVE-2021-25945']
},
events: [
{
id: 3241,
advisory_id: 1756,
created: '2021-02-08T23:17:06.692',
type: 'published'
}
]
id: 1756,
cves: ['CVE-2021-25945'],
created: '2021-06-08T23:16:33.057',
updated: '2021-02-08T23:17:06.691'
});
});

Expand All @@ -124,8 +106,7 @@ it('should return a warning if a moderate vulnerability is active for more than
expect(result[0].type).toBe('warning');
expect(network.fetch).toHaveBeenCalled();
expect(network.fetch).toHaveBeenCalledWith(
'https://npmjs.com/advisories/1756',
{ headers: { 'X-Spiferack': 1 } }
'https://registry.npmjs.org/-/npm/v1/security/advisories/1756'
);
expect(warning).toHaveBeenCalled();
});
Expand Down Expand Up @@ -161,18 +142,10 @@ it("should return a warning if they're more than 10 low risk vulnerabilities", a
*/
network.fetch.mockImplementation(() => {
return Promise.resolve({
advisoryData: {
id: 1756,
cves: ['CVE-2021-25945']
},
events: [
{
id: 3241,
advisory_id: 1756,
created: '2021-02-08T23:17:06.692',
type: 'published'
}
]
id: 1756,
cves: ['CVE-2021-25945'],
created: '2021-06-08T23:16:33.057',
updated: '2021-02-08T23:17:06.691'
});
});

Expand Down

0 comments on commit fc82dd3

Please sign in to comment.