├── .gitignore ├── lib ├── Commit.js ├── Releases.js ├── Vulnerability.js ├── ChangeLog.js ├── Release.js ├── ReleaseLines.js └── ReleaseLine.js ├── index.js ├── examples ├── v8v8.js ├── basic.js └── v10info.js ├── CONTRIBUTING.md ├── package.json ├── LICENSE.md ├── test ├── changelog.unit.js ├── releases.unit.js └── release.unit.js ├── data ├── schedule.json └── vulns.core.json ├── bin ├── isnodesafe.js └── nodetimeline.js ├── CODE_OF_CONDUCT.md ├── scripts ├── refreshData.js └── changelogParser.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /lib/Commit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class Commit { 4 | constructor (data) { 5 | ['sha', 'pr', 'author', 'reverts', 'desc'].forEach(key => { 6 | this[key] = data[key] 7 | }) 8 | this.topics = Array.from(data.topics) 9 | } 10 | 11 | get shaUrl () { 12 | return `https://github.com/nodejs/node/commit/${this.sha}` 13 | } 14 | 15 | get prUrl () { 16 | return `https://github.com/nodejs/node/pull/${this.pr}` 17 | } 18 | } 19 | 20 | module.exports = Commit 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ReleaseLines = require('./lib/ReleaseLines') 4 | const ReleaseLine = require('./lib/ReleaseLine') 5 | const Releases = require('./lib/Releases') 6 | const Release = require('./lib/Release') 7 | const Vulnerability = require('./lib/Vulnerability') 8 | const ChangeLog = require('./lib/ChangeLog') 9 | const Commit = require('./lib/Commit') 10 | 11 | module.exports = { 12 | ReleaseLines, 13 | ReleaseLine, 14 | Releases, 15 | Release, 16 | Vulnerability, 17 | ChangeLog, 18 | Commit 19 | } 20 | -------------------------------------------------------------------------------- /lib/Releases.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Release = require('./Release') 4 | const metadata = require('../data/releases.meta.json') 5 | 6 | class Releases extends Array { 7 | constructor (obj) { 8 | (Array.isArray(obj)) ? super(...obj) : super(obj) 9 | return this 10 | } 11 | 12 | getSafe () { 13 | return new Releases(this.filter(r => r.isSafe)) 14 | } 15 | 16 | static load (version) { 17 | return new Releases((metadata[version] || []).map(r => new Release(r))) 18 | } 19 | } 20 | 21 | module.exports = Releases 22 | -------------------------------------------------------------------------------- /examples/v8v8.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ReleaseLines } = require('../') 4 | const data = require('../data/schedule.json') 5 | 6 | let v8 = ReleaseLines.load(data, new Date('2018-09-15')).get('v8') 7 | let releases = v8.releases 8 | let safe = releases.getSafe() 9 | let unique = releases.reduce((p, c) => add(p, c.v8), {}) 10 | 11 | console.log(`v8: 12 | published releases ${releases.length} 13 | safe releases ${safe.length}`) 14 | console.log('v8 versions:\n', unique) 15 | 16 | function add (obj, key) { 17 | if (!obj[key]) obj[key] = 0 18 | obj[key]++ 19 | return obj 20 | } 21 | -------------------------------------------------------------------------------- /lib/Vulnerability.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const vulns = require('../data/vulns.core.json') // freeze? 4 | 5 | class Vulnerability { 6 | constructor (id) { 7 | this.id = id 8 | } 9 | 10 | get source () { 11 | return `https://github.com/nodejs/security-wg/tree/master/vuln/core/${this.id}.json` 12 | } 13 | 14 | get isValid () { 15 | return !!vulns[this.id] 16 | } 17 | } 18 | 19 | ;['cve', 'ref', 'vulnerable', 'patched', 'description', 20 | 'overview', 'author', 'cvss', 'cvss_score'].forEach(getter => { 21 | Vulnerability.prototype.__defineGetter__(getter, function () { 22 | return (vulns[this.id] || {})[getter] 23 | }) 24 | }) 25 | 26 | module.exports = Vulnerability 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ================================================================================ 3 | 4 | Awesome! We're happy that you want to contribute. 5 | 6 | Make sure that you're read and understand the [Code of Conduct](CODE_OF_CONDUCT.md). 7 | 8 | 9 | Building from source 10 | -------------------------------------------------------------------------------- 11 | 12 | There are no special instructions to build this project, other than to 13 | run `npm install` after cloning the repo, to get all the dependent packages 14 | installed. 15 | 16 | After making changes, you should run `npm test` to ensure all the tests 17 | currently pass. If you are adding or changing function, you should add tests 18 | in either an existing test module or a new one. 19 | 20 | Updating Data 21 | -------------------------------------------------------------------------------- 22 | 23 | Run `npm run refresh` -------------------------------------------------------------------------------- /lib/ChangeLog.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Commit = require('./Commit') 4 | const changelogCache = {} 5 | 6 | class ChangeLog { 7 | constructor (data) { 8 | this.version = data.version 9 | this.line = data.line 10 | this.date = data.date 11 | this.releasedBy = data.releasedBy 12 | this.text = data.text 13 | this.raw = data.raw 14 | 15 | this.commits = data.commits.map(c => new Commit(c)) 16 | } 17 | 18 | static load (version) { 19 | let line = version.split('.')[0] 20 | let changeData = changelogCache[line] 21 | 22 | if (typeof changeData === 'undefined') { 23 | try { 24 | changeData = changelogCache[line] = require(`../data/changelogs/${line}.json`) 25 | } catch (err) { 26 | changeData = changelogCache[line] = false 27 | } 28 | } 29 | if (!changeData || !changeData[version]) return null 30 | 31 | return new ChangeLog(changeData[version]) 32 | } 33 | } 34 | 35 | module.exports = ChangeLog 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-release-lines", 3 | "version": "1.3.4", 4 | "description": "Data api for node.js releases", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/nw/node-release-lines.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/nw/node-release-lines/issues" 12 | }, 13 | "bin": { 14 | "amisafe": "./bin/isnodesafe.js", 15 | "isnodesafe": "./bin/isnodesafe.js", 16 | "nodetimeline": "./bin/nodetimeline.js" 17 | }, 18 | "scripts": { 19 | "test": "mocha", 20 | "refresh": "node scripts/refreshData.js && node scripts/changelogParser.js" 21 | }, 22 | "author": "Nathan White ", 23 | "contributors": [ 24 | "Tierney Cyren " 25 | ], 26 | "license": "MIT", 27 | "files": [ 28 | "index.js", 29 | "data", 30 | "lib", 31 | "bin" 32 | ], 33 | "devDependencies": { 34 | "mocha": "^5.2.0", 35 | "semver": "^5.5.1", 36 | "split2": "^3.0.0", 37 | "standard": "^12.0.1" 38 | }, 39 | "dependencies": { 40 | "commander": "^2.18.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | const { ReleaseLines } = require('../') 2 | 3 | const releases = ReleaseLines.load('2018-09-15') // state of release lines at a point in time 4 | 5 | if (releases.getLTS().length === 4 && // LTS release lines 6 | releases.getEOL().length === 6 && // release lines that have reached EOL (end of life) 7 | releases.getEOL().getModern().length === 4 && // "modern" EOL release lines 8 | releases.getEOL().getLTS().length === 1) { // LTS release lines that are EOL 9 | // examine supported release lines 10 | 11 | releases.getSupported().forEach(line => { 12 | let stats = line.getStats() 13 | console.log({ 14 | version: line.version, 15 | daysToEOL: stats.days.until.eol, 16 | progress: `${stats.percent.total}%`, 17 | state: { 18 | lts: line.isLTS, 19 | isCurrent: line.isCurrent, 20 | isActive: line.isActive, 21 | isMaintenance: line.isMaintenance 22 | }, 23 | releases: { 24 | total: line.releases.length, 25 | safe: line.releases.getSafe().length, 26 | latest: line.releases[0].version 27 | } 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2018 Nathan White 5 | ----------------------------- 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /examples/v10info.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ReleaseLines } = require('../') 4 | const schedule = require('../data/schedule.json') 5 | 6 | let v10 = ReleaseLines.load(schedule, '2018-09-15').get('v10') 7 | let stats = v10.getStats() 8 | let safe = v10.releases.getSafe() 9 | 10 | let latest = safe[0] // latest "safe" (no vulns) release 11 | v10.setDate(latest.date) // calculate stats at time of release 12 | let s = v10.getStats() 13 | 14 | let data = [ 15 | `The ${v10.version} release line will become an active LTS in ${stats.days.until.lts} days. `, 16 | `In the ${stats.days.completed.total} days ${v10.version} has been actively developed `, 17 | `${v10.releases.length} releases have been published. ${safe.length} of those releases `, 18 | `currently have no vulnerabilities. ${v10.version} is ${stats.percent.total}% towards `, 19 | `EOL (End of Life), which happens in ${(stats.days.until.eol / 365.25).toFixed(1)} years.`, 20 | '\n\n', 21 | `The latest version ${latest.version} was released ${latest.date.toDateString()}, `, 22 | `${s.percent.total}% into the release lifecycle. ${latest.version} core component release versions `, 23 | `are npm: (${latest.npm}), v8: (${latest.v8}), uv: (${latest.uv}) and (${latest.modules}) modules.` 24 | ].join('') 25 | 26 | console.log(data) 27 | -------------------------------------------------------------------------------- /test/changelog.unit.js: -------------------------------------------------------------------------------- 1 | /* global describe it */ 2 | const { Release, ChangeLog } = require('../') 3 | const assert = require('assert') 4 | const controlDate = new Date('2018-09-01') 5 | 6 | describe('ChangeLog', function () { 7 | it('should have a changelog', function () { 8 | let release = Release.load('v10.11.0') 9 | let changelog = release.changelog 10 | 11 | assert.strictEqual(changelog instanceof ChangeLog, true, 'instance of ChangeLog') 12 | assert.strictEqual(changelog.version, 'v10.11.0', 'ChangeLog version') 13 | assert.strictEqual(changelog.line, 'Current', 'ChangeLog release line') 14 | assert.strictEqual(changelog.date, '2018-09-20', 'ChangeLog date') 15 | assert.strictEqual(changelog.releasedBy, '@targos', 'ChangeLog releasedBy') 16 | assert.strictEqual(changelog.text, '', 'ChangeLog header text') 17 | assert.strictEqual(changelog.raw.length > 10000, true, 'ChangeLog raw text') 18 | 19 | assert.strictEqual(changelog.commits.length, 106, 'number of commits') 20 | 21 | changelog.commits.forEach(commit => { 22 | assert.strictEqual(!!commit.shaUrl.match(/^https/), true, 'url sha') 23 | assert.strictEqual(!!commit.prUrl.match(/^https/), true, 'url pr') 24 | assert.strictEqual(commit.sha.length, 10, 'commit sha length') 25 | assert.strictEqual(commit.pr.length >= 4, true, 'commit pr length') 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /lib/Release.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Vulnerability = require('./Vulnerability') 4 | const ChangeLog = require('./ChangeLog') 5 | const metadata = require('../data/releases.meta.json') 6 | 7 | class Release { 8 | constructor (publication) { 9 | this.version = publication.version 10 | this.date = new Date(publication.date) 11 | this.modules = parseInt(publication.modules) 12 | 13 | ;['npm', 'v8', 'uv', 'zlib', 'openssl'].forEach(k => { 14 | this[k] = publication[k] 15 | }) 16 | 17 | this._vulnIds = Array.from(publication.vulns) 18 | } 19 | 20 | get vulns () { 21 | if (!this._vulns) this._vulns = this._vulnIds.map(v => { return new Vulnerability(v) }) 22 | return this._vulns 23 | } 24 | 25 | get changelog () { 26 | if (typeof this._changelog !== 'undefined') return this._changelog 27 | this._changelog = ChangeLog.load(this.version) 28 | return this._changelog 29 | } 30 | 31 | get docs () { 32 | return `https://nodejs.org/dist/${this.version}/docs/` 33 | } 34 | 35 | get isVulnerable () { 36 | return !!this._vulnIds.length 37 | } 38 | 39 | get isSafe () { 40 | return !this._vulnIds.length 41 | } 42 | 43 | download (file, type) { 44 | let dl = `https://nodejs.org/dist/${this.version}/` 45 | if (!file) return dl 46 | return dl 47 | } 48 | 49 | static load (version) { 50 | let line = version.split('.')[0] 51 | if (line.indexOf('v') === -1) { 52 | line = `v${line}` 53 | version = `v${version}` 54 | } 55 | 56 | let data = (metadata[line] || []).filter(v => v.version === version) 57 | 58 | if (!data[0]) return null 59 | return new Release(data[0]) 60 | } 61 | } 62 | 63 | module.exports = Release 64 | -------------------------------------------------------------------------------- /data/schedule.json: -------------------------------------------------------------------------------- 1 | { 2 | "v0.10": { 3 | "start": "2013-03-11", 4 | "end": "2016-10-31" 5 | }, 6 | "v0.12": { 7 | "start": "2015-02-06", 8 | "end": "2016-12-31" 9 | }, 10 | "v4": { 11 | "start": "2015-09-08", 12 | "lts": "2015-10-12", 13 | "maintenance": "2017-04-01", 14 | "end": "2018-04-30", 15 | "codename": "Argon" 16 | }, 17 | "v5": { 18 | "start": "2015-10-29", 19 | "maintenance": "2016-04-30", 20 | "end": "2016-06-30" 21 | }, 22 | "v6": { 23 | "start": "2016-04-26", 24 | "lts": "2016-10-18", 25 | "maintenance": "2018-04-30", 26 | "end": "2019-04-30", 27 | "codename": "Boron" 28 | }, 29 | "v7": { 30 | "start": "2016-10-25", 31 | "maintenance": "2017-04-30", 32 | "end": "2017-06-30" 33 | }, 34 | "v8": { 35 | "start": "2017-05-30", 36 | "lts": "2017-10-31", 37 | "maintenance": "2019-01-01", 38 | "end": "2019-12-31", 39 | "codename": "Carbon" 40 | }, 41 | "v9": { 42 | "start": "2017-10-01", 43 | "maintenance": "2018-04-01", 44 | "end": "2018-06-30" 45 | }, 46 | "v10": { 47 | "start": "2018-04-24", 48 | "lts": "2018-10-30", 49 | "maintenance": "2020-04-01", 50 | "end": "2021-04-01", 51 | "codename": "Dubnium" 52 | }, 53 | "v11": { 54 | "start": "2018-10-23", 55 | "maintenance": "2019-04-22", 56 | "end": "2019-06-01" 57 | }, 58 | "v12": { 59 | "start": "2019-04-23", 60 | "lts": "2019-10-22", 61 | "maintenance": "2021-04-01", 62 | "end": "2022-04-01", 63 | "codename": "" 64 | }, 65 | "v13": { 66 | "start": "2019-10-22", 67 | "maintenance": "2020-04-20", 68 | "end": "2020-06-01" 69 | }, 70 | "v14": { 71 | "start": "2020-04-21", 72 | "lts": "2020-10-20", 73 | "maintenance": "2022-04-01", 74 | "end": "2023-04-01", 75 | "codename": "" 76 | } 77 | } -------------------------------------------------------------------------------- /lib/ReleaseLines.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const https = require('https') 4 | const ReleaseLine = require('./ReleaseLine.js') 5 | const schedule = require('../data/schedule.json') 6 | 7 | class ReleaseLines extends Array { 8 | constructor (obj) { 9 | (Array.isArray(obj)) ? super(...obj) : super(obj) 10 | return this 11 | } 12 | 13 | setDate (date) { 14 | this.forEach(r => r.setDate(date)) 15 | return this 16 | } 17 | 18 | get (version, resetDate) { 19 | let found = this.filter(v => v.version === version)[0] 20 | if (!found) return 21 | 22 | return new ReleaseLine(found).setDate(resetDate || found._date) 23 | } 24 | 25 | static load (data, date) { 26 | if (!data || typeof data === 'string' || data instanceof Date) { 27 | date = data 28 | data = schedule 29 | } 30 | return new ReleaseLines( 31 | Object.keys(data) 32 | .map(k => new ReleaseLine( 33 | Object.assign({ version: k }, data[k]), date))) 34 | } 35 | 36 | static fetch (date) { 37 | let payload = '' 38 | return new Promise((resolve, reject) => { 39 | https.get(ReleaseLines.scheduleUrl, res => { 40 | res 41 | .on('error', err => reject(err)) 42 | .on('data', d => { payload += d.toString() }) 43 | .on('end', () => { 44 | try { 45 | resolve(ReleaseLines.load(JSON.parse(payload), date)) 46 | } catch (e) { 47 | reject(e) 48 | } 49 | }) 50 | }) 51 | }) 52 | } 53 | } 54 | 55 | ['Supported', 'Current', 'Maintenance', 'Future', 'Active', 'EOL', 'Modern', 'LTS'].forEach(type => { 56 | ReleaseLines.prototype[`get${type}`] = function (resetDate) { 57 | return new ReleaseLines(this.filter(r => r[`is${type}`]) 58 | .map(r => new ReleaseLine(r).setDate(resetDate || r._date)) 59 | ) 60 | } 61 | }) 62 | 63 | ReleaseLines.scheduleUrl = 'https://raw.githubusercontent.com/nodejs/Release/master/schedule.json' 64 | 65 | module.exports = ReleaseLines 66 | -------------------------------------------------------------------------------- /bin/isnodesafe.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var cli = require('commander') 4 | 5 | cli 6 | .version('1.0.0', '-v, --version') 7 | .usage('[options]') 8 | .description('CLI tool to check if Node.js is safe. Defaults to checking system version.') 9 | .option('-c, --ci', 'Returns a non-zero exit code if the version of Node.js is not safe, and a zero exit code if it is safe.') 10 | .option('-r, --release [release]', 'Checks to see if a specific release of Node.js is safe') 11 | .parse(process.argv) 12 | 13 | var version = (cli.release) ? cli.release : process.version 14 | 15 | // Handle EOL before we get into program logic 16 | handleEOLVersions() 17 | 18 | var api = require('../') 19 | var release = api.Release.load(version) 20 | 21 | handleInvalid(release) 22 | 23 | if (release.isSafe) { 24 | if (!cli.ci) { 25 | console.log('') 26 | console.log('✅ Node.js ' + version + ' is safe!') 27 | console.log('') 28 | } 29 | process.exit(0) 30 | } else { 31 | if (!cli.ci) { 32 | var releaseLine = release.version.split('.')[0] 33 | var safeReleases = api.Releases.load(releaseLine).getSafe() 34 | var safe = safeReleases[safeReleases.length - 1] 35 | 36 | console.log('') 37 | console.log('⚠️ Node.js ' + release.version + ' is not safe! You should upgrade now.') 38 | console.log('') 39 | if (safe) console.log('👉 Minimum safe Node.js version in the ' + releaseLine + ' release line: ' + safe.version) 40 | else console.log('☠️ No safe Node.js version in the ' + releaseLine + ' release line.') 41 | console.log('') 42 | } 43 | process.exit(1) 44 | } 45 | 46 | function handleInvalid (release) { 47 | if (!release) { 48 | console.error('Invalid Version') 49 | process.exit(1) 50 | } 51 | } 52 | 53 | function handleEOLVersions () { 54 | var releaseLine = parseInt(version.split('.')[0].replace('v', ''), 10) 55 | if (isNaN(releaseLine) || releaseLine < 6) { 56 | console.error('Node.js release line is EOL (End of Life) - please install a supported release line.') 57 | process.exit(1) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Code of Conduct 2 | ================================================================================ 3 | 4 | As contributors and maintainers of this project, and in the interest of 5 | fostering an open and welcoming community, we pledge to respect all people who 6 | contribute through reporting issues, posting feature requests, updating 7 | documentation, submitting pull requests or patches, and other activities. 8 | 9 | We are committed to making participation in this project a harassment-free 10 | experience for everyone, regardless of level of experience, gender, gender 11 | identity and expression, sexual orientation, disability, personal appearance, 12 | body size, race, ethnicity, age, religion, citizenship or national origin. 13 | 14 | Examples of unacceptable behavior by participants include: 15 | 16 | * The use of sexualized language or imagery 17 | * Personal attacks 18 | * Trolling or insulting/derogatory comments 19 | * Public or private harassment 20 | * Publishing other's private information, such as physical or electronic 21 | addresses, without explicit permission 22 | * Other unethical or unprofessional conduct. 23 | 24 | Project maintainers have the right and responsibility to remove, edit, or reject 25 | comments, commits, code, wiki edits, issues, and other contributions that are 26 | not aligned to this Code of Conduct. By adopting this Code of Conduct, project 27 | maintainers commit themselves to fairly and consistently applying these 28 | principles to every aspect of managing this project. Project maintainers who do 29 | not follow or enforce this Code of Conduct may be permanently removed from the 30 | project team. 31 | 32 | This Code of Conduct applies both within project spaces and outside of project spaces 33 | when an individual is representing the project or its community. 34 | 35 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 36 | reported by opening an issue or contacting one or more of the project 37 | maintainers. 38 | 39 | This Code of Conduct is adapted from the 40 | [Contributor Covenant](http://contributor-covenant.org), 41 | version 1.2.0, available at 42 | -------------------------------------------------------------------------------- /test/releases.unit.js: -------------------------------------------------------------------------------- 1 | /* global describe it */ 2 | const { ReleaseLines } = require('..') 3 | const assert = require('assert') 4 | const data = require('../data/schedule.json') 5 | const controlDate = new Date('2018-09-01') 6 | 7 | describe('ReleaseLines', function () { 8 | it('load data via static `load`', function () { 9 | let releases = ReleaseLines.load(data) 10 | assert(releases.length, 10, 'length of releases is incorrect') 11 | }) 12 | 13 | it('respect date', function () { 14 | let releases = ReleaseLines.load(data, controlDate) 15 | assert.strictEqual(releases.getSupported().length, 3, 'supported releases') 16 | assert.strictEqual(releases.getCurrent().length, 1, 'current releases') 17 | assert.strictEqual(releases.getMaintenance().length, 1, 'maintenance releases') 18 | assert.strictEqual(releases.getFuture().length, 1, 'future releases') 19 | assert.strictEqual(releases.getActive().length, 1, 'active releases') 20 | assert.strictEqual(releases.getEOL().length, 6, 'EOL releases') 21 | assert.strictEqual(releases.getModern().length, 8, 'modern releases') 22 | assert.strictEqual(releases.getLTS().length, 4, 'LTS releases') 23 | }) 24 | 25 | it('should have 2 current releases', function () { 26 | let releases = ReleaseLines.load(data, new Date('2017-10-15')) 27 | assert.strictEqual(releases.getCurrent().length, 2, 'current releases') 28 | }) 29 | 30 | it('should have 2 active lts releases', function () { 31 | let releases = ReleaseLines.load(data, new Date('2018-02-28')) 32 | assert.strictEqual(releases.getActive().length, 2, 'active releases') 33 | }) 34 | 35 | it('should have 4 supported releases', function () { 36 | let releases = ReleaseLines.load(data, new Date('2018-06-17')) 37 | assert.strictEqual(releases.getSupported().length, 4, 'supported releases') 38 | }) 39 | 40 | it('should support subquery filters on releases', function () { 41 | let releases = ReleaseLines.load(data, new Date('2018-10-31')) 42 | 43 | assert.strictEqual(releases.getActive().length, 2, 'active releases') 44 | assert.strictEqual(releases.getSupported().length, 4, 'supported releases') 45 | assert.strictEqual(releases.getSupported().getLTS().length, 3, 'supported LTS releases') 46 | // redundant 47 | // console.log(releases.getSupported().getLTS().getActive()) need to test resetDate 48 | assert.strictEqual(releases.getSupported().getLTS().getActive().length, 2, 'LTS releases') 49 | 50 | assert.strictEqual(releases.getEOL().length, 6, 'EOL releases') 51 | assert.strictEqual(releases.getModern().getEOL().length, 4, 'Modern EOL releases') 52 | }) 53 | 54 | it('should return create new `Release` instances on filters', function () { 55 | let releases = ReleaseLines.load(data, new Date('2017-01-01')) 56 | let old = releases.filter(r => r.version === 'v4')[0] 57 | let current = releases.get('v4').setDate() 58 | 59 | let oldStats = old.getStats() 60 | let newStats = current.getStats() 61 | 62 | assert.strictEqual(oldStats.days.completed.total, 481, 'days completed') 63 | assert.strictEqual(oldStats.days.completed.total < newStats.days.completed.total, true, 'completed days') 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /bin/nodetimeline.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var cli = require('commander') 4 | 5 | cli 6 | .version('1.0.0', '-v, --version') 7 | .usage('[options]') 8 | .description('CLI tool to understand Node.js release lines lifespans.') 9 | .option('-c, --current', 'Returns all "Current" versions of Node.js.') 10 | .option('-l, --lts', 'Returns all presently supported "LTS" versions of Node.js – regardless of whether or not they are presently active "LTS".') 11 | .option('-a, --active', 'Returns all active "LTS" Node.js release lines.') 12 | .option('-m, --maintenance', 'Returns all presently supported "Maintenance" versions of Node.js.') 13 | .option('-s, --supported', 'Returns all presently supported Node.js version') 14 | .parse(process.argv) 15 | 16 | var api = require('../') 17 | var releases = api.ReleaseLines.load() 18 | let v = process.version.split('.') 19 | 20 | var releasesType = [releases.get(v[0])] 21 | 22 | if (cli.current) { 23 | releasesType = releases.getCurrent() 24 | } else if (cli.lts) { 25 | releasesType = releases.getLTS() 26 | } else if (cli.active) { 27 | releasesType = releases.getActive() 28 | } else if (cli.maintenance) { 29 | releasesType = releases.getMaintenance() 30 | } else if (cli.supported) { 31 | releasesType = releases.getSupported() 32 | } else if (cli.modern) { 33 | releasesType = releases.getModern() 34 | } 35 | releasesType.forEach(release => { 36 | var icon 37 | if (release.isCurrent) { 38 | icon = '✅' 39 | } else if (release.isActive) { 40 | icon = '🆗' 41 | } else if (release.isMaintenance) { 42 | icon = '⚠️' 43 | } else if (release.isEOL) { 44 | icon = '☠️' 45 | } 46 | 47 | var stats = release.getStats() 48 | 49 | console.log(icon + ' Node.js ' + release.version + ' Timeline') 50 | console.log(' - Node.js ' + release.version + ' will be EOL in ' + stats.days.until.eol + ' days.') 51 | console.log(' - Node.js ' + release.version + ' is ' + stats.percent.total + '% through its total lifespan.') 52 | console.log('') 53 | console.log(' ℹ️ Release Line Information') 54 | if (release.isCurrent) { 55 | console.log(' - Node.js ' + release.version + ' is under active development!') 56 | } else if (release.isActive) { 57 | console.log(' - Node.js ' + release.version + ' is in the LTS phase of its lifecycle.') 58 | } else if (release.isMaintenance) { 59 | console.log(' - Node.js ' + release.version + ' is currently in Maintenance mode, and will only recieve security patches and bug fixes.') 60 | } else if (release.isEOL) { 61 | console.log(' - Node.js ' + release.version + ' is in the End of Life (EOL) stage of its lifecycle, meaning it will recieve no further updates, fixes, or patches.') 62 | } 63 | console.log('') 64 | console.log(' 📊 Release Line Metadata') 65 | console.log(' - There are a total of ' + release.releases.length + ' releases in the Node.js ' + release.version + ' release line.') 66 | console.log(' - There are currently ' + release.releases.getSafe().length + ' releases in the Node.js ' + release.version + ' release line that have zero known vulnerabilities.') 67 | console.log(' - The latest release in the ' + release.version + ' release line is ' + release.releases[0].version) 68 | console.log('') 69 | }) 70 | -------------------------------------------------------------------------------- /scripts/refreshData.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const url = require('url') 5 | const path = require('path') 6 | const https = require('https') 7 | const semver = require('semver') 8 | const { ReleaseLines } = require('../') 9 | 10 | let releaseDataUrl = 'https://nodejs.org/download/release/index.json' 11 | let vulnsUrl = addHeaders('https://api.github.com/repos/nodejs/security-wg/git/trees/master?recursive=1') 12 | let vulnItemUrl = 'https://raw.githubusercontent.com/nodejs/security-wg/master' 13 | 14 | let loader = async () => { 15 | // get schedule data 16 | let schedule = await fetch(ReleaseLines.scheduleUrl) 17 | // save schedule.json 18 | fs.writeFileSync(path.resolve(__dirname, '../data/schedule.json'), JSON.stringify(schedule, null, 2)) 19 | 20 | // filter for only modern release lines that have started 21 | // TODO this is for fetching CHANGELOGS 22 | // let modern = ReleaseLines.load(schedule) 23 | // .getModern() 24 | // .filter(v => !v.notStarted) 25 | // .map(v => v.version) 26 | 27 | // release details, group by release lines 28 | let releases = (await fetch(releaseDataUrl)).reduce((p, c) => { 29 | let version = c.version.match(/(v\d+)\./)[1] 30 | if (version === 'v0') return p // filter out old release lines 31 | if (!p[version]) p[version] = [] 32 | p[version].push(c) 33 | return p 34 | }, {}) 35 | 36 | // SECURITY DATA 37 | // get a list of the vuln files for core 38 | let securityTree = await fetch(vulnsUrl) 39 | 40 | if (securityTree.trucated) throw new Error('Truncated Security Vulnerability Data') 41 | 42 | let promises = securityTree 43 | .tree 44 | .filter(obj => { 45 | if (obj.type === 'blob' && obj.path.indexOf('vuln/core') === 0) return true // filtering 46 | return false 47 | }) 48 | .map(async function (v) { 49 | let data = await fetch(addHeaders(`${vulnItemUrl}/${v.path}`)) // get each vuln 50 | data.id = path.basename(v.path, '.json') // add an id 51 | return data 52 | }) 53 | 54 | let vulns = (await Promise.all(promises)) // resolve promises 55 | .sort((a, b) => { 56 | return parseInt(a.id) - parseInt(b.id) // help preserve order 57 | }) 58 | .reduce((p, c) => { // use id to build dict 59 | p[c.id] = c 60 | return p 61 | }, {}) 62 | 63 | // save vulns.core.json 64 | fs.writeFileSync(path.resolve(__dirname, '../data/vulns.core.json'), JSON.stringify(vulns, null, 2)) 65 | 66 | // merge vulns into release details 67 | Object.keys(releases).forEach(releaseLine => { 68 | releases[releaseLine].forEach(r => { 69 | r.vulns = [] 70 | Object.keys(vulns).forEach(id => { 71 | if (semver.satisfies(r.version, vulns[id].vulnerable) && !semver.satisfies(r.version, vulns[id].patched)) { 72 | r.vulns.push(id) 73 | } 74 | }) 75 | }) 76 | }) 77 | 78 | // save releases.meta.json 79 | fs.writeFileSync(path.resolve(__dirname, '../data/releases.meta.json'), JSON.stringify(releases, null, 2)) 80 | } 81 | 82 | loader() 83 | 84 | function fetch (url) { 85 | let payload = '' 86 | return new Promise((resolve, reject) => { 87 | https.get(url, res => { 88 | res 89 | .on('error', err => reject(err)) 90 | .on('data', d => { payload += d.toString() }) 91 | .on('end', () => { 92 | try { 93 | resolve(JSON.parse(payload)) 94 | } catch (e) { 95 | reject(e) 96 | } 97 | }) 98 | }) 99 | }) 100 | } 101 | 102 | function addHeaders (urlStr) { 103 | let urlObj = new url.URL(urlStr) 104 | return { 105 | protocol: urlObj.protocol, 106 | hostname: urlObj.hostname, 107 | port: 443, 108 | method: 'GET', 109 | path: `${urlObj.pathname}${urlObj.search}`, 110 | headers: { 'User-Agent': 'NodeReleaseAPI' } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/ReleaseLine.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Releases = require('./Releases') 4 | const DAY = 1000 * 60 * 60 * 24 5 | 6 | class ReleaseLine { 7 | constructor (obj, date) { 8 | Object.keys(obj).forEach(k => { 9 | if (k.indexOf('_') === 0) return // prevent private key copy 10 | this[k] = (['start', 'lts', 'maintenance', 'end'].includes(k)) 11 | ? new Date(obj[k]) // use `getTime()`? 12 | : obj[k] 13 | }) 14 | 15 | this.setDate(date) 16 | } 17 | 18 | get releases () { 19 | if (!this._releases) this._releases = Releases.load(this.version) 20 | return this._releases 21 | } 22 | 23 | setDate (date) { 24 | this._date = (!date || typeof date === 'boolean') ? Date.now() : (date instanceof Date) ? date : new Date(date) 25 | this._current = Math.floor((this._date - this.start) / DAY) 26 | return this 27 | } 28 | 29 | getStats (precision) { 30 | let total = (this.end - this.start) / DAY 31 | let lts = (this.isLTS) ? ((this.maintenance || this.end) - this.lts) / DAY : 0 32 | let maintenance = (!this.maintenance) ? 0 : (this.end - this.maintenance) / DAY 33 | let current = total - lts - maintenance 34 | let daysIn = this._current 35 | 36 | let daysCompleted = { 37 | total: (this.isEOL) ? total : (this.notStarted) ? 0 : daysIn, 38 | current: (daysIn > current) ? current : (this.notStarted) ? 0 : daysIn, 39 | lts: (this.inLTS) ? daysIn - current : (this.isLTS && (this.isEOL || this.isMaintenance)) ? lts : 0, 40 | maintenance: (this.isMaintenance) ? daysIn - current - lts : (this.isEOL) ? maintenance : 0 41 | } 42 | 43 | let daysRemaining = { 44 | total: total - daysCompleted.total, 45 | current: current - daysCompleted.current, 46 | lts: lts - daysCompleted.lts, 47 | maintenance: maintenance - daysCompleted.maintenance 48 | } 49 | 50 | let percentComplete = { 51 | total: toPercent(total, daysCompleted.total, precision), 52 | current: toPercent(current, daysCompleted.current, precision), 53 | lts: toPercent(lts, daysCompleted.lts, precision), 54 | maintenance: toPercent(maintenance, daysCompleted.maintenance, precision) 55 | } 56 | 57 | let daysUntil = { 58 | start: (this.notStarted) ? Math.floor((this.start - this._date) / DAY) : 0, 59 | lts: (this.isLTS && this.lts > this._date) ? Math.floor((this.lts - this._date) / DAY) : 0, 60 | maintenance: ((this.isMaintenance || this.isEOL) && this.maintenance) ? 0 : Math.floor((this.maintenance - this._date) / DAY), 61 | eol: (this.isEOL) ? 0 : Math.floor((this.end - this._date) / DAY) 62 | } 63 | 64 | return { 65 | days: { 66 | total: total, 67 | current: current, 68 | lts: lts, 69 | maintenance: maintenance, 70 | completed: daysCompleted, 71 | remaining: daysRemaining, 72 | until: daysUntil 73 | }, 74 | percent: percentComplete 75 | } 76 | } 77 | 78 | get isEOL () { 79 | if (!this.isSupported && this.end < this._date) return true 80 | return false 81 | } 82 | 83 | get notStarted () { 84 | if (!this.isSupported && this.start > this._date) return true 85 | return false 86 | } 87 | 88 | get isSupported () { 89 | if (this.start <= this._date && this.end >= this._date) return true 90 | return false 91 | } 92 | 93 | get isLTS () { 94 | return !!this.lts 95 | } 96 | 97 | get inLTS () { 98 | if (!this.isEOL && !this.isMaintenance && this.isLTS && this.lts <= this._date) return true 99 | return false 100 | } 101 | 102 | get isMaintenance () { 103 | if (!this.maintenance || this.isEOL) return false 104 | if (this.maintenance <= this._date) return true 105 | return false 106 | } 107 | 108 | get isActive () { 109 | return this.inLTS 110 | } 111 | 112 | get isFuture () { 113 | return this.notStarted 114 | } 115 | 116 | get isModern () { 117 | return !this.version.includes('.') 118 | } 119 | 120 | get isCurrent () { 121 | if (!this.isSupported) return false 122 | if (this.isMaintenance) return false 123 | if (this.inLTS) return false 124 | return true 125 | } 126 | } 127 | 128 | function toPercent (a, b, precision) { 129 | if (a === 0) return 0 130 | return parseFloat((100 / a * b).toFixed(precision)) 131 | } 132 | 133 | module.exports = ReleaseLine 134 | -------------------------------------------------------------------------------- /scripts/changelogParser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | const https = require('https') 6 | const split = require('split2') 7 | 8 | const changeLogUrl = 'https://raw.githubusercontent.com/nodejs/node/master/doc/changelogs/CHANGELOG_' 9 | 10 | const versions = ['V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10'] 11 | 12 | versions.forEach(v => { 13 | loader(`${changeLogUrl}${v}.md`, function (err, result) { 14 | if (err) console.log('ERROR: ', err) 15 | 16 | let filename = path.resolve(__dirname, `../data/changelogs/${v.toLowerCase()}.json`) 17 | fs.writeFileSync(filename, JSON.stringify(sanitize(result), null, 2)) 18 | }) 19 | }) 20 | 21 | // removes data that could be added / validated later 22 | function sanitize (result) { 23 | Object.keys(result).forEach(version => { 24 | let cl = result[version] 25 | 26 | result[version] = { 27 | version: cl.version, 28 | line: cl.line, 29 | lts: cl.lts, 30 | date: cl.date, 31 | releasedBy: cl.releasedBy, 32 | text: cl.text, 33 | raw: cl.raw, 34 | commits: cl.commits.items, 35 | deprecations: cl.deprecations.items 36 | } 37 | }) 38 | 39 | return result 40 | } 41 | 42 | function loader (changelogUrl, callback) { 43 | let data = {} 44 | let current = null 45 | let context = null 46 | let headers = ['changes', 'commits', 'deprecations', 'issues'] 47 | let regexs = headers.map(h => new RegExp(h, 'i')) 48 | 49 | https.get(changelogUrl, res => { 50 | res 51 | .pipe(split()) 52 | .on('error', callback) 53 | .on('data', line => { 54 | // addLine(line) 55 | 56 | if (isHeader(line)) return 57 | if (isBulletItem(line)) return 58 | if (ignoreAnchors(line)) return 59 | 60 | addLine(line) 61 | }) 62 | .on('end', () => { 63 | cleanup() 64 | callback(null, data) 65 | }) 66 | }) 67 | 68 | function ignoreAnchors (line) { 69 | if (line.match(/^<\/a>/)) return true 70 | return false 71 | } 72 | 73 | function addLine (line) { 74 | let version = data[current] 75 | if (!version) return 76 | if (!context) version.text.push(line) 77 | else version[context].text.push(line) 78 | version.raw.push(line) 79 | } 80 | 81 | function updateState (str) { 82 | context = str 83 | return true 84 | } 85 | 86 | function isBulletItem (line) { 87 | let match = line.match(/^\s*?(\*|-|\+)\s/) 88 | 89 | if (!match) return false 90 | 91 | normalizeBullet(line, match[0].length) 92 | return true 93 | } 94 | 95 | function isHeader (line) { 96 | let match = line.match(/^#+/) 97 | 98 | if (!match) return false 99 | 100 | let size = match[0].length 101 | // line = clean(line, size) 102 | 103 | switch (size) { 104 | case 1: break 105 | case 2: 106 | parseReleaseHeader(line, size) 107 | break 108 | default: 109 | normalizeHeader(line, size) 110 | } 111 | return true 112 | } 113 | 114 | function normalizeBullet (line, size) { 115 | if (!context || !data[current] || !data[current][context]) return // BAD STATE 116 | data[current].raw.push(line) 117 | line = clean(line, size) 118 | data[current][context].items.push((context === 'commits') ? parseCommit(line) : line) 119 | } 120 | 121 | function normalizeHeader (line, len) { 122 | if (data[current]) data[current].raw.push(line) 123 | if (len === 4 && line.match(/semver/i)) return // DEAL WITH THIS LATER 124 | 125 | if (!headers.some((h, i) => { 126 | if (line.match(regexs[i])) return updateState(h) 127 | return false 128 | })) normalizeBullet('* ' + clean(line, len), 2) // hacky Doesn't solve V8 6.0 header 129 | } 130 | 131 | function cleanup () { 132 | let version = data[current] 133 | if (!version) return 134 | 135 | version.raw = linesToString(version.raw) 136 | version.text = linesToString(version.text).trim() 137 | 138 | headers.forEach(h => { 139 | version[h].text = linesToString(version[h].text).trim() 140 | }) 141 | } 142 | 143 | function parseReleaseHeader (line, size) { 144 | let meta = line.split(',').map(v => v.trim()) 145 | let title = (meta[1] || '').match(/Version ([.0-9]+)( '(.+?)')? \((.+)\)$/) 146 | 147 | if (!title) return normalizeHeader(line, size + 1) 148 | 149 | if (current) cleanup() 150 | 151 | context = null 152 | current = `v${title[1]}` 153 | 154 | data[current] = { 155 | version: current, 156 | line: title[4], 157 | lts: title[3], 158 | date: meta[0].replace(/^#+/, '').trim(), 159 | releasedBy: meta[2], 160 | text: [], 161 | raw: [line] 162 | } 163 | 164 | headers.forEach(h => { 165 | data[current][h] = { items: [], text: [] } 166 | }) 167 | } 168 | } 169 | 170 | function parseCommit (line, semver) { 171 | let mdurl = /(\[?\[`?(.+?)`?\]\(.+?\)\]?)/g 172 | let sha = mdurl.exec(line) 173 | let pr = (sha) ? mdurl.exec(line) : null 174 | let splitPoint = line.indexOf(': ') 175 | semver = semver || 'PATCH' 176 | 177 | let meta = line.substring(sha ? sha[0].length : 0, (splitPoint === -1) ? undefined : splitPoint).trim() 178 | let desc = line.substring((splitPoint === -1) ? 0 : splitPoint + 2, pr ? pr.index : undefined).trim() 179 | // line = line.substring(sha ? sha[0].length : 0, pr ? pr.index : undefined) 180 | 181 | let semverCheck = meta.match(/\*\*\(SEMVER-([A-Z]+)\)\*\*/) 182 | if (semverCheck) { 183 | semver = semverCheck[1] 184 | meta = meta.substring(semverCheck[0].length + semverCheck.index) 185 | } 186 | 187 | let revertCheck = meta.match(/\*\*\*Revert\*\*\*/) 188 | if (revertCheck) { 189 | meta = meta.substring(revertCheck[0].length + revertCheck.index) 190 | } 191 | 192 | let topics = meta.match(/\*\*(.+?)\*\*/) 193 | if (topics) { 194 | topics = topics[1].split(',').map(t => t.trim()) 195 | } 196 | 197 | if (!pr) { 198 | pr = desc.match(/(\[(#[0-9]+?)\])$/) 199 | if (pr) desc = desc.substring(0, pr.index).trim() 200 | } 201 | 202 | let author = desc.match(/\(([^()]+?)\)$/) 203 | if (author) { 204 | desc = desc.substring(0, author.index).trim() 205 | author = author[1].trim() 206 | } 207 | 208 | return { 209 | sha: (sha) ? sha[2] : null, 210 | pr: (pr) ? pr[2].replace(/#/g, '') : null, 211 | author: author, 212 | topics: topics || [], 213 | reverts: !!revertCheck, 214 | desc: desc 215 | // semver: semver FIXME 216 | } 217 | } 218 | 219 | function linesToString (arr) { 220 | if (!Array.isArray(arr) || !arr.length) return '' 221 | return arr.join('\n') 222 | } 223 | 224 | function clean (str, len) { 225 | return str.substring(len).trim() 226 | // return str.replace(/^#+/, '').trim() 227 | } 228 | -------------------------------------------------------------------------------- /test/release.unit.js: -------------------------------------------------------------------------------- 1 | /* global describe it */ 2 | const { ReleaseLine } = require('../') 3 | const assert = require('assert') 4 | const data = require('../data/schedule.json') 5 | const controlDate = new Date('2018-09-01') 6 | 7 | // Helper function 8 | function getVersion (name, date) { 9 | return new ReleaseLine(Object.assign({ version: name }, data[name]), date || controlDate) 10 | } 11 | 12 | describe('ReleaseLine', function () { 13 | it('load properly', function () { 14 | let release = getVersion('v6') 15 | 16 | assert.strictEqual(release.version, 'v6', 'version') 17 | assert.strictEqual(release.codename, 'Boron', 'version') 18 | assert.strictEqual(release.isEOL, false, 'EOL') 19 | assert.strictEqual(release.notStarted, false, 'not started') 20 | assert.strictEqual(release.isSupported, true, 'supported') 21 | assert.strictEqual(release.isLTS, true, 'LTS') 22 | assert.strictEqual(release.inLTS, false, 'in LTS') 23 | assert.strictEqual(release.isMaintenance, true, 'maintenance') 24 | assert.strictEqual(release.isActive, false, 'active') 25 | assert.strictEqual(release.isFuture, false, 'future') 26 | assert.strictEqual(release.isModern, true, 'modern') 27 | assert.strictEqual(release.isCurrent, false, 'current') 28 | }) 29 | 30 | it('work with date changes', function () { 31 | let release = getVersion('v6') 32 | // change to active LTS date 33 | release.setDate(new Date('2018-01-01')) 34 | 35 | assert.strictEqual(release.inLTS, true, 'in LTS') 36 | assert.strictEqual(release.isMaintenance, false, 'maintenance') 37 | assert.strictEqual(release.isActive, true, 'active') 38 | // change to EOL date 39 | release.setDate(new Date('2019-06-01')) 40 | 41 | assert.strictEqual(release.isEOL, true, 'EOL') 42 | assert.strictEqual(release.notStarted, false, 'not started') 43 | assert.strictEqual(release.isSupported, false, 'supported') 44 | assert.strictEqual(release.inLTS, false, 'in LTS') 45 | assert.strictEqual(release.isMaintenance, false, 'maintenance') 46 | assert.strictEqual(release.isActive, false, 'active') 47 | }) 48 | 49 | it('provide accurate stats', function () { 50 | let release = getVersion('v11') 51 | let stats = release.getStats() 52 | 53 | assert.strictEqual(stats.days.total, 250, 'total supported period') 54 | assert.strictEqual(stats.days.completed.current, 0, 'completed current') 55 | assert.strictEqual(stats.days.maintenance, 90, 'maintenance days') 56 | assert.strictEqual(stats.days.until.start, 52, 'until it starts') 57 | assert.strictEqual(stats.percent.total, 0, 'percent complete') 58 | 59 | release.setDate(new Date('2018-10-31')) 60 | stats = release.getStats() 61 | 62 | assert.strictEqual(stats.days.completed.current, 8, 'completed current') 63 | assert.strictEqual(stats.days.until.start, 0, 'until it starts') 64 | assert.strictEqual(stats.percent.total, 3, 'percent complete') 65 | }) 66 | 67 | it('handle stats for releases with no maintenance', function () { 68 | let release = getVersion('v0.10', new Date('2013-01-01')) 69 | let stats = release.getStats() 70 | 71 | assert.strictEqual(stats.days.maintenance, 0, 'maintenance days') 72 | assert.strictEqual(stats.days.until.start, 69, 'till start') 73 | assert.strictEqual(stats.days.until.eol, 1399, 'until eol') 74 | 75 | release.setDate(new Date('2013-09-01')) 76 | stats = release.getStats() 77 | 78 | assert.strictEqual(stats.days.maintenance, 0, 'maintenance days') 79 | assert.strictEqual(stats.days.until.start, 0, 'till start') 80 | assert.strictEqual(stats.days.until.eol, 1156, 'until eol') 81 | 82 | assert.strictEqual(stats.days.completed.current, 174, 'completed current') 83 | assert.strictEqual(stats.percent.total, 13, 'percent complete') 84 | 85 | release.setDate(controlDate) 86 | stats = release.getStats() 87 | 88 | assert.strictEqual(release.isEOL, true, 'eol') 89 | assert.strictEqual(stats.days.completed.maintenance, 0, 'maintenance completed') 90 | assert.strictEqual(stats.percent.total, 100, 'percent complete') 91 | assert.strictEqual(stats.percent.maintenance, 0, 'maintenance percent complete') 92 | }) 93 | 94 | it('handle stats for non lts release lines', function () { 95 | let release = getVersion('v9', new Date('2018-06-17')) 96 | let stats = release.getStats() 97 | 98 | assert.strictEqual(release.isLTS, false, 'LTS') 99 | assert.strictEqual(stats.days.lts, 0, 'total lts days') 100 | assert.strictEqual(stats.days.remaining.lts, 0, 'remaining lts days') 101 | assert.strictEqual(stats.days.until.eol, 13, 'days till EOL') 102 | assert.strictEqual(release.isEOL, false, 'isEOL') 103 | assert.strictEqual(stats.percent.total, 95, 'percent complete') 104 | assert.strictEqual(stats.percent.lts, 0, 'lts percent complete') 105 | }) 106 | 107 | it('adjust stats precision', function () { 108 | let release = getVersion('v10') 109 | let stats = release.getStats() 110 | 111 | assert.strictEqual(release.isLTS, true, 'isLTS line') 112 | assert.strictEqual(release.inLTS, false, 'in LTS') 113 | assert.strictEqual(stats.days.lts, 548, 'total lts days') 114 | assert.strictEqual(stats.percent.total, 12, 'percent complete') 115 | assert.strictEqual(stats.percent.current, 81, 'current percent complete') 116 | 117 | stats = release.getStats(2) 118 | assert.strictEqual(stats.percent.total, 12.12, 'percent complete') 119 | assert.strictEqual(stats.percent.current, 81.25, 'current percent complete') 120 | }) 121 | 122 | it('should set unsupported values to null') 123 | it('should error on bad Date input') 124 | }) 125 | 126 | describe('Release', function () { 127 | it('should return published release details', function () { 128 | let release = getVersion('v8') 129 | let meta = release.releases 130 | let latest = meta[0] 131 | let oldest = meta[meta.length - 1] 132 | 133 | assert.strictEqual(release.version, 'v8', 'version release line') 134 | assert.strictEqual(meta.length, 27, 'number of published releases') 135 | // latest should be safe 136 | assert.strictEqual(latest.isSafe, true, 'vuln check') 137 | // 138 | assert.strictEqual(oldest.version, 'v8.0.0', 'pub version') 139 | assert.strictEqual(oldest.npm, '5.0.0', 'pub npm version') 140 | assert.strictEqual(oldest.v8, '5.8.283.41', 'pub v8 version') 141 | assert.strictEqual(oldest.uv, '1.11.0', 'pub uv version') 142 | assert.strictEqual(oldest.zlib, '1.2.11', 'pub zlib version') 143 | assert.strictEqual(oldest.openssl, '1.0.2k', 'pub openssl version') 144 | // 145 | assert.strictEqual(oldest.isSafe, false, 'vuln check') 146 | assert.strictEqual(oldest.vulns.length, 9, 'number of vulns') 147 | 148 | assert.strictEqual(meta.filter(m => m.isSafe).length, 3, 'safe versions') 149 | }) 150 | 151 | it('should provide details of vulns', function () { 152 | let release = getVersion('v8') 153 | let meta = release.releases 154 | let oldest = meta[meta.length - 1] 155 | let vuln = oldest.vulns[oldest.vulns.length - 1] 156 | 157 | assert.strictEqual(vuln.cve.length, 1, 'CVEs') 158 | assert.strictEqual(vuln.cve[0], 'CVE-2018-7167', 'cve') 159 | assert.strictEqual(vuln.vulnerable, '^6.0.0 || ^8.0.0 || ^9.0.0', 'vulnerable versions') 160 | assert.strictEqual(vuln.patched, '^6.14.3 || ^8.11.3 || ^9.11.2', 'patched') 161 | assert.strictEqual(!!vuln.ref, true, 'ref') 162 | assert.strictEqual(vuln.overview.length > 50, true, 'over') 163 | assert.strictEqual(!!vuln.source.match('github.com/nodejs/security-wg'), true, 'source url') 164 | }) 165 | }) 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Node Release Lines 3 | 4 | Introspection API for Node.js release metadata. Provides information about release lines, their relative status along with details of each release. 5 | 6 | See [how release lines work][] for more context. 7 | 8 | >Currently optimized for offline mode. Release schedule has apis to support both online & offline mode. 9 | 10 | ## Installation 11 | 12 | ``` bash 13 | npm install node-release-lines 14 | ``` 15 | 16 | ## Outline 17 | - [Command Line][] 18 | - [Terminology][] 19 | - [ReleaseLine][] _class_ 20 | - [ReleaseLines][] extends `Array` _class_ 21 | - [Release][] _class_ 22 | - [Releases][] extends `Array` _class_ 23 | - [Vulnerability][] _class_ 24 | - [ChangeLog][] _class_ 25 | - [Commit][] _class_ 26 | 27 | ## Usage 28 | 29 | ``` js 30 | const { ReleaseLines } = require('node-release-lines') 31 | 32 | const releases = ReleaseLines.load('2018-09-15') // state of release lines at a point in time 33 | 34 | if (releases.getLTS().length === 4 && // LTS release lines 35 | releases.getEOL().length === 6 && // release lines that have reached EOL (end of life) 36 | releases.getEOL().getModern().length === 4 && // "modern" EOL release lines 37 | releases.getEOL().getLTS().length === 1) { // LTS release lines that are EOL 38 | // examine supported release lines 39 | 40 | releases.getSupported().forEach(line => { 41 | let stats = line.getStats() 42 | console.log({ 43 | version: line.version, 44 | daysToEOL: stats.days.until.eol, 45 | progress: `${stats.percent.total}%`, 46 | state: { 47 | lts: line.isLTS, 48 | isCurrent: line.isCurrent, 49 | isActive: line.isActive, 50 | isMaintenance: line.isMaintenance 51 | }, 52 | releases: { 53 | total: line.releases.length, 54 | safe: line.releases.getSafe().length, 55 | latest: line.releases[0].version 56 | } 57 | }) 58 | }) 59 | } 60 | ``` 61 | 62 | ### Output 63 | 64 | ``` js 65 | { version: 'v6', 66 | daysToEOL: 198, 67 | progress: '81%', 68 | state: 69 | { lts: true, 70 | isCurrent: false, 71 | isActive: false, 72 | isMaintenance: true }, 73 | releases: { total: 39, safe: 1, latest: 'v6.14.3' } } 74 | { version: 'v8', 75 | daysToEOL: 472, 76 | progress: '50%', 77 | state: 78 | { lts: true, 79 | isCurrent: false, 80 | isActive: true, 81 | isMaintenance: false }, 82 | releases: { total: 26, safe: 2, latest: 'v8.11.4' } } 83 | { version: 'v10', 84 | daysToEOL: 929, 85 | progress: '13%', 86 | state: 87 | { lts: true, 88 | isCurrent: true, 89 | isActive: false, 90 | isMaintenance: false }, 91 | releases: { total: 12, safe: 6, latest: 'v10.9.0' } } 92 | ``` 93 | 94 | ## Command Line 95 | 96 | The node-release-lines CLI options can be used globally or via `npx`. 97 | 98 | Globally, where `` is the command you'd like to use: 99 | ``` 100 | npm install node-release-lines -g 101 | [options] 102 | ``` 103 | 104 | Via `npx`, where `` is the command you'd like to use: 105 | ``` 106 | npx node-release-lines [options] 107 | ``` 108 | 109 | ### Command: `isnodesafe [options]` 110 | 111 | `isnodesafe` is a CLI utility to check if Node.js is safe. The CLI defaults to checking system version, but can also check user-specified versions. 112 | 113 | #### `isnodesafe` Options 114 | 115 | * `-v, --version`: output the version number 116 | * `-c, --ci`: Returns a non-zero exit code if the version of Node.js is not safe, and a zero exit code if it is safe. 117 | * `-r, --release [release]` Checks to see if a specific release of Node.js is safe 118 | * `-h, --help` output usage information 119 | 120 | #### `isnodesafe` Examples 121 | ``` 122 | $ isnodesafe 123 | $ isnodesafe --ci 124 | $ isnodesafe --release 10.2.0 125 | $ isnodesafe -rc 6.9.5 126 | ``` 127 | 128 | ### Command: `amisafe` 129 | This command is deprecated in favor of [`isnodesafe`](#command-isnodesafe-options). It will work as an alias of `isnodesafe` until `node-release-lines@2.0.0`, at which point it will be removed. 130 | 131 | ### Command: `nodetimeline [options]` 132 | 133 | `nodetimeline` is a CLI tool to understand Node.js release lines lifespans. 134 | 135 | #### `nodetimeline` Options 136 | 137 | * `-v, --version`: output the version number 138 | * `-c, --current`: Returns all "Current" versions of Node.js. 139 | * `-l, --lts`: Returns all presently supported "LTS" versions of Node.js – regardless of whether or not they are presently active "LTS". 140 | * `-a, --active`: Returns all active "LTS" Node.js release lines. 141 | * `-m, --maintenance`: Returns all presently supported "Maintenance" versions of Node.js. 142 | * `-s, --supported`: Returns all presently supported Node.js version 143 | * `-h, --help`: output usage information 144 | 145 | #### `nodetimeline` Examples 146 | ``` 147 | $ nodetimeline 148 | $ nodetimeline -c 149 | $ nodetimeline --lts 150 | ``` 151 | 152 | # API 153 | 154 | 155 | ## Terminology 156 | 157 | - **EOL**: (end of life) - any `ReleaseLine` no longer supported and recieves no more updates. 158 | - **Supported**: any `ReleaseLine` that has been started and has not reached EOL. 159 | - **LTS**: any `ReleaseLine` that has an active LTS period in the lifecycle. 160 | - **Active**: any `ReleaseLine` that is in LTS, excluding maintenance window. 161 | - **Maintenance**: any `ReleaseLine` that is in maintenance mode and has not reached EOL. 162 | - **Current**: any `ReleaseLine` that has been started, in active development, not in maintenance or LTS. 163 | - **Future**: any defined `ReleaseLine` that has yet to start. 164 | - **Modern**: any `ReleaseLine` that is `v1` or greater. This does not include io.js releases (any version from `v4` onwards). 165 | 166 | 167 | ## `ReleaseLine` _class_ 168 | 169 | **Instance properties**: 170 | - **version**: `String` 171 | - **start**: `Date` 172 | - **end**: `Date` 173 | - **lts**: `Date` or `undefined` 174 | - **maintenance**: `Date` or `undefined` 175 | - **codename**: `String` or `undefined` 176 | 177 | **Instance getters**: 178 | 179 | _[see terminology]_ 180 | 181 | - **releases**: `Releases` _see [Releases][] section_ (lazy loaded) 182 | - **isEOL**: `Boolean` 183 | - **isSupported**: `Boolean` 184 | - **isLTS**: `Boolean` 185 | - **isActive**: `Boolean` 186 | - **inLTS**: `Boolean` alias for `isActive` 187 | - **isMaintenance**: `Boolean` 188 | - **isCurrent**: `Boolean` 189 | - **isFuture**: `Boolean` 190 | - **isModern**: `Boolean` 191 | - **notStarted**: `Boolean` 192 | 193 | ### `setDate(date)` 194 | 195 | Changes the `date` for calculating stats in `getStats` 196 | 197 | **Params**: 198 | - **date**: Date instance (optional, defaults=`Date.now`) 199 | 200 | **Returns**: `this` 201 | 202 | ### `getStats(precision)` 203 | 204 | **Params**: 205 | - **precision**: `Number` 206 | 207 | Stats about the relative timeline of the release based on the current `setDate`. 208 | 209 | **Notes**: 210 | - `0` will be used for unknown values. For example `maintenance`, `lts` are not valid for some release. 211 | - If a value is negative `0` is returned instead. This is useful for a `ReleaseLine` that hasn't started. 212 | - returned stats object is not bound to `setDate` 213 | 214 | **Returns**: 215 | ``` js 216 | { days: { 217 | total: 1073, // total days release is supported 218 | current: 160, // days in `current` mode 219 | lts: 548, // days in `active` LTS 220 | maintenance: 365, // days in maintenance 221 | completed: { 222 | total: 144, 223 | current: 144, 224 | lts: 0, 225 | maintenance: 0 }, 226 | remaining: { 227 | total: 929, 228 | current: 16, 229 | lts: 548, 230 | maintenance: 365 }, 231 | until: { 232 | start: 0, // already started 233 | lts: 16, 234 | maintenance: 564, 235 | eol: 929 } 236 | }, 237 | percent: { 238 | total: 13, // complete 239 | current: 90, 240 | lts: 0, 241 | maintenance: 0 } 242 | } 243 | ``` 244 | 245 | 246 | ## `ReleaseLines` extends `Array` _class_ 247 | 248 | An array of `ReleaseLine` instances. Provides helper methods for updating, filtering and querying release lines. 249 | 250 | ### `ReleaseLines.load(schedule, date)` _static_ 251 | 252 | Hydrates a schedule. If a schedule is not defined then the internal cached copy is automatically used. 253 | 254 | **Params**: 255 | - **schedule**: an object of release lines (optional) 256 | - **key**: version of the release 257 | - **value**: `Object` 258 | - **start**: `String` or `Date` (required) 259 | - **endt**: `String` or `Date` (required) 260 | - **lts**: `String` or `Date` 261 | - **maintenance**: `String` or `Date` 262 | - **codename**: `String` 263 | - **date**: Date instance (optional, defaults=`Date.now`) 264 | 265 | **Returns**: `ReleaseLines` instance 266 | 267 | ### `ReleaseLines.fetch(date)` _static_ 268 | 269 | **Params**: 270 | - **date**: Date instance (optional, defaults=`Date.now`) 271 | 272 | **Returns**: `Promise` - resolves to `ReleaseLines` instance 273 | 274 | ### `ReleaseLines.scheduleUrl` (string) _static_ 275 | 276 | The url to the offical [release schedule][]. 277 | 278 | ### `get(version, resetDate)` 279 | 280 | **Params**: 281 | - **version**: a release line name (example: `v10`) 282 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 283 | 284 | **Returns**: `ReleaseLine` or `undefined` 285 | 286 | ### `setDate(date)` 287 | 288 | **Params**: 289 | - **date**: Date instance (optional, defaults=`Date.now`) 290 | 291 | **Returns**: `this` 292 | 293 | ### `getSupported(resetDate)` 294 | 295 | Filters `ReleaseLine` items by **isSupported** 296 | 297 | **Params**: 298 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 299 | 300 | **Returns**: `ReleaseLines` instance with only supported release lines. 301 | 302 | ### `getCurrent(resetDate)` 303 | 304 | Filters `ReleaseLine` items by **isCurrent** 305 | 306 | **Params**: 307 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 308 | 309 | **Returns**: `ReleaseLines` instance with only current release lines. 310 | 311 | ### `getMaintenance(resetDate)` 312 | 313 | Filters `ReleaseLine` items by **isMaintenance** 314 | 315 | **Params**: 316 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 317 | 318 | **Returns**: `ReleaseLines` instance with only release lines in maintenance mode. 319 | 320 | ### `getFuture(resetDate)` 321 | 322 | Filters `ReleaseLine` items by **isFuture** 323 | 324 | **Params**: 325 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 326 | 327 | **Returns**: `ReleaseLines` instance with only release lines that have yet to start. 328 | 329 | ### `getActive(resetDate)` 330 | 331 | Filters `ReleaseLine` items by **isActive** 332 | 333 | **Params**: 334 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 335 | 336 | **Returns**: `ReleaseLines` instance with only release lines that are in LTS, excluding maintenance window. 337 | 338 | ### `getEOL(resetDate)` 339 | 340 | Filters `ReleaseLine` items by **isEOL** 341 | 342 | **Params**: 343 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 344 | 345 | **Returns**: `ReleaseLines` instance with only release lines that have hit EOL 346 | 347 | ### `getModern(resetDate)` 348 | 349 | Filters `ReleaseLine` items by **isModern** 350 | 351 | **Params**: 352 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 353 | 354 | **Returns**: `ReleaseLines` instance with only modern release lines. 355 | 356 | ### `getLTS(resetDate)` 357 | 358 | Filters `ReleaseLine` items by **isLTS** 359 | 360 | **Params**: 361 | - **resetDate**: `Date`, `String` or `Boolean` - changes the date. (optional) 362 | 363 | **Returns**: `ReleaseLines` instance with only release lines that have an LTS active mode in their lifecycle. Note: It does not neccessarily mean it is an active LTS (see `getActive()`). 364 | 365 | 366 | ## `Release` _class_ 367 | 368 | **Instance properties**: 369 | - **version**: `String` - version number of release 370 | - **date**: `Date` - date of release 371 | - **modules**: `Number` - number of modules 372 | - **npm**: `String` - version 373 | - **v8**: `String` - version 374 | - **uv**: `String` - version 375 | - **zlib**: `String` - version 376 | - **openssl**: `String` - version 377 | 378 | **Instance getters**: 379 | - **vulns**: `Array` of [Vulnerability][] (lazy loading) 380 | - **isSafe**: `Boolean` - `true` if `Release` has no known vulnerabilities. 381 | - **isVulnerable**: `Boolean` - `true` if `Release` has one or more vulnerabilities. 382 | - **doc**: `String` url of docs for the specific release 383 | 384 | ### `Release.load(version)` _static_ 385 | 386 | **options**: 387 | - **version**: `String` 388 | 389 | **Returns**: `Release` or `null` if version does not exist 390 | 391 | ### `download(arch, type)` 392 | 393 | If `arch` is omitted returns directory to all download resources for release version. 394 | 395 | >Currently `arch` and `type` are not implemented 396 | 397 | **options**: 398 | - **arch**: `String` 399 | - **type**: `String` (gz, xz, pkg, msi, zip) 400 | 401 | **Returns**: `String` url of download resource 402 | 403 | 404 | ## `Releases` extends `Array` _class_ 405 | 406 | An array of `Release` instances. Provides helper methods for updating, filtering and querying release lines. 407 | 408 | ### `Releases.load(version)` _static_ 409 | 410 | **Params**: 411 | - **version**: `String` (example `v6`) 412 | 413 | **Returns**: `Releases` instance 414 | 415 | ### `getSafe()` 416 | 417 | Filters `Release` items by **isSafe** 418 | 419 | **Returns**: `Releases` instance with only releases that have no known vulnerabilities. 420 | 421 | 422 | ## `Vulnerability` _class_ 423 | 424 | **Instance properties**: 425 | - **id**: `String` 426 | 427 | **Instance getters**: 428 | 429 | - **cve** 430 | - **ref** 431 | - **vulnerable** 432 | - **patched** 433 | - **description** 434 | - **overview** 435 | - **author** 436 | - **cvss** 437 | - **cvss_score** 438 | - **source**: `String` - url to specific vulnerability in [nodejs/security-wg][] repo. 439 | - **isValid**: `Boolean` 440 | 441 | 442 | ## `ChangeLog` _class_ 443 | 444 | Currently `deprecations` and `notable changes` are not fully supported. 445 | 446 | **Instance properties**: 447 | - **version**: `String` 448 | - **line**: `String` current release line (example: `Current`) 449 | - **date**: `Date` release date 450 | - **releasedBy**: `String` individual who performed the release 451 | - **text**: `String` meta text above other sub headers 452 | - **raw**: `String` raw markdown text of the whole release 453 | - **commits**: `Array` of `Commit` 454 | 455 | ### `load(version)` _static_ 456 | 457 | **options**: 458 | - **version**: `String` 459 | 460 | **Returns**: `ChangeLog` instance or `null` 461 | 462 | 463 | ## `Commit` _class_ 464 | 465 | **Instance properties**: 466 | - **sha**: `String` commit sha 467 | - **pr**: `String` pr number associated with commit 468 | - **author**: `String` author of commit 469 | - **reverts**: `Boolean` if commit reverts prior commit(s) or behavior 470 | - **desc**: `String` commit description 471 | - **topics**: `Array` areas the commit touches (example: ['build', 'win']) 472 | 473 | **Instance getters**: 474 | - **shaUrl**: `String` url to the commit in github 475 | - **prUrl**: `String` url to the pr on github 476 | 477 | ## Acknowledgements 478 | 479 | Thank you [Node.js Release Team][] and all the contributors to the [Node.js project][], without you none of this is possible. Special thanks goes out to [Tierney Cyren][]. His relentless desire to improve accessibility, visibility and communication inspired this project. 480 | 481 | ## Contributing 482 | 483 | To submit a bug report, please create an [issue on GitHub][]. 484 | 485 | If you'd like to contribute code to this project, please read the 486 | [CONTRIBUTING.md][] document. 487 | 488 | ## License & Copyright 489 | 490 | **node-release-lines** is Copyright (c) 2018 Nathan White and licensed under the 491 | MIT license. All rights not explicitly granted in the MIT license are reserved. 492 | See the included [LICENSE.md][] file for more details. 493 | 494 | [Node.js project]: https://nodejs.org/ 495 | [Node.js Release Team]: https://github.com/nodejs/Release#lts-team-members 496 | [Tierney Cyren]: https://bnb.im/ 497 | [how release lines work]: http://nodesource.com/blog/understanding-how-node-js-release-lines-work/ 498 | [release schedule]: https://raw.githubusercontent.com/nodejs/Release/master/schedule.json 499 | [nodejs/security-wg]: https://github.com/nodejs/security-wg 500 | [issue on GitHub]: https://github.com/nw/node-release-lines/issues 501 | [Command Line]: #cli 502 | [see terminology]: #terminology 503 | [Terminology]: #terminology 504 | [ReleaseLine]: #ReleaseLine 505 | [ReleaseLines]: #ReleaseLines 506 | [Release]: #Release 507 | [Releases]: #Releases 508 | [Vulnerability]: #Vulnerability 509 | [ChangeLog]: #ChangeLog 510 | [Commit]: #Commit 511 | [CONTRIBUTING.md]: CONTRIBUTING.md 512 | [LICENSE.md]: LICENSE.md -------------------------------------------------------------------------------- /data/vulns.core.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": { 3 | "cve": [ 4 | "CVE-2017-1000381" 5 | ], 6 | "ref": "https://nodejs.org/en/blog/vulnerability/july-2017-security-releases/", 7 | "vulnerable": "8.x || 7.x || 4.x || 6.x || 5.x", 8 | "patched": "^8.1.4 || ^7.10.1 || ^4.8.4 || ^6.11.1", 9 | "description": "memory overread when parsing invalid NAPTR responses", 10 | "overview": "The c-ares function ares_parse_naptr_reply(), which is used for parsing NAPTR\nresponses, could be triggered to read memory outside of the given input buffer\nif the passed in DNS response packet was crafted in a particular way.\n\n", 11 | "id": "1" 12 | }, 13 | "2": { 14 | "cve": [], 15 | "vulnerable": "4.x || 5.x || 6.x || 7.x || 8.x", 16 | "patched": "^4.8.4 || ^6.11.1 || ^7.10.1 || ^8.1.4", 17 | "description": "DoS possible in V8 object lookup", 18 | "overview": "Disable V8 snapshots - The hashseed embedded in the snapshot is\ncurrently the same for all runs of the binary. This opens node up to\ncollision attacks which could result in a Denial of Service. We have\ntemporarily disabled snapshots until a more robust solution is found\nFixed: Ali Ijaz Sheikh\nReported: Fedor Indutny\nref: https://nodejs.org/en/blog/vulnerability/july-2017-security-releases/\n\n", 19 | "id": "2" 20 | }, 21 | "3": { 22 | "cve": [ 23 | "CVE-2017-3731" 24 | ], 25 | "description": "Truncated packet could crash via OOB read", 26 | "vulnerable": "4.x || 5.x || 6.x || 7.x", 27 | "patched": "^4.7.3 || ^6.9.5 || ^7.5.0", 28 | "ref": "https://nodejs.org/en/blog/vulnerability/openssl-january-2017/", 29 | "overview": "This is a moderate severity flaw in OpenSSL. By default, Node.js disables RC4 so\nmost users are not affected. As RC4 can be enabled programmatically, it is\npossible for a Node.js developer to craft code that may be vulnerable to this\nflaw. Any user activating RC4 in their codebase should prioritise this update.\n\nAll active versions of Node.js are affected, but the severity is very low for\nmost users.\n\n", 30 | "id": "3" 31 | }, 32 | "4": { 33 | "cve": [ 34 | "CVE-2017-3732" 35 | ], 36 | "vulnerable": "4.x || 5.x || 6.x || 7.x", 37 | "patched": "^4.7.3 || ^6.9.5 || ^7.5.0", 38 | "ref": "https://nodejs.org/en/blog/vulnerability/openssl-january-2017/", 39 | "description": "BN_mod_exp may produce incorrect results on x86_64", 40 | "overview": "As noted by the OpenSSL team, the likelihood of being able to craft a practical\nattack that uses this flaw is very low. In addition, Node.js enables\nSSL_OP_SINGLE_DH_USE, further decreasing the chance of a successful exploit of\nthis vulnerability in a Node.js service.\n\nAll active versions of Node.js are affected, but the severity is very low for\nNode.js users.\n\n", 41 | "id": "4" 42 | }, 43 | "5": { 44 | "cve": [ 45 | "CVE-2016-7055" 46 | ], 47 | "description": "Montgomery multiplication may produce incorrect results", 48 | "vulnerable": "4.x || 5.x || 6.x || 7.x", 49 | "patched": "^4.7.3 || ^6.9.5 || ^7.5.0", 50 | "ref": "https://nodejs.org/en/blog/vulnerability/openssl-january-2017/", 51 | "overview": "Some calculations, when run on an Intel Broadwell or later CPU, can produce in\nerroneous results. This flaw has been previously discussed by the Node.js team\non GitHub. It is not believed that practical attacks can be crafted to exploit\nthis vulnerability except in very specific circumstances. Therefore this is a\nlow severity flaw.\n\nAll active versions of Node.js are affected, but the severity is very low for\nNode.js users.\n\n", 52 | "id": "5" 53 | }, 54 | "6": { 55 | "cve": [ 56 | "CVE-2016-9551" 57 | ], 58 | "description": "potential buffer overflow when writing data to console on Windows 10", 59 | "vulnerable": "^7.1.0", 60 | "patched": "^7.2.0", 61 | "ref": "https://github.com/nodejs/node/pull/9647", 62 | "overview": "Please be aware that Node.js v7.2.0 was released today and includes a small\nsecurity update arising from libuv: https://nodejs.org/en/blog/release/v7.2.0/\n\nlibuv v1.10.1 reverts a change that was introduced in v1.10.0, included in\nNode.js v7.1.0. The reverted code was found to contain a potential buffer\noverflow in output written to the console. We are not aware of any exploit of\nthis flaw and it only impacts Windows 10 (November update and later). This flaw\nhas been assigned the identifier CVE-2016-9551 and was originally discovered and\nreported by Hitesh Kanwathirtha of Microsoft.\n\nUsers of the v7 release line running on Windows 10 should upgrade to Node.js\nv7.2.0 at their earliest convenience.\n\nNo other version of Node.js is known to be impacted by this flaw.\n\n", 63 | "id": "6" 64 | }, 65 | "7": { 66 | "cve": [ 67 | "CVE-2016-9840.", 68 | "CVE-2016-9841.", 69 | "CVE-2016-9842.", 70 | "CVE-2016-9843." 71 | ], 72 | "ref": "https://github.com/madler/zlib/commit/d1d577490c15a0c6862473d7576352a9f18ef811", 73 | "description": "undefined language constructs that may have security impact", 74 | "vulnerable": "4.x || 5.x || 6.x || 7.x", 75 | "patched": "^4.8.2 || ^6.10.2 || ^7.6.0", 76 | "overview": "An upgrade to zlib 1.2.11 to fix a number of low severity CVEs\nthat were present in zlib 1.2.8.\n\n", 77 | "id": "7" 78 | }, 79 | "8": { 80 | "cve": [ 81 | "CVE-2016-5180" 82 | ], 83 | "vulnerable": "0.10.x || 0.12.x || 4.x", 84 | "patched": "^0.10.48 || ^0.12.17 || ^4.6.1", 85 | "description": "ares_create_query single byte out of buffer write", 86 | "ref": "https://c-ares.haxx.se/adv_20160929.html", 87 | "overview": "A security vulnerability has been discovered in the c-ares library that is\nbundled with all versions of Node.js. Due to the difficulty of triggering and\nmaking use of this vulnerability we currently consider this a low-severity\nsecurity flaw for Node.js users.\n\nMore information at https://c-ares.haxx.se/adv_20160929.html\n\n", 88 | "id": "8" 89 | }, 90 | "9": { 91 | "cve": [], 92 | "vulnerable": "6.x", 93 | "patched": "^6.9.0", 94 | "ref": "https://nodejs.org/en/blog/vulnerability/october-2016-security-releases/", 95 | "description": "automatically loading OPENSSL_CONF is unsafe", 96 | "overview": "Always triggering a configuration file load attempt from `OPENSSL_CONF` or the\ndefault location for the current platform may allow an attacker to load\ncompromised OpenSSL configuration into a Node.js process if they are able to\nplace a file in a default location.\n\n", 97 | "id": "9" 98 | }, 99 | "10": { 100 | "cve": [ 101 | "CVE-2016-5172" 102 | ], 103 | "vulnerable": "6.x", 104 | "patched": "^6.9.0", 105 | "ref": "https://nodejs.org/en/blog/vulnerability/october-2016-security-releases/", 106 | "overview": "The V8 parser mishandled scopes, potentially allowing an attacker to obtain\nsensitive information from arbitrary memory locations via crafted JavaScript\ncode. This vulnerability would require an attacker to be able to execute\narbitrary JavaScript code in a Node.js process.\n\n", 107 | "id": "10" 108 | }, 109 | "11": { 110 | "cve": [], 111 | "vulnerable": "6.x", 112 | "patched": "^6.9.0", 113 | "ref": "https://nodejs.org/en/blog/vulnerability/october-2016-security-releases/", 114 | "author": "Jann Horn", 115 | "description": "unauthorized clients can easily access inspector port", 116 | "overview": "Generate a UUID for each execution of the inspector. This provides additional\nsecurity to prevent unauthorized clients from connecting to the Node.js process\nvia the v8_inspector port when running with `--inspect`. Since the debugging\nprotocol allows extensive access to the internals of a running process, and the\nexecution of arbitrary code, it is important to limit connections to authorized\ntools only.\n\n", 117 | "id": "11" 118 | }, 119 | "12": { 120 | "cve": [ 121 | "CVE-2016-6304" 122 | ], 123 | "ref": "https://github.com/nodejs/node/pull/8714", 124 | "vulnerable": "6.x || 5.x || 4.x", 125 | "patched": "^6.7.0 || ^4.6.0", 126 | "description": "openssl 1.0.2h vulnerabilities", 127 | "overview": "A malicious client can exhaust a server's memory, resulting in a denial of\nservice (DoS) by sending very large OCSP Status Request extensions in a single\nsession.\n\nThis flaw is labelled high severity due to the ease of use for a DoS attack and\nNode.js servers using TLS are vulnerable.\n\n", 128 | "id": "12" 129 | }, 130 | "13": { 131 | "cve": [ 132 | "CVE-2016-2183" 133 | ], 134 | "ref": "https://github.com/nodejs/node/pull/8714", 135 | "vulnerable": "6.x || 5.x || 4.x", 136 | "patched": "^6.7.0 || ^4.6.0", 137 | "description": "openssl 1.0.2h vulnerabilities", 138 | "overview": "SWEET32 is a new attack on older block cipher algorithms that use a block size\nof 64 bits.\n\nAs mitigation, OpenSSL has moved DES-based ciphers from the HIGH to MEDIUM\ngroup. As Node.js includes HIGH, but not MEDIUM, in its default suite, affected\nciphers are no longer included unless the default suite is not used. Node's\ndefault TLS cipher suite can be found in the API documentation.\n\n", 139 | "id": "13" 140 | }, 141 | "14": { 142 | "cve": [ 143 | "CVE-2016-6303" 144 | ], 145 | "ref": "https://github.com/nodejs/node/pull/8714", 146 | "vulnerable": "6.x || 5.x || 4.x", 147 | "patched": "^6.7.0 || ^4.6.0", 148 | "description": "openssl 1.0.2h vulnerabilities", 149 | "overview": "An overflow can occur in MDC2_Update() under certain circumstances resulting in\nan out of bounds (OOB) error. This attack is impractical on most platforms due\nto the size of data required to trigger the OOB error.\n\nNode.js is impacted by this flaw but due to the impracticalities of exploiting\nit and the very low usage of of MDC-2, it is very low severity for Node.js\nusers.\n\n", 150 | "id": "14" 151 | }, 152 | "15": { 153 | "cve": [ 154 | "CVE-2016-2178" 155 | ], 156 | "ref": "https://github.com/nodejs/node/pull/8714", 157 | "vulnerable": "6.x || 5.x || 4.x", 158 | "patched": "^6.7.0 || ^4.6.0", 159 | "description": "openssl 1.0.2h vulnerabilities", 160 | "overview": "A flaw in the OpenSSL DSA implementation means that a non-constant time codepath\nis followed for certain operations. This has been demonstrated through a\ncache-timing attack to be sufficient for an attacker to recover the private DSA\nkey.\n\nThis is very low severity for Node.js users due to the difficulty in taking\nadvantage of this attack and because DSA is very rarely used.\n\n", 161 | "id": "15" 162 | }, 163 | "16": { 164 | "cve": [ 165 | "CVE-2016-6306" 166 | ], 167 | "ref": "https://github.com/nodejs/node/pull/8714", 168 | "vulnerable": "6.x || 5.x || 4.x", 169 | "patched": "^6.7.0 || ^4.6.0", 170 | "description": "openssl 1.0.2h vulnerabilities", 171 | "overview": "Some missing message length checks can result in out of bounds (OOB) reads of up\nto 2 bytes beyond an allocated buffer. There is a theoretical denial of service\n(DoS) risk. This only impacts a client or a server which enables client\nauthentication.\n\nNode.js is impacted by this low severity flaw.\n\n", 172 | "id": "16" 173 | }, 174 | "17": { 175 | "cve": [], 176 | "ref": "https://github.com/nodejs/node-private/pull/73", 177 | "vulnerable": "6.x", 178 | "patched": "^6.7.0", 179 | "author": "Ahmed Zaki", 180 | "overview": "Remove support for loading dynamic third-party engine modules. An attacker\nmay be able to hide malicious code to be inserted into Node.js at runtime by\nmasquerading as one of the dynamic engine modules. Originally reported by\n\n", 181 | "id": "17" 182 | }, 183 | "18": { 184 | "cve": [ 185 | "CVE-2016-5325" 186 | ], 187 | "ref": "https://github.com/nodejs/node-private/pull/60", 188 | "vulnerable": "6.x || 4.x || 5.x", 189 | "patched": "^6.7.0 || ^4.6.0", 190 | "author": "Romain Gaucher", 191 | "description": "HTTP processing security defect (CVE-2016-5325)", 192 | "cvss": "3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N", 193 | "cvss_score": "4.8 (Medium)", 194 | "overview": "**http**: Properly validate for allowable characters in the `reason` argument in\n`ServerResponse#writeHead()`. Fixes a possible response splitting attack vector.\nThis introduces a new case where `throw` may occur when configuring HTTP\nresponses, users should already be adopting try/catch here.\n\nThis is a low severity security defect that that may make HTTP response\nsplitting possible under certain circumstances. If user-input is passed to the\nreason argument to writeHead() on an HTTP response, a new-line character may be\nused to inject additional responses.\n\nThe fix for this defect introduces a new case where throw may occur when\nconfiguring HTTP responses. Users should already be adopting try/catch here.\n\nCommon Vulnerability Scoring System (CVSS) v3 Base Score:\n\n\tMetric\tScore\n\tBase Score:\t4.8 (Medium)\n\tBase Vector:\tCVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N\n\tAttack Vector:\tNetwork (AV:N)\n\tAttack Complexity:\tHigh (AC:H)\n\tPrivileges Required:\tNone (PR:N)\n\tUser Interaction:\tNone (UI:N)\n\tScope of Impact:\tUnchanged (S:U)\n\tConfidentiality Impact:\tLow (C:L)\n\tIntegrity Impact:\tLow (I:L)\n\tAvailability Impact:\tNone (A:N)\n\nRefer to the\n[CVSS v3 Specification](https://www.first.org/cvss/specification-document)\nfor details on the meanings and application of the vector components.\n\n", 195 | "id": "18" 196 | }, 197 | "19": { 198 | "cve": [ 199 | "CVE-2016-7099" 200 | ], 201 | "ref": "https://github.com/nodejs/node/commit/743f0c9164", 202 | "vulnerable": "6.x || 4.x || 5.x", 203 | "patched": "^6.7.0 || ^4.6.0", 204 | "author": "Alexander Minozhenko and James Bunton (Atlassian)", 205 | "description": "invalid wildcard certificate validation check", 206 | "overview": "Fix invalid wildcard certificate validation check whereby a TLS server may be\nable to serve an invalid wildcard certificate for its hostname due to improper\nvalidation of `*.` in the wildcard string. \n\nThis is a high severity defect that would allow a malicious TLS server to serve\nan invalid wildcard certificate for its hostname and be improperly validated by\na Node.js client. This is due to a flaw in the validation of *. in the wildcard\nname string.\n\n", 207 | "id": "19" 208 | }, 209 | "20": { 210 | "cve": [], 211 | "ref": "https://github.com/nodejs/node-private/pull/73", 212 | "vulnerable": "6.x || 4.x", 213 | "patched": "^6.7.0 || ^4.6.0", 214 | "description": "**crypto**: don't build hardware engines (Ben Noordhuis)", 215 | "overview": "This is a low severity security defect. By default, OpenSSL will load a list of\nthird-party engine modules when the ENGINE_load_builtin_engines() function is\nused. These are normally not present on a user's system. An attacker may be able\nto make Node.js load malicious code by masquerading it as one of the dynamic\nengine modules.\n\nThis defect primarily impacts Windows due to the standard DLL search paths.\nHowever, UNIX users may also be at risk with a poorly configured LD_LIBRARY_PATH\nenvironment variable or /etc/ld.so.conf path list.\n\n", 216 | "id": "20" 217 | }, 218 | "21": { 219 | "cve": [], 220 | "vulnerable": "6.x || 5.x || 4.x", 221 | "patched": "^6.2.1 || ^4.5.0", 222 | "description": "Ignore negative lengths in calls to `Buffer()` and `Buffer.allocUnsafe()`.", 223 | "ref": "https://github.com/nodejs/node/issues/7047#issuecomment-222393982", 224 | "overview": "This fixes a possible security concern (reported by Feross Aboukhadijeh) where\nuser input is passed unchecked to the Buffer constructor or `allocUnsafe()` as\nit can expose parts of the memory slab used by other Buffers in the application.\nNote that negative lengths are not supported by the Buffer API and user input to\nthe constructor should always be sanitised and type-checked.\n\n", 225 | "id": "21" 226 | }, 227 | "22": { 228 | "cve": [ 229 | "CVE-2016-2107" 230 | ], 231 | "vulnerable": "4.x || 5.x || 6.x", 232 | "patched": "^4.4.4 || ^5.11.1 || ^6.1.0", 233 | "ref": "https://nodejs.org/en/blog/vulnerability/openssl-may-2016/#cve-2016-2107-padding-oracle-in-aes-ni-cbc-mac-check", 234 | "description": "Padding oracle in AES-NI CBC MAC check", 235 | "overview": "A man-in-the-middle (MITM) attacker may be able to execute a padding oracle\nattack to decrypt traffic when a connection uses an AES-CBC cipher and the\nserver runs on an Intel CPU supporting AES-NI. This is a common configuration\nfor TLS servers.\n\nThe OpenSSL project has labelled this vulnerability high severity.\n\n", 236 | "id": "22" 237 | }, 238 | "23": { 239 | "cve": [ 240 | "CVE-2016-2105" 241 | ], 242 | "vulnerable": "4.x || 5.x || 6.x", 243 | "patched": "^4.4.4 || ^5.11.1 || ^6.1.0", 244 | "ref": "https://nodejs.org/en/blog/vulnerability/openssl-may-2016/#cve-2016-2107-padding-oracle-in-aes-ni-cbc-mac-check", 245 | "description": "EVP_EncodeUpdate overflow", 246 | "overview": "An overflow can occur in the OpenSSL EVP_EncodeUpdate() function which is used\nfor Base64 encoding of binary data. An attacker must be able to supply large\namounts of input data in order to cause an overflow.\n\nNode.js uses the EVP_EncodeUpdate() internally during calls to\ncrypto.Certificate#exportPublicKey() for SPKAC Certificate Signing\nRequests. User-supplied data must be passed to this method for\napplications to be vulnerable. This method has been available since\nNode.js v0.12.\n\n", 247 | "id": "23" 248 | }, 249 | "24": { 250 | "cve": [ 251 | "CVE-2016-1669" 252 | ], 253 | "vulnerable": ">=6.0.0 <6.2.0 || 5.x || 4.x", 254 | "patched": "^4.4.6 || ^5.12.0", 255 | "ref": "https://nodejs.org/en/blog/vulnerability/june-2016-security-releases", 256 | "description": "Buffer overflow in V8", 257 | "overview": "Under certain conditions, V8 may improperly expand memory allocations in the\nZone::New function. This could potentially be used to cause a Denial of Service\nvia buffer overflow or as a trigger for a remote code execution.\n\nAlthough this bug is marked as high severity in the corresponding Chromium\nrelease (50.0.2661.102), our assessment is that this is low severity for\nNode.js users due to the level of difficulty in making use of this\nvulnerability. However, users are encouraged to upgrade their Node.js\ninstallation to ensure they are properly protected.\n\n", 258 | "id": "24" 259 | }, 260 | "25": { 261 | "cve": [], 262 | "vulnerable": "5.x || 4.x || 6.x", 263 | "patched": "^5.12.0 || ^4.5.0 || ^6.2.1", 264 | "ref": "https://github.com/nodejs/node/pull/7562", 265 | "description": "ignore negative allocation lengths", 266 | "id": "25" 267 | }, 268 | "26": { 269 | "cve": [], 270 | "description": "security flaw in the use of npm authentication tokens in HTTP requests", 271 | "vulnerable": "5.x || 4.x", 272 | "patched": "^5.10.0 || 4.4.2", 273 | "ref": "https://github.com/npm/node/pull/6", 274 | "overview": "Upgrade npm to fixes a security flaw in the use of\nauthentication tokens in HTTP requests that would allow an attacker to set up a\nserver that could collect tokens from users of the command-line interface.\nAuthentication tokens have previously been sent with every request made by the\nCLI for logged-in users, regardless of the destination of the request. This\nupdate fixes this by only including those tokens for requests made against the\nregistry or registries used for the current install.\n\nThis is a flaw in the version of npm included with node.\n\nnpm is updated to 3.8.3 in node 5.10.1, and to 2.15.1 in node 4.4.2.\n\n", 275 | "id": "26" 276 | }, 277 | "27": { 278 | "cve": [ 279 | "CVE-2016-2086" 280 | ], 281 | "vulnerable": "5.x || 4.x", 282 | "patched": "^5.6.0 || ^4.3.0", 283 | "overview": "Fix defects in HTTP header parsing for requests and responses that\ncan allow request smuggling (CVE-2016-2086).\n\nHTTP header parsing now aligns more closely with the HTTP spec\nincluding restricting the acceptable characters.\n\n", 284 | "id": "27" 285 | }, 286 | "28": { 287 | "cve": [ 288 | "CVE-2016-2216" 289 | ], 290 | "vulnerable": "5.x || 4.x", 291 | "patched": "^5.6.0 || ^4.3.0", 292 | "overview": "Fix defects in HTTP header parsing for requests and responses that\ncan allow response splitting (CVE-2016-2216).\n\nHTTP header parsing now aligns more closely with the HTTP spec\nincluding restricting the acceptable characters.\n\nIntroduce new `--security-revert={cvenum}` command line flag for selective\nreversion of specific CVE fixes allow the fix for CVE-2016-2216 to be\nselectively reverted using `--security-revert=CVE-2016-2216`.\n\n", 293 | "id": "28" 294 | }, 295 | "29": { 296 | "cve": [], 297 | "vulnerable": "4.x || 5.x", 298 | "patched": "^5.11.1 || ^4.4.4", 299 | "description": "buffer safeguard against accidental kNoZeroFill", 300 | "overview": "To reproduce: try { Buffer(1e10); } catch (e) {} new Uint8Array(100);.\n\nTo be affected, one would need to:\n\nHave any way how the user could make the API pass huge numbers to\nBuffer/SlowBuffer/Buffer.allocUnsafe, e.g. by sending invalid input\n\n", 301 | "id": "29" 302 | }, 303 | "30": { 304 | "cve": [], 305 | "vulnerable": "4.x || 5.x", 306 | "patched": "^4.3.2 || ^5.7.1", 307 | "ref": "https://github.com/nodejs/node/pull/5507", 308 | "overview": "Fix a double-free defect in parsing malformed DSA keys that may potentially be\nused for DoS or memory corruption attacks. It is likely to be very difficult to\nuse this defect for a practical attack and is therefore considered low severity\nfor Node.js users. More info is available at\ncve: CVE-2016-0705\n\n", 309 | "id": "30" 310 | }, 311 | "31": { 312 | "cve": [ 313 | "CVE-2016-0797" 314 | ], 315 | "vulnerable": "4.x || 5.x", 316 | "patched": "^4.3.2 || ^5.7.1", 317 | "ref": "https://github.com/nodejs/node/pull/5507", 318 | "overview": "Fix a defect that can cause memory corruption in certain very rare cases\nrelating to the internal `BN_hex2bn()` and `BN_dec2bn()` functions. It is\nbelieved that Node.js is not invoking the code paths that use these functions so\npractical attacks via Node.js using this defect are _unlikely_ to be possible.\n\n", 319 | "id": "31" 320 | }, 321 | "32": { 322 | "cve": [ 323 | "CVE-2016-0702" 324 | ], 325 | "vulnerable": "4.x || 5.x", 326 | "patched": "^4.3.2 || ^5.7.1", 327 | "ref": "https://ssrg.nicta.com.au/projects/TS/cachebleed", 328 | "overview": "Fix a defect that makes the _[CacheBleed\nAttack](https://ssrg.nicta.com.au/projects/TS/cachebleed/)_ possible. This\ndefect enables attackers to execute side-channel attacks leading to the\npotential recovery of entire RSA private keys. It only affects the Intel Sandy\nBridge (and possibly older) microarchitecture when using hyper-threading. Newer\nmicroarchitectures, including Haswell, are unaffected.\n\n", 329 | "id": "32" 330 | }, 331 | "33": { 332 | "cve": [], 333 | "vulnerable": "4.x || 5.x", 334 | "patched": "^5.6.0 || ^4.3.0", 335 | "description": "mitigate against the Logjam attack", 336 | "overview": "To mitigate against the Logjam attack, TLS clients now reject Diffie-Hellman\nhandshakes with parameters shorter than 1024-bits, up from the previous limit of\n768-bits.\n\n", 337 | "id": "33" 338 | }, 339 | "34": { 340 | "cve": [ 341 | "CVE-2015-8027" 342 | ], 343 | "vulnerable": "5.x || 4.x", 344 | "patched": "^5.1.1 || ^4.2.3", 345 | "overview": "A bug whereby an HTTP socket may no longer have a parser associated with it but\na pipelined request attempts to trigger a pause or resume on the non-existent\nparser, a potential denial-of-service vulnerability.\n\n", 346 | "id": "34" 347 | }, 348 | "35": { 349 | "cve": [ 350 | "CVE-2015-6764" 351 | ], 352 | "vulnerable": "5.x || 4.x", 353 | "patched": "^5.1.1 || ^4.2.3", 354 | "overview": "Backport fix for CVE-2015-6764, a bug in v8's `JSON.stringify()` that can result\nin out-of-bounds reads for arrays.\n\n", 355 | "id": "35" 356 | }, 357 | "36": { 358 | "cve": [ 359 | "CVE-2015-3193" 360 | ], 361 | "vulnerable": "5.x || 4.x", 362 | "patched": "^5.1.1 || ^4.2.3", 363 | "ref": "http://openssl.org/news/secadv/20151203.txt", 364 | "description": "BN_mod_exp may produce incorrect results on x86_64", 365 | "overview": "An attack may be possible against a Node.js TLS server using DHE key exchange.\nDetails are available at .\n\n", 366 | "id": "36" 367 | }, 368 | "37": { 369 | "cve": [ 370 | "CVE-2015-3194" 371 | ], 372 | "vulnerable": "5.x || 4.x", 373 | "patched": "^5.1.1 || ^4.2.3", 374 | "ref": "https://github.com/nodejs/node/pull/4134", 375 | "description": "Certificate verify crash with missing PSS parameter", 376 | "overview": "A potential denial-of-service vector for Node.js TLS servers using client\ncertificate authentication; TLS clients are also impacted. Details are available\nat .\n\n", 377 | "id": "37" 378 | }, 379 | "38": { 380 | "cve": [], 381 | "vulnerable": "4.7.1 || 6.9.3", 382 | "description": "no shasum exists to verify downloads", 383 | "overview": "While promoting additional platforms for v4.7.1 and v6.9.3 after the release,\nthe tarballs on the release server were overwritten and now have different\nshasums.\n\n", 384 | "id": "38" 385 | }, 386 | "39": { 387 | "cve": [], 388 | "vulnerable": "^4.1.0", 389 | "patched": "^4.1.1", 390 | "description": "data leakage via reuse of memory space in TypedArrays", 391 | "ref": "https://github.com/nodejs/node/pull/2931", 392 | "overview": "A bug was introduced in v4.1.0 where allocating a new zero-length buffer can\nresult in the _next_ allocation of a TypedArray in JavaScript not being\nzero-filled. In certain circumstances this could result in data leakage via\nreuse of memory space in TypedArrays, breaking the normally safe assumption that\nTypedArrays should be always zero-filled.\n\n", 393 | "id": "39" 394 | }, 395 | "40": { 396 | "cve": [ 397 | "CVE-2015-7384" 398 | ], 399 | "vulnerable": "4.x", 400 | "patched": "^4.1.2", 401 | "cvss": "3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H", 402 | "cvss_score": "5.9 (Medium)", 403 | "ref": "https://github.com/nodejs/node/pull/3128", 404 | "description": "out-of-order 'finish' event bug in pipelining can abort execution", 405 | "id": "40" 406 | }, 407 | "41": { 408 | "cve": [], 409 | "vulnerable": "4.x", 410 | "patched": "^4.1.1", 411 | "ref": "https://github.com/nodejs/node/pull/2945", 412 | "overview": "Guard against response-splitting of HTTP trailing headers added via\n[`response.addTrailers()`](https://nodejs.org/api/http.html#http_response_addtrailers_headers)\nby removing new-line (`[\\r\\n]`) characters from values. Note that standard\nheader values are already stripped of new-line characters. The expected security\nimpact is low because trailing headers are rarely used.\n\n", 413 | "id": "41" 414 | }, 415 | "42": { 416 | "cve": [ 417 | "CVE-2017-14849" 418 | ], 419 | "vulnerable": "8.5.0", 420 | "patched": "^8.6.0", 421 | "ref": "https://nodejs.org/en/blog/vulnerability/september-2017-path-validation/", 422 | "overview": "Node.js version 8.5.0 included a change which caused a security vulnerability in the checks on paths made by some community modules. As a result, an attacker may be able to access file system paths other than those intended.", 423 | "id": "42" 424 | }, 425 | "43": { 426 | "cve": [ 427 | "CVE-2017-14919" 428 | ], 429 | "vulnerable": "^4.8.2 || ^6.10.2 || 8.x", 430 | "patched": "^4.8.5 || ^6.11.5 || ^8.8.0", 431 | "ref": "https://nodejs.org/en/blog/vulnerability/oct-2017-dos/", 432 | "overview": "Node.js was susceptible to a remote DoS attack due to a change that came in as part of zlib v1.2.9. In zlib v1.2.9 8 became an invalid value for the windowBits parameter and Node's zlib module will crash or throw an exception (depending on the version) if you call zlib.createDeflateRaw({windowBits: 8}).", 433 | "id": "43" 434 | }, 435 | "44": { 436 | "cve": [ 437 | "CVE-2017-15896" 438 | ], 439 | "vulnerable": "^4.0.0 || ^6.0.0 || ^8.0.0 || ^9.0.0", 440 | "patched": "^4.8.7 || ^6.12.2 || ^8.9.3 || ^9.2.1", 441 | "ref": "https://nodejs.org/en/blog/vulnerability/december-2017-security-releases/", 442 | "overview": "Node.js was affected by OpenSSL vulnerability CVE-2017-3737 in regards to the use of SSL_read() due to TLS handshake failure. The result was that an active network attacker could send application data to Node.js using the TLS or HTTP2 modules in a way that bypassed TLS authentication and encryption.", 443 | "id": "44" 444 | }, 445 | "45": { 446 | "cve": [ 447 | "CVE-2017-15897" 448 | ], 449 | "vulnerable": "^8.0.0 || ^9.0.0", 450 | "patched": "^8.9.3 || ^9.2.1", 451 | "ref": "https://nodejs.org/en/blog/vulnerability/december-2017-security-releases/", 452 | "overview": "Node.js had a bug in versions 8.X and 9.X which caused buffers to not be initialized when the encoding for the fill value did not match the encoding specified. For example, 'Buffer.alloc(0x100, \"This is not correctly encoded\", \"hex\");' The buffer implementation was updated such that the buffer will be initialized to all zeros in these cases.", 453 | "id": "45" 454 | }, 455 | "46": { 456 | "cve": [ 457 | "CVE-2018-7158" 458 | ], 459 | "vulnerable": "^4.0.0", 460 | "patched": "^4.9.0", 461 | "ref": "https://nodejs.org/en/blog/vulnerability/march-2018-security-releases/", 462 | "overview": "The `'path'` module in the Node.js 4.x release line contains a potential regular expression denial of service (ReDoS) vector. The code in question was replaced in Node.js 6.x and later so this vulnerability only impacts all versions of Node.js 4.x. The regular expression, `splitPathRe`, used within the `'path'` module for the various path parsing functions, including `path.dirname()`, `path.extname()` and `path.parse()` was structured in such a way as to allow an attacker to craft a string, that when passed through one of these functions, could take a significant amount of time to evaluate, potentially leading to a full denial of service.", 463 | "id": "46" 464 | }, 465 | "47": { 466 | "cve": [ 467 | "CVE-2018-7159" 468 | ], 469 | "vulnerable": "^4.0.0 || ^6.0.0 || ^8.0.0 || ^9.0.0", 470 | "patched": "^4.9.0 || ^6.14.0 || ^8.11.0 || ^9.10.0", 471 | "ref": "https://nodejs.org/en/blog/vulnerability/march-2018-security-releases/", 472 | "overview": "The HTTP parser in all current versions of Node.js ignores spaces in the `Content-Length` header, allowing input such as `Content-Length: 1 2` to be interpreted as having a value of `12`. The HTTP specification does not allow for spaces in the `Content-Length` value and the Node.js HTTP parser has been brought into line on this particular difference. The security risk of this flaw to Node.js users is considered to be VERY LOW as it is difficult, and may be impossible, to craft an attack that makes use of this flaw in a way that could not already be achieved by supplying an incorrect value for `Content-Length`. Vulnerabilities may exist in user-code that make incorrect assumptions about the potential accuracy of this value compared to the actual length of the data supplied. Node.js users crafting lower-level HTTP utilities are advised to re-check the length of any input supplied after parsing is complete.", 473 | "id": "47" 474 | }, 475 | "48": { 476 | "cve": [ 477 | "CVE-2018-7160" 478 | ], 479 | "vulnerable": "^6.0.0 || ^8.0.0 || ^9.0.0", 480 | "patched": "^6.14.0 || ^8.11.0 || ^9.10.0", 481 | "ref": "https://nodejs.org/en/blog/vulnerability/march-2018-security-releases/", 482 | "overview": "The Node.js inspector, in 6.x and later is vulnerable to a DNS rebinding attack which could be exploited to perform remote code execution. An attack is possible from malicious websites open in a web browser on the same computer, or another computer with network access to the computer running the Node.js process. A malicious website could use a DNS rebinding attack to trick the web browser to bypass same-origin-policy checks and to allow HTTP connections to localhost or to hosts on the local network. If a Node.js process with the debug port active is running on localhost or on a host on the local network, the malicious website could connect to it as a debugger, and get full code execution access.", 483 | "id": "48" 484 | }, 485 | "49": { 486 | "cve": [ 487 | "CVE-2018-7161" 488 | ], 489 | "vulnerable": "^8.0.0 || ^9.0.0 || ^10.0.0", 490 | "patched": "^8.11.3 || ^9.11.2 || ^10.4.1", 491 | "ref": "https://nodejs.org/en/blog/vulnerability/june-2018-security-releases/", 492 | "overview": "All versions of 8.x and later are vulnerable and the severity is HIGH. An attacker can cause a denial of service (DoS) by causing a node server providing an http2 server to crash. This can be accomplished by interacting with the http2 server in a manner that triggers a cleanup bug where objects are used in native code after they are no longer available. This has been addressed by updating the http2 implementation. Thanks to Jordan Zebor at F5 Networks for reporting this issue.", 493 | "id": "49" 494 | }, 495 | "50": { 496 | "cve": [ 497 | "CVE-2018-7162" 498 | ], 499 | "vulnerable": "^9.0.0 || ^10.0.0", 500 | "patched": "^9.11.2 || ^10.4.1", 501 | "ref": "https://nodejs.org/en/blog/vulnerability/june-2018-security-releases/", 502 | "overview": "All versions of 9.x and later are vulnerable and the severity is HIGH. An attacker can cause a denial of service (DoS) by causing a node process which provides an http server supporting TLS server to crash. This can be accomplished by sending duplicate/unexpected messages during the handshake. This vulnerability has been addressed by updating the TLS implementation. Thanks to Jordan Zebor at F5 Networks all of his help investigating this issue with the Node.js team.", 503 | "id": "50" 504 | }, 505 | "51": { 506 | "cve": [ 507 | "CVE-2018-7164" 508 | ], 509 | "vulnerable": "^9.7.x || ^10.0.0", 510 | "patched": "^9.11.2 || ^10.4.1", 511 | "ref": "https://nodejs.org/en/blog/vulnerability/june-2018-security-releases/", 512 | "overview": "Versions 9.7.0 and later are vulnerable and the severity is MEDIUM. A bug introduced in 9.7.0 increases the memory consumed when reading from the network into JavaScript using the net.Socket object directly as a stream. An attacker could use this cause a denial of service by sending tiny chunks of data in short succession. This vulnerability was restored by reverting to the prior behaviour.", 513 | "id": "51" 514 | }, 515 | "52": { 516 | "cve": [ 517 | "CVE-2018-7167" 518 | ], 519 | "vulnerable": "^6.0.0 || ^8.0.0 || ^9.0.0", 520 | "patched": "^6.14.3 || ^8.11.3 || ^9.11.2", 521 | "ref": "https://nodejs.org/en/blog/vulnerability/june-2018-security-releases/", 522 | "overview": "Calling Buffer.fill() or Buffer.alloc() with some parameters can lead to a hang which could result in a Denial of Service.", 523 | "id": "52" 524 | }, 525 | "53": { 526 | "cve": [ 527 | "CVE-2018-7166" 528 | ], 529 | "vulnerable": "10", 530 | "patched": ">= 10.9.0", 531 | "publish_date": "2018-08-16", 532 | "author": "Сковорода Никита Андреевич (Nikita Skovoroda / @ChALkeR)", 533 | "ref": "https://nodejs.org/en/blog/vulnerability/august-2018-security-releases/", 534 | "type": "CWE-226: Sensitive Information Uncleared Before Release", 535 | "overview": "An argument processing flaw can cause `Buffer.alloc()` to return uninitialized memory. This method is intended to be safe and only return initialized, or cleared, memory. The third argument specifying `encoding` can be passed as a number, this is misinterpreted by `Buffer's` internal \"fill\" method as the `start` to a fill operation. This flaw may be abused where `Buffer.alloc()` arguments are derived from user input to return uncleared memory blocks that may contain sensitive information.", 536 | "id": "53" 537 | }, 538 | "54": { 539 | "cve": [ 540 | "CVE-2018-12115" 541 | ], 542 | "vulnerable": "<= 10", 543 | "patched": "^6.14.4 || ^8.11.4 || >= 10.9.0", 544 | "publish_date": "2018-08-16", 545 | "author": "Сковорода Никита Андреевич (Nikita Skovoroda / @ChALkeR)", 546 | "ref": "https://nodejs.org/en/blog/vulnerability/august-2018-security-releases/", 547 | "type": "CWE-787: Out-of-bounds Write", 548 | "overview": "When used with UCS-2 encoding (recognized by Node.js under the names `'ucs2'`, `'ucs-2'`, `'utf16le'` and `'utf-16le'`), `Buffer#write()` can be abused to write outside of the bounds of a single `Buffer`. Writes that start from the second-to-last position of a buffer cause a miscalculation of the maximum length of the input bytes to be written.", 549 | "id": "54" 550 | }, 551 | "55": { 552 | "cve": [ 553 | "CVE-2018-12116" 554 | ], 555 | "vulnerable": "6 || 8", 556 | "patched": "^6.15.0 || ^8.14.0", 557 | "publish_date": "2018-11-27", 558 | "author": "Matteo Collina", 559 | "reported_by": "Arkadiy Tetelman", 560 | "ref": "https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/", 561 | "type": "CWE-115: Misinterpretation of Input", 562 | "overview": "HTTP request splitting: If Node.js can be convinced to use unsanitized user-provided Unicode data for the `path` option of an HTTP request, then data can be provided which will trigger a second, unexpected, and user-defined HTTP request to made to the same server.", 563 | "id": "55" 564 | }, 565 | "56": { 566 | "cve": [ 567 | "CVE-2018-12120" 568 | ], 569 | "vulnerable": "6", 570 | "patched": "^6.15.0 || ^8.14.0", 571 | "publish_date": "2018-11-27", 572 | "author": "Ben Noordhuis", 573 | "reported_by": "Ben Noordhuis", 574 | "ref": "https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/", 575 | "type": "CWE-419: Unprotected Primary Channel", 576 | "overview": "Debugger port 5858 listens on any interface by default: When the debugger is enabled with `node --debug` or `node debug`, it listens to port 5858 on all interfaces by default. This may allow remote computers to attach to the debug port and evaluate arbitrary JavaScript. The default interface is now localhost. It has always been possible to start the debugger on a specific interface, such as `node --debug=localhost`. The debugger was removed in Node.js 8 and replaced with the inspector, so no versions from 8 and later are vulnerable.", 577 | "id": "56" 578 | }, 579 | "57": { 580 | "cve": [ 581 | "CVE-2018-12121" 582 | ], 583 | "vulnerable": "6 || 8 || 10 || 11", 584 | "patched": "^6.15.0 || ^8.14.0 || ^10.14.0 || ^11.3.0", 585 | "publish_date": "2018-11-27", 586 | "author": "Matteo Collina", 587 | "reported_by": "Trevor Norris", 588 | "ref": "https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/", 589 | "type": "CWE-400: Uncontrolled Resource Consumption / Denial of Service", 590 | "overview": "Denial of Service with large HTTP headers: By using a combination of many requests with maximum sized headers (almost 80 KB per connection), and carefully timed completion of the headers, it is possible to cause the HTTP server to abort from heap allocation failure. Attack potential is mitigated by the use of a load balancer or other proxy layer.", 591 | "id": "57" 592 | }, 593 | "58": { 594 | "cve": [ 595 | "CVE-2018-12122" 596 | ], 597 | "vulnerable": "6 || 8 || 10 || 11", 598 | "patched": "^6.15.0 || ^8.14.0 || ^10.14.0 || ^11.3.0", 599 | "publish_date": "2018-11-27", 600 | "author": "Matteo Collina", 601 | "reported_by": "Jan Maybach", 602 | "ref": "https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/", 603 | "type": "CWE-400: Uncontrolled Resource Consumption / Denial of Service", 604 | "overview": "Slowloris HTTP Denial of Service: An attacker can cause a Denial of Service (DoS) by sending headers very slowly keeping HTTP or HTTPS connections and associated resources alive for a long period of time.", 605 | "id": "58" 606 | }, 607 | "59": { 608 | "cve": [ 609 | "CVE-2018-12123" 610 | ], 611 | "vulnerable": "6 || 8 || 10 || 11", 612 | "patched": "^6.15.0 || ^8.14.0 || ^10.14.0 || ^11.3.0", 613 | "publish_date": "2018-11-27", 614 | "author": "Matteo Collina", 615 | "reported_by": "Martin Bajanik", 616 | "ref": "https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/", 617 | "type": "CWE-115: Misinterpretation of Input", 618 | "overview": "Hostname spoofing in URL parser for javascript protocol: If a Node.js application is using url.parse() to determine the URL hostname, that hostname can be spoofed by using a mixed case \"javascript:\" (e.g. \"javAscript:\") protocol (other protocols are not affected). If security decisions are made about the URL based on the hostname, they may be incorrect.", 619 | "id": "59" 620 | }, 621 | "60": { 622 | "cve": [ 623 | "CVE-2019-5737" 624 | ], 625 | "vulnerable": "6 || 8 || 10 || 11", 626 | "patched": "^6.17.0 || ^8.15.1 || ^10.15.2 || ^11.10.1", 627 | "publish_date": "2019-02-28", 628 | "author": "Matteo Collina", 629 | "reported_by": "Marco Pracucci", 630 | "ref": "https://nodejs.org/en/blog/vulnerability/february-2019-security-releases/", 631 | "type": "CWE-400: Uncontrolled Resource Consumption / Denial of Service", 632 | "overview": "An attacker can cause a Denial of Service (DoS) by establishing an HTTP or HTTPS connection in keep-alive mode and by sending headers very slowly thereby keeping the connection and associated resources alive for a long period of time. Attack potential is mitigated by the use of a load balancer or other proxy layer. This vulnerability is an extension of CVE-2018-12121, addressed in November and impacts all active release lines including 6, 8, 10 and 11.", 633 | "id": "60" 634 | }, 635 | "61": { 636 | "cve": [ 637 | "CVE-2019-5739" 638 | ], 639 | "vulnerable": "6", 640 | "patched": "^6.17.0", 641 | "publish_date": "2019-02-28", 642 | "author": "Matteo Collina", 643 | "reported_by": "Timur Shemsedinov", 644 | "ref": "https://nodejs.org/en/blog/vulnerability/february-2019-security-releases/", 645 | "type": "CWE-400: Uncontrolled Resource Consumption / Denial of Service", 646 | "overview": "Keep-alive HTTP and HTTPS connections can remain open and inactive for up to 2 minutes in Node.js 6.16.0 and earlier. Node.js 8.0.0 introduced a dedicated server.keepAliveTimeout which defaults to 5 seconds. The behavior in Node.js 6.16.0 and earlier is a potential Denial of Service (DoS) attack vector. Node.js 6.17.0 introduces server.keepAliveTimeout and the 5-second default.", 647 | "id": "61" 648 | } 649 | } --------------------------------------------------------------------------------