is-my-node-vulnerable
Advanced tools
Comparing version 1.0.0-2 to 1.0.0-4
16
ascii.js
@@ -1,2 +0,2 @@ | ||
const clc = require('cli-color'); | ||
const clc = require('cli-color') | ||
@@ -11,3 +11,3 @@ const danger = ` | ||
`; | ||
` | ||
@@ -22,9 +22,9 @@ const allGood = ` | ||
`; | ||
` | ||
const bold = clc.bold; | ||
const bold = clc.bold | ||
const vulnerableWarning = bold(`The current Node.js version (${process.version}) is vulnerable to the following CVEs:`); | ||
const vulnerableWarning = bold(`The current Node.js version (${process.version}) is vulnerable to the following CVEs:`) | ||
const separator = '='.repeat(process.stdout.columns); | ||
const separator = '='.repeat(process.stdout.columns) | ||
@@ -36,3 +36,3 @@ module.exports = { | ||
vulnerableWarning, | ||
separator, | ||
}; | ||
separator | ||
} |
118
index.js
#!/usr/bin/env node | ||
const { request, stream, setGlobalDispatcher, Agent } = require('undici'); | ||
const EE = require('node:events'); | ||
const fs = require('node:fs'); | ||
const path = require('node:path'); | ||
const debug = require('debug')('is-my-node-vulnerable'); | ||
const satisfies = require('semver/functions/satisfies'); | ||
const { danger, vulnerableWarning, bold, separator, allGood } = require('./ascii'); | ||
const { request, stream, setGlobalDispatcher, Agent } = require('undici') | ||
const EE = require('node:events') | ||
const fs = require('node:fs') | ||
const path = require('node:path') | ||
const debug = require('debug')('is-my-node-vulnerable') | ||
const satisfies = require('semver/functions/satisfies') | ||
const { danger, vulnerableWarning, bold, separator, allGood } = require('./ascii') | ||
setGlobalDispatcher(new Agent({ connections: 20 })); | ||
setGlobalDispatcher(new Agent({ connections: 20 })) | ||
const CORE_RAW_URL = 'https://raw.githubusercontent.com/nodejs/security-wg/main/vuln/core/index.json' | ||
let lastETagValue; | ||
let lastETagValue | ||
const coreLocalFile = path.join(__dirname, 'core.json'); | ||
const ETagFile = path.join(__dirname, '.etag'); | ||
const coreLocalFile = path.join(__dirname, 'core.json') | ||
const ETagFile = path.join(__dirname, '.etag') | ||
async function readLocal(file) { | ||
return require(file); | ||
async function readLocal (file) { | ||
return require(file) | ||
} | ||
function loadETag() { | ||
function loadETag () { | ||
if (fs.existsSync(ETagFile)) { | ||
lastETagValue = fs.readFileSync(ETagFile).toString(); | ||
debug('Loading local ETag') | ||
lastETagValue = fs.readFileSync(ETagFile).toString() | ||
} | ||
} | ||
function updateLastETag(etag) { | ||
lastETagValue = etag; | ||
fs.writeFileSync(ETagFile, lastETagValue); | ||
function updateLastETag (etag) { | ||
lastETagValue = etag | ||
fs.writeFileSync(ETagFile, lastETagValue) | ||
} | ||
async function fetchCoreIndex() { | ||
const abortRequest = new EE(); | ||
async function fetchCoreIndex () { | ||
const abortRequest = new EE() | ||
await stream(CORE_RAW_URL, { signal: abortRequest }, ({ statusCode }) => { | ||
if (statusCode !== 200) { | ||
console.error('Request to Github failed. Aborting...'); | ||
abortRequest.emit('abort'); | ||
process.nextTick(() => { process.exit(1); }); | ||
console.error('Request to Github failed. Aborting...') | ||
abortRequest.emit('abort') | ||
process.nextTick(() => { process.exit(1) }) | ||
} | ||
return fs.createWriteStream(coreLocalFile); | ||
}); | ||
return readLocal(coreLocalFile); | ||
return fs.createWriteStream(coreLocalFile, { flags: 'w', autoClose: true }) | ||
}) | ||
return readLocal(coreLocalFile) | ||
} | ||
async function getCoreIndex() { | ||
const { headers } = await request(CORE_RAW_URL, { method: 'HEAD' }); | ||
if (!lastETagValue || lastETagValue !== headers['etag']) { | ||
updateLastETag(headers['etag']); | ||
debug('Creating local core.json'); | ||
return fetchCoreIndex(); | ||
async function getCoreIndex () { | ||
const { headers } = await request(CORE_RAW_URL, { method: 'HEAD' }) | ||
if (!lastETagValue || lastETagValue !== headers.etag || !fs.existsSync(coreLocalFile)) { | ||
updateLastETag(headers.etag) | ||
debug('Creating local core.json') | ||
return fetchCoreIndex() | ||
} else { | ||
debug('No updates from upstream. Getting a cached version.'); | ||
return readLocal(coreLocalFile); | ||
debug(`No updates from upstream. Getting a cached version: ${coreLocalFile}`) | ||
return readLocal(coreLocalFile) | ||
} | ||
} | ||
function getVulnerabilityList(data) { | ||
const list = []; | ||
function getVulnerabilityList (currentVersion, data) { | ||
const list = [] | ||
for (const key in data) { | ||
const vuln = data[key]; | ||
const vuln = data[key] | ||
if ( | ||
satisfies(process.version, vuln.vulnerable) && | ||
!satisfies(process.version, vuln.patched) | ||
satisfies(currentVersion, vuln.vulnerable) && | ||
!satisfies(currentVersion, vuln.patched) | ||
) { | ||
@@ -71,19 +72,36 @@ list.push(`${bold(vuln.cve)}: ${vuln.overview}\n${bold('Patched versions')}: ${vuln.patched}`) | ||
} | ||
return list; | ||
return list | ||
} | ||
async function main() { | ||
loadETag(); | ||
const coreIndex = await getCoreIndex(); | ||
const list = getVulnerabilityList(coreIndex); | ||
async function main (currentVersion) { | ||
const coreIndex = await getCoreIndex() | ||
const list = getVulnerabilityList(currentVersion, coreIndex) | ||
if (list.length) { | ||
console.error(danger); | ||
console.error(vulnerableWarning + '\n'); | ||
console.error(`${list.join(`\n${separator}\n\n`)}\n${separator}`); | ||
process.exit(1); | ||
console.error(danger) | ||
console.error(vulnerableWarning + '\n') | ||
console.error(`${list.join(`\n${separator}\n\n`)}\n${separator}`) | ||
process.exit(1) | ||
} else { | ||
console.info(allGood); | ||
console.info(allGood) | ||
} | ||
} | ||
main(); | ||
async function isNodeVulnerable (version) { | ||
const coreIndex = await getCoreIndex() | ||
const list = getVulnerabilityList(version, coreIndex) | ||
return list.length > 0 | ||
} | ||
if (process.argv[2] !== '-r') { | ||
loadETag() | ||
} | ||
// CLI | ||
if (require.main === module) { | ||
console.log('calling from main') | ||
main(process.version) | ||
} else { | ||
module.exports = { | ||
isNodeVulnerable | ||
} | ||
} |
{ | ||
"name": "is-my-node-vulnerable", | ||
"version": "1.0.0-2", | ||
"version": "1.0.0-4", | ||
"description": "package that checks if your Node.js installation is vulnerable to known security vulnerabilities", | ||
@@ -14,3 +14,4 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "npm run lint && node test.js", | ||
"lint": "standard" | ||
}, | ||
@@ -24,3 +25,6 @@ "author": "RafaelGSS <rafael.nunu@hotmail.com>", | ||
"undici": "^5.15.1" | ||
}, | ||
"devDependencies": { | ||
"standard": "^17.0.0" | ||
} | ||
} |
# is-my-node-vulnerable | ||
This package helps ensure the security of your Node.js installation by checking for known vulnerabilities. | ||
It compares the version of Node.js you have installed (`process.version`) to the [Node.js Security Database][] | ||
and alerts you if a vulnerability is found. | ||
## Usage | ||
``` | ||
npx is-my-node-vulnerable | ||
``` | ||
It's strongly recommended to include this as a step in the app CI. | ||
### Output - When vulnerable | ||
```console | ||
$ node -v | ||
v19.0.0 | ||
$ npx is-my-node-vulnerable | ||
██████ █████ ███ ██ ██████ ███████ ██████ | ||
██ ██ ██ ██ ████ ██ ██ ██ ██ ██ | ||
██ ██ ███████ ██ ██ ██ ██ ███ █████ ██████ | ||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
██████ ██ ██ ██ ████ ██████ ███████ ██ ██ | ||
The current Node.js version (v19.0.0) is vulnerable to the following CVEs: | ||
CVE-2022-43548: The Node.js rebinding protector for --inspect still allows invalid IP address, specifically, the octal format. | ||
Patched versions: ^14.21.1 || ^16.18.1 || ^18.12.1 || ^19.0.1 | ||
============================================================================================================================================= | ||
``` | ||
### Output - When non-vulnerable | ||
```console | ||
$ node -v | ||
v19.5.0 | ||
$ npx is-my-node-vulnerable | ||
█████ ██ ██ ██████ ██████ ██████ ██████ ██ | ||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
███████ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ | ||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
██ ██ ███████ ███████ ██████ ██████ ██████ ██████ ██ | ||
``` | ||
## API | ||
This package also exports a function `isNodeVulnerable` to perform the check in runtime | ||
```js | ||
const { isNodeVulnerable } = require('is-my-node-vulnerable') | ||
isNodeVulnerable('19.0.0') // true | ||
``` | ||
[Node.js Security Database]: https://github.com/nodejs/security-wg/tree/main/vuln |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
9854
7
125
2
65
1